Optimising MySQL Query, Select within Select, Multiple of same - mysql

I need help optimising this MySQL statement that I whipped up. It does exactly what I want, however I have a great feeling that it'll be quite slow, since I do multiple selects within the statement, and I also query achievements_new multiple times. This is the first time I do some major statement like this, I'm used to the simple SELECT FROM WHERE style crap.
I might do some explaining, this is for a leaderboard style thing for my website.
--First variable output is a rank that is calculated according to the formula shown, (Log + Log + # of achievements).
--Wepvalue is the sum of the values of the weapons which that id has. playerweapons contains all the weapons, and weaponprices convert the type to the price, and then the SUM calculates the value.
--Achcount is simply the amount of achievements that's unlocked. Maybe this can be optimised somehow with the rank output?
--id in achievements_new and playerweapons are Foreign Keys to the id in playerdata
SELECT
(
IFNULL(LOG(1.5, cashearned),0) +
IFNULL(LOG(1.3, roundswon), 0) +
(
SELECT COUNT(*)
FROM achievements_new
WHERE `value` = -1 AND achievements_new.id = playerdata.id
)
) as rank,
nationality,
nick,
steamid64,
cash,
playtime,
damage,
destroyed,
(
SELECT SUM(price)
FROM weaponprices
WHERE weapon IN
(
SELECT class
FROM playerweapons
WHERE playerweapons.id = playerdata.id
)
) as wepvalue,
(
SELECT COUNT(*)
FROM achievements_new
WHERE `value` = -1 AND achievements_new.id = playerdata.id
) as achcount,
lastplayed
FROM playerdata
ORDER BY rank DESC
Table structures:
playerdata:
CREATE TABLE IF NOT EXISTS `playerdata` (
`id` int(11) unsigned NOT NULL,
`steamid64` char(17) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`nick` varchar(32) NOT NULL DEFAULT '',
`cash` int(32) unsigned NOT NULL DEFAULT '0',
`playtime` int(32) unsigned NOT NULL DEFAULT '0',
`nationality` char(2) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`damage` int(32) unsigned NOT NULL DEFAULT '0',
`destroyed` int(32) unsigned NOT NULL DEFAULT '0',
`cashearned` int(10) unsigned NOT NULL,
`roundswon` smallint(5) unsigned NOT NULL,
`lastplayed` datetime NOT NULL,
) ENGINE=InnoDB
achievements_new:
CREATE TABLE IF NOT EXISTS `achievements_new` (
`id` int(10) unsigned NOT NULL,
`achkey` enum(<snip - lots of values here>) NOT NULL,
`value` mediumint(8) NOT NULL DEFAULT '0'
) ENGINE=InnoDB
playerweapons:
CREATE TABLE IF NOT EXISTS `playerweapons` (
`id` int(10) unsigned NOT NULL,
`class` varchar(30) CHARACTER SET ascii NOT NULL
) ENGINE=InnoDB
weaponprices:
CREATE TABLE IF NOT EXISTS `weaponprices` (
`weapon` varchar(30) NOT NULL,
`price` int(10) unsigned NOT NULL
) ENGINE=InnoDB
Thanks in advance!

Try something like the query below.
I used LEFT JOIN instead of joins because there may be players without achievements or weapons. If you do not need these players you can use JOIN
SELECT
IFNULL(LOG(1.5, p.cashearned),0) +
IFNULL(LOG(1.3, p.roundswon), 0) +
SUM(CASE WHEN ac.id IS NOT NULL THEN 1 ELSE 0 END)/COUNT(pw.id) as rank
p.nationality,
p.nick,
p.steamid64,
p.cash,
p.playtime,
p.damage,
p.destroyed,
--SUM(CASE WHEN pw.id IS NOT NULL THEN pw.price ELSE 0 END) as wepvalue,
--wpn.price as wepvalue,
SUM(CASE WHEN pw.id IS NOT NULL THEN wp.price ELSE 0 END)/COUNT(ac.id) as wepvalue,
SUM(CASE WHEN ac.id IS NOT NULL THEN 1 ELSE 0 END)/COUNT(pw.id) as achcount,
lastplayed
FROM playerdata as p
JOIN playerweapons as pw ON pw.id = p.id
JOIN weaponprices as wp ON pw.class = wp.weapon
LEFT JOIN achievements_new as ac ON ac.id = p.id AND ac.value = -1
--LEFT JOIN playerweapons as pw ON pw.id = p.id
--LEFT JOIN weaponprices as wp ON pw.class = wp.weapon
--LEFT JOIN ( SELECT
--pw.id as player,
--SUM(wp.price) as price
--FROM weaponprices as wp
--JOIN playerweapons as pw ON pw.class = wp.weapon
--GROUP BY pw.id
--) as wpn ON wpn.player = p.id
GROUP BY
p.nationality,
p.nick,
p.steamid64,
p.cash,
p.playtime,
p.damage,
p.destroyed,
p.lastplayed

Your query is fairly reasonable, although I would rewrite the subqueries to use explicit joins rather than in and factor out the achievements subquery:
SELECT (IFNULL(LOG(1.5, cashearned),0) + IFNULL(LOG(1.3, roundswon), 0) +
coalesce(an.cnt, 0)
) as rank,
nationality, nick, steamid64, cash, playtime, damage, destroyed,
(SELECT SUM(wp.price)
FROM weaponprices wp JOIN
playerweapons pw
on pw.class = wp.weapons
WHERE pw.id = pd.id
) as wepvalue,
coalesce(an.cnt, 0) as achcount,
lastplayed
FROM playerdata pd left outer join
(SELECT id, count(*) as cnt
FROM achievements_new an
WHERE an.`value` = -1
GROUP BY an.id
) an
on an.id = pd.id
ORDER BY rank DESC;
For this query, create the following indexes:
playerweapons(id, weapon);
weaponprices(class, price);
achievements_new(value, id);
This does the following things:
It eliminates two redundant subqueries on achievements_new.
It should optimize the prices subquery to only use indexes.
It replaces the in with an explicit join, which is sometimes optimized better.
It does not require an outer group by.

I would try to remove all correlated subqueries
SELECT
( COALESCE(LOG(1.5, pd.cashearned), 0)
+ COALESCE(LOG(1.3, pd.roundswon), 0)
+ COALESCE(an.cnt, 0)) AS rank
, pd.nationality
, pd.nick
, pd.steamid64
, pd.cash
, pd.playtime
, pd.damage
, pd.destroyed
, COALESCE(pw.wepvalue, 0) AS wepvalue
, COALESCE(an.cnt, 0) AS achcount
, pd.lastplayed
FROM playerdata pd
LEFT JOIN (
SELECT
id
, COUNT(*) AS cnt
FROM achievements_new
WHERE value = -1
GROUP BY
id
) an
ON pd.id = an.id
LEFT JOIN (
SELECT
playerweapons.id
, SUM(price) AS wepvalue
FROM weaponprices
INNER JOIN playerweapons
ON weaponprices.weapon = playerweapons.class
GROUP BY
playerweapons.id
) pw
ON pd.id = pw.id
ORDER BY
rank DESC;

Related

How optimize query

How can i optimize this query ?
CREATE TABLE rechercherefppv3bis_tmp
( `id` varchar(25) KEY, `dateMaj` datetime DEFAULT NULL, `type` varchar(9) DEFAULT NULL, `nom` varchar(60) DEFAULT NULL, `prenom` varchar(150) DEFAULT NULL, `adresseLigne2` varchar(38) DEFAULT NULL, `adresseLigne3` varchar(38) DEFAULT NULL, `adresseLigne4` varchar(38) DEFAULT NULL, `adresseLigne5` varchar(38) DEFAULT NULL, `adresseLigne6` varchar(38) DEFAULT NULL, `adresseLigne7` varchar(38) DEFAULT NULL, `emailPrincipal` varchar(255) DEFAULT NULL, `telephonePrincipal` varchar(50) DEFAULT NULL, `lignes4` text, `lignes6` text, `numeros` text, `adresses` text, `numProcuration` text, `numReexpedition` text, `cp` varchar(255), `isDGP` boolean NULL, `civilite` varchar(255) NULL,`codeCivilite` int(10) NULL , `coclico` varchar(8) NULL , `certificationStatut` varchar(255) NULL , `siret` varchar(14) NULL , `nomSociete` varchar(255) NULL )
AS (
SELECT
u.id,
str_to_date(GREATEST(u.dateMaj, ifnull(a.dateMaj, '1970-01-01 08:00:00'), ifnull(t.dateMaj, '1970-01-01 08:00:00'), ifnull(m.dateMaj, '1970-01-01 08:00:00')),'%Y-%m-%d %H:%i:%s') as dateMaj,
u.type as type,
u.nom as nom,
u.prenom as prenom,
a.ligne1 as adresseLigne1,
a.ligne2 as adresseLigne2,
a.ligne3 as adresseLigne3,
a.ligne4 as adresseLigne4,
a.ligne5 as adresseLigne5,
a.ligne6 as adresseLigne6,
a.ligne7 as adresseLigne7,
m.adresse as emailPrincipal,
t.numero AS telephonePrincipal,
GROUP_CONCAT(DISTINCT ads.ligne4 SEPARATOR ';') as lignes4,
GROUP_CONCAT(DISTINCT ads.ligne6 SEPARATOR ';') as lignes6,
GROUP_CONCAT(DISTINCT ts.numero SEPARATOR ';') as numeros,
GROUP_CONCAT(DISTINCT ms.adresse SEPARATOR ';') as adresses,
GROUP_CONCAT(DISTINCT pp.idOrigine SEPARATOR ';') as numProcuration,
GROUP_CONCAT(DISTINCT rc.numero SEPARATOR ';') as numReexpedition,
SUBSTRING_INDEX(a.ligne6,' ',1) AS cp,
case when ctp1.idContratDGP is null then false else true end as isDGP,
u.niveauAdhesion,
u.civilite,
u.codeCivilite,
u.certificationStatus as certificationStatut,
u.coclico,
cr.nom as nomSociete,
cr.siret as siret
FROM ccu_user_v3bis u
LEFT OUTER JOIN ccu_adresse_v3bis a ON a.idUser = u.id AND a.principale = 1
LEFT OUTER JOIN ccu_email_v3bis m ON m.idUser = u.id AND m.principal = 1
LEFT OUTER JOIN ccu_telephone_v3bis t ON t.idUser = u.id AND t.principal = 1
LEFT OUTER JOIN ccu_adresse_v3bis ads ON ads.idUser = u.id
LEFT OUTER JOIN ccu_email_v3bis ms ON ms.idUser = u.id
LEFT OUTER JOIN ccu_telephone_v3bis ts ON ts.idUser = u.id
LEFT OUTER JOIN procuration_part pp ON pp.idClient = u.id
LEFT OUTER JOIN reexp_contrat rc ON rc.idCCU = u.id
LEFT OUTER JOIN client_refpm cr ON cr.id = u.coclico
LEFT OUTER JOIN
(SELECT ctp.idCCUSouscripteur , max(ctp.id) as idContratDGP
FROM contrats_part ctp
WHERE ctp.idCCUSouscripteur is not null and ctp.source = 'Digiposte' and ctp.statutContrat = '2' GROUP BY ctp.idCCUSouscripteur)
as ctp1 ON ctp1.idCCUSouscripteur = u.id
group by u.id
);
this query takes too long, I want to simplify it
Don't use "LEFT" unless the right-hand column is missing some rows and you want NULLs. In particular, contrats_part looks like it can never be null.
These indexes may help:
ctp: INDEX(source, statutContrat, idCCUSouscripteur, id)
a: INDEX(idUser)
t: INDEX(idUser, dateMaj, numero, principal)
m: INDEX(idUser, dateMaj, adresse, principal)
ads: INDEX(idUser, ligne4, ligne6)
ts: INDEX(idUser, numero)
ms: INDEX(idUser, adresse)
pp: INDEX(idClient, idOrigine)
rc: INDEX(idCCU, numero)
It may be better to make this change; I do not know for sure:
Instead of, for example,
GROUP_CONCAT(DISTINCT ts.numero SEPARATOR ';') as numerous,
...
LEFT OUTER JOIN ccu_telephone_v3bis ts ON ts.idUser = u.id
get rid of the JOIN and change the select part to
( SELECT GROUP_CONCAT(DISTINCT ts.numero SEPARATOR ';') as numerous
FROM ccu_telephone_v3bis ts WHERE ts.idUser = u.id
) as numerous,
Doing that to all the aggregates should let you get rid of
GROUP BY u.id
This
case when ctp1.idContratDGP is null then false else true end
can be simplified to
ctp1.idContratDGP is NOT null
Looking at that list of JOINs, I suspect the schema is "over-normalized".

Optimizing rand query with join

I have a rand query which runs very slow like almost every rand query. I researched all stackoverflow but cannot find any good solution for my query
SELECT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND u.username NOT IN (
SELECT blocked_username
FROM hided_mobile_users_from_shuffle
WHERE username = 'enesdoo'
)
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
GROUP
BY l.username
ORDER
BY RAND( )
LIMIT 27
If i remove the order by rand line, this runs very very quick like 100 times faster.
How can i speed up this query?
mobile_login_list has > 50k rows
users has > 1m rows
Edit:
Explain:
My table:
CREATE TABLE IF NOT EXISTS `mobile_login_list` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(30) COLLATE utf8_bin NOT NULL,
`key` varchar(32) COLLATE utf8_bin NOT NULL,
`time` int(11) NOT NULL,
`ip` int(11) NOT NULL,
`version` smallint(4) NOT NULL,
`messaged` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `kontrol` (`username`,`key`),
KEY `username` (`username`),
KEY `time` (`time`),
KEY `username_2` (`username`,`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=3351637 ;
In the lingo of random retrieval, this is called a deal operation (deal 27 different cards from a shuffled deck of 4k or so. The other random operation is called roll: it allows duplicates.)
You're using SELECT mess-of-columns FROM mess-of-joins WHERE mess-of-criteria ORDER BY RAND() LIMIT small-number to do shuffle and deal operation. That is a notorious performance antipattern. It causes some extra work for the server because it must order a fairly large result set then discard almost all of it (with the LIMIT).
A way to save some of the trouble is to defer the joins to the details. Shuffle only the ids. Then take the small number of results and fetch the details you need. Something like this.
SELECT u.id /* just the id values */
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND u.username NOT IN (
SELECT blocked_username
FROM hided_mobile_users_from_shuffle
WHERE username = 'enesdoo'
)
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
ORDER
BY RAND( )
LIMIT 27
You can debug, run EXPLAIN and optimize this subquery by changing indexes and maybe tightening up your selection criteria. It's the one doing all the hard work of shuffling and dealing.
Then join that resultset to your detail tables to choose the data you need. This outer query only needs to process your 27 rows. Be sure to shuffle again.
SELECT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN (
/* the subquery from above */
) selected ON u.id = selected.id
ORDER BY RAND()
Putting it all together, you get this big repetitive mess of a query. But it should be a little faster.
SELECT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN (
SELECT u.id
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND u.username NOT IN (
SELECT blocked_username
FROM hided_mobile_users_from_shuffle
WHERE username = 'enesdoo'
)
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
ORDER
BY RAND( )
LIMIT 27
) selected ON u.id = selected.id
ORDER BY RAND()
A more performant way to deal records is this, if you do the dealing a lot.
Add a FLOAT column to the table you're dealing from, let's call it deal. Put an index on it.
Every few hours, or maybe overnight or even once a week, shuffle the table by running this query UPDATE users SET deal = RAND(); It will take a while; it needs to change the deal value in every row.
When you need to deal, do ...WHERE deal >= RAND() * 0.9 ... ORDER BY deal LIMIT n. The multiplication by 0.9 helps ensure you don't hit the end of the table by choosing a random number too close to 1.
This is equivalent, in cardshark terms, to shuffling the deck every few hours and then just cutting it for every deal. It's the way Wikipedia implements their "show a random article" feature.
Can we see the EXPLAIN for this instead...?
SELECT DISTINCT u.id
, u.is_instagram_connected
, u.tokens
, u.username
, u.name
, u.photo
, u.bio
, u.voice
, u.mobile_update
, 1584450999 - l.time idleTime
FROM mobile_login_list l
JOIN users u
ON l.username = u.username
JOIN mobile_token_list t
ON t.username = l.username
LEFT
JOIN hided_mobile_users_from_shuffle x
ON x.blocked_username = u.username
AND x.username = 'enesdoo'
WHERE l.time > 1584393399
AND l.username NOT IN ('enesdoo')
AND x.blocked_username IS NULL
AND u.ban_status = 0
AND u.perma_ban = 0
AND u.mobile_online_status = 1
AND u.lock_status = 0
ORDER
BY RAND( )
LIMIT 27
Given my limited knowledge of query optimisation, I would simply define the table as follows, but maybe someone else can suggest further improvements:
CREATE TABLE IF NOT EXISTS mobile_login_list
(id SERIAL PRIMARY KEY
,username varchar(30) COLLATE utf8_bin NOT NULL
,`key` varchar(32) COLLATE utf8_bin NOT NULL
,time int NOT NULL
,ip int NOT NULL
,version smallint NOT NULL
,messaged int NOT NULL DEFAULT 0
,KEY username_2 (username,time) -- or (time,username)
);
Note that key is a reserved word (and time is a 'keyword') rendering it a poor choice for a table/column identifier

How to set a col val based on 2 other cols in a table in mysql?

In mySql I'm trying to set the value of a column (total) based on the values of 2 other columns of the same table.
http://sqlfiddle.com/#!9/e9c27a/1
CREATE TABLE IF NOT EXISTS `order_total` (
`order_total_id` int(10) NOT NULL AUTO_INCREMENT,
`order_id` int(11) NOT NULL,
`code` varchar(32) NOT NULL,
`title` varchar(255) NOT NULL,
`value` decimal(15,4) NOT NULL DEFAULT '0.0000',
`sort_order` int(3) NOT NULL,
PRIMARY KEY (`order_total_id`),
KEY `order_id` (`order_id`)
) ENGINE=MyISAM AUTO_INCREMENT=244 DEFAULT CHARSET=utf8;
INSERT INTO `order_total` (`order_total_id`, `order_id`, `code`, `title`, `value`, `sort_order`) VALUES
(241, 80, 'sub_total', 'Sub-Total', '400.0000', 1),
(242, 80, 'shipping', 'Free Shipping', '10.0000', 3),
(243, 80, 'total', 'Total', '0', 9);
I tried such a code which works in local mySql, but I thought maybe there is a better way to do it.
UPDATE order_total ot
INNER JOIN (
SELECT order_id, value
FROM order_total
WHERE code = 'sub_total'
GROUP BY order_id
) o ON ot.order_id = o.order_id
INNER JOIN (
SELECT order_id, value
FROM order_total
WHERE code = 'shipping'
GROUP BY order_id
) o2 ON ot.order_id = o2.order_id
SET ot.value = o.value + o2.value
WHERE ot.code = 'total' AND ot.order_id = 80
How to do it more efficient?
You can calculate the total using one query as follows:
update order_total ot
INNER JOIN (
SELECT order_id, sum(value) value
FROM order_total
WHERE code = 'sub_total' or code= 'shipping'
GROUP BY order_id
) o ON ot.order_id = o.order_id
SET ot.value = o.value
WHERE ot.code = 'total' AND ot.order_id = 80;
Your current query is technically invalid, because the subqueries are selecting non aggregate columns which do not appear in the GROUP BY clause. But we can fix this, and make the query more succinct, by using conditional aggregation to find the subtotal and shipping values for each order:
UPDATE order_total ot
INNER JOIN
(
SELECT
order_id,
MAX(CASE WHEN code = 'sub_total' THEN value END) AS sub_value,
MAX(CASE WHEN code = 'shipping' THEN value END) AS shipping_value,
FROM order_total
GROUP BY order_id
) o
ON ot.order_id = o.order_id
SET ot.value = o.sub_value + o.shipping_value
WHERE
ot.code = 'total' AND
ot.order_id = 80;
For this particular problem, the accepted answer is the way to go. But if you wanted something other than the sum, then it would not work. My answer would let you do something like this:
SET ot.value = o.sub_value + 2*o.shipping_value
That is, if we wanted to give the shipping value a weight of 2, this answer allows for this to be easily done.

MySQL: Joining two tables and populating fields according to two columns in the joined table

I'm working on existing gallery system for which I want to make a few fields multilingual (title, source, description). A distinct table "translations" is used to store these translated texts. The "lang" column of this table stores the language code ("fr", "en", "de"). The "file_id" column stores to which file the translation relates.
I'm looking for the best way in MySQL to output the record for one file, with all its columns, including all translations as columns "title_fr, title_en, ..., description_de.
Tables structure:
CREATE TABLE `files` (
`id` int(10) unsigned NOT NULL auto_increment,
`src` varchar(128) NOT NULL default '',
`name` varchar(128) NOT NULL default '',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE `translations` (
`id` int(10) unsigned NOT NULL,
`file_id` int(10) unsigned NOT NULL,
`lang` char(2) NOT NULL default '',
`title` varchar(255) NOT NULL default '',
`source` varchar(32) default '',
`description` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
I tried two ideas, but none worked.
1) The first idea was using VIEWs. Failed because of missing access rights on the shared hosting:
GRANT CREATE VIEW ON *.* TO 'the-user#the-host' IDENTIFIED BY PASSWORD 'here-the-password';
CREATE VIEW French AS
SELECT f.id, title AS title_fr, source AS source_fr, description AS description_fr
FROM files AS f LEFT OUTER JOIN translations AS t ON t.file_id = f.id WHERE (f.id=11 AND t.lang='fr');
CREATE VIEW English AS
SELECT f.id, title AS title_en, source AS source_en, description AS description_en
FROM files AS f LEFT OUTER JOIN translations AS t ON t.file_id = f.id WHERE (f.id=11 AND t.lang='en');
CREATE VIEW German AS
SELECT f.id, title AS title_de, source AS source_de, description AS description_de
FROM files AS f LEFT OUTER JOIN translations AS t ON t.file_id = f.id WHERE (f.id=11 AND t.lang='de');
SELECT * FROM files AS f
LEFT OUTER JOIN French ON f.id=French.id
LEFT OUTER JOIN English ON f.id=English.id
LEFT OUTER JOIN German ON f.id=German.id
GROUP BY f.id;
2) The second idea is inspired from the example in this thread using a pivot table. In my case, I cannot use COUNT() and the following SQL query is not valid, but gives the idea. Sorry as there is some redundancy.
SELECT f.*,
(CASE
WHEN t.`lang`='fr'
THEN t.title
ELSE NULL
) AS title_fr,
(CASE
WHEN t.`lang`='en'
THEN t.title
ELSE NULL
) AS title_en,
(CASE
WHEN t.`lang`='de'
THEN t.title
ELSE NULL
) AS title_de,
(CASE
WHEN t.`lang`='fr'
THEN t.source
ELSE NULL
) AS source_fr,
(CASE
WHEN t.`lang`='en'
THEN t.source
ELSE NULL
) AS source_en,
(CASE
WHEN t.`lang`='de'
THEN t.source
ELSE NULL
) AS source_de
(CASE
WHEN t.`lang`='fr'
THEN t.description
ELSE NULL
) AS description_fr,
(CASE
WHEN t.`lang`='en'
THEN t.description
ELSE NULL
) AS description_en,
(CASE
WHEN t.`lang`='de'
THEN t.description
ELSE NULL
) AS description_de
FROM files AS f WHERE id=11
LEFT OUTER JOIN
translations AS t
ON f.id=t.file_id
GROUP BY id
I also read about CONCAT and CONCAT_WS but they are not what I'm looking for, as I want to make further PHP processing as simple as possible.
Although you can't use count, you can use max, so try this query, it should be what you are looking for:
SELECT
f.id, f.src, f.name,
MAX(CASE WHEN t.lang='fr' THEN t.title ELSE NULL END) AS title_fr,
MAX(CASE WHEN t.lang='en' THEN t.title ELSE NULL END) AS title_en,
MAX(CASE WHEN t.lang='de' THEN t.title ELSE NULL END) AS title_de,
MAX(CASE WHEN t.lang='fr' THEN t.source ELSE NULL END) AS source_fr,
MAX(CASE WHEN t.lang='en' THEN t.source ELSE NULL END) AS source_en,
MAX(CASE WHEN t.lang='de' THEN t.source ELSE NULL END) AS source_de,
MAX(CASE WHEN t.lang='fr' THEN t.description ELSE NULL END) AS description_fr,
MAX(CASE WHEN t.lang='en' THEN t.description ELSE NULL END) AS description_en,
MAX(CASE WHEN t.lang='de' THEN t.description ELSE NULL END) AS description_de
FROM files AS f
LEFT OUTER JOIN translations AS t ON f.id=t.file_id
WHERE f.id=11
GROUP BY f.id, f.src, f.name;

Display the result in table By month

I am trying to get the total of my intax and outTax. I have the right query, if i run it shows proper result. But i am not able to display it as i need.
Here is my code.
$sqlOut = "SELECT sales_invoice.invoice_id, MONTHNAME(sales_invoice.date_invoiced) AS month, sales_invoice_line_items.invoice_id, sales_invoice_line_items.tax, SUM(sales_invoice_line_items.tax_amount) AS totaltax, taxes.tax_id, taxes.rate, taxes.name AS Tname FROM sales_invoice INNER JOIN sales_invoice_line_items ON sales_invoice.invoice_id=sales_invoice_line_items.invoice_id INNER JOIN taxes ON sales_invoice_line_items.tax=taxes.tax_id WHERE sales_invoice_line_items.tax=".$tax." GROUP BY sales_invoice.date_invoiced";
$sqlIn = "SELECT purchase_invoice.invoice_id, MONTHNAME(purchase_invoice.date_invoiced) AS month, purchase_invoice_line_items.invoice_id, purchase_invoice_line_items.tax, SUM(purchase_invoice_line_items.tax_amount) AS totaltax, taxes.tax_id, taxes.rate, taxes.name AS Tname FROM purchase_invoice INNER JOIN purchase_invoice_line_items ON purchase_invoice.invoice_id=purchase_invoice_line_items.invoice_id INNER JOIN taxes ON purchase_invoice_line_items.tax=taxes.tax_id WHERE purchase_invoice_line_items.tax=".$tax." GROUP BY purchase_invoice.date_invoiced";
$ResOut = mysql_query($sqlOut) or die(mysql_error());
$ResIn = mysql_query($sqlIn) or die(mysql_error());
}
I want to display it like this
<td>Months</td><td>Out Tax</td><td>In Tax</td><td>Difference(OutTax-InTax)</td>
My output format has to be
<table><tr>
<td>Months</td><td>Out Tax</td><td>In Tax</td><td>Difference(OutTax-InTax)</td></tr>
<tr><td>Jan</td> <td>3456</td> <td>2311</td> <td>1145</td></tr>
<tr><td>March</td> <td>4123</td> <td>3125</td> <td>2978</td></tr>
</table>
And table structure is
purchase_invoice table
CREATE TABLE IF NOT EXISTS `purchase_invoice` (
`invoice_id` int(50) NOT NULL AUTO_INCREMENT,
`order_id` int(50) NOT NULL,
`date_invoiced` date NOT NULL,
`status` varchar(10) NOT NULL,
PRIMARY KEY (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
purchase_invoice_line_items
CREATE TABLE IF NOT EXISTS `purchase_invoice_line_items` (
`id` int(50) NOT NULL AUTO_INCREMENT,
`invoice_id` int(50) NOT NULL,
`tax` int(10) NOT NULL,
`discount` int(10) NOT NULL,
`freight` int(20) NOT NULL,
`sub_total` double NOT NULL,
`tax_amount` double NOT NULL,
`reason` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ;
Can somebody please help me in this
Try this :
SELECT A.month_invoiced AS 'Months', A.totaltax AS 'Out Tax',
B.totaltax AS 'In Tax', (A.totaltax - B.totaltax) AS 'Difference(OutTax-InTax)'
FROM (SELECT MONTHNAME(si.date_invoiced) AS month_invoiced,
EXTRACT(YEAR_MONTH FROM si.date_invoiced) AS YM_Invoced,
SUM(sil.tax_amount) AS totaltax
FROM sales_invoice si
INNER JOIN sales_invoice_line_items sil ON si.invoice_id=sil.invoice_id
INNER JOIN taxes t ON sil.tax=t.tax_id
WHERE sil.tax=".$tax."
GROUP BY YM_Invoced
) AS A
INNER JOIN (SELECT MONTHNAME(pi.date_invoiced) AS month_invoiced,
EXTRACT(YEAR_MONTH FROM pi.date_invoiced) AS YM_Invoced,
SUM(pil.tax_amount) AS totaltax
FROM purchase_invoice PI
INNER JOIN purchase_invoice_line_items pil ON pi.invoice_id=pil.invoice_id
INNER JOIN taxes t ON pil.tax=t.tax_id
WHERE pil.tax=".$tax."
GROUP BY YM_Invoced
) AS B ON A.YM_Invoced = B.YM_Invoced;
::EDIT::
SELECT A.month_invoiced,
MAX(CASE WHEN A.taxType = 'Out' THEN A.totaltax ELSE 0 END) AS 'Out Tax',
MAX(CASE WHEN A.taxType = 'In' THEN A.totaltax ELSE 0 END) AS 'In Tax',
(IFNULL(MAX(CASE WHEN A.taxType = 'Out' THEN A.totaltax ELSE 0 END), 0) -
IFNULL(MAX(CASE WHEN A.taxType = 'In' THEN A.totaltax ELSE 0 END), 0)
) AS 'Difference(OutTax-InTax)'
FROM (SELECT MONTHNAME(si.date_invoiced) AS month_invoiced,
EXTRACT(YEAR_MONTH FROM si.date_invoiced) AS YM_Invoced,
SUM(sil.tax_amount) AS totaltax,
'Out' AS taxType
FROM sales_invoice si
INNER JOIN sales_invoice_line_items sil ON si.invoice_id=sil.invoice_id
INNER JOIN taxes t ON sil.tax=t.tax_id
WHERE sil.tax=".$tax."
GROUP BY YM_Invoced
UNION
SELECT MONTHNAME(pi.date_invoiced) AS month_invoiced,
EXTRACT(YEAR_MONTH FROM pi.date_invoiced) AS YM_Invoced,
SUM(pil.tax_amount) AS totaltax,
'IN' AS taxType
FROM purchase_invoice PI
INNER JOIN purchase_invoice_line_items pil ON pi.invoice_id=pil.invoice_id
INNER JOIN taxes t ON pil.tax=t.tax_id
WHERE pil.tax=".$tax."
GROUP BY YM_Invoced
) AS A
GROUP BY A.YM_Invoced