How optimize query - mysql

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".

Related

GROUP_CONCAT in subquery returns one row only

I have some troubles to finish my Mysql Query to return me what I need to have. I am new to such long queries in MYSQL.
SELECT
lang_rel_a_id,
lang_rel_b_id,
lang_rel_id,
tla.text_lang_t AS atext,
lald.lang_data_lang_id AS laid,
lald.lang_data_position AS lapp,
lald.lang_data_font_weight AS lafw,
lald.lang_data_font_size AS lafs,
lald.lang_data_font_color AS lafc,
lald.lang_data_bg_color AS labg,
lasdf.funca AS lafunc,
lang_ship,
lbld.lang_data_lang_id AS lbid,
lbld.lang_data_position AS lbpp,
lbld.lang_data_font_weight AS lbfw,
lbld.lang_data_font_size AS lbfs,
lbld.lang_data_font_color AS lbfc,
lbld.lang_data_bg_color AS lbbg,
tlb.text_lang_t AS btext,
lbsdf.funcb AS lbfunc
FROM lang_relation
LEFT JOIN
(SELECT *, GROUP_CONCAT(text_func_t SEPARATOR ', ') AS funca
FROM synt_data_func
LEFT JOIN text_func ON text_func_id = synt_df_func
GROUP BY synt_df_lang_data
)
lasdf ON lang_rel_a_id = lasdf.synt_df_lang_data
LEFT JOIN lang_data lald ON lald.lang_data_id = lang_rel_a_id
LEFT JOIN text_lang tla ON lald.lang_data_lang_id = tla.text_lang_id
LEFT JOIN
(SELECT *, GROUP_CONCAT(text_func_t SEPARATOR ', ') AS funcb
FROM synt_data_func
LEFT JOIN text_func ON text_func_id = synt_df_func
GROUP BY synt_df_lang_data
)
lbsdf ON lang_rel_b_id = lbsdf.synt_df_lang_data
LEFT JOIN lang_data lbld ON lbld.lang_data_id = lang_rel_b_id
LEFT JOIN text_lang tlb ON lbld.lang_data_lang_id = tlb.text_lang_id
WHERE lang_rel_a_id < lang_rel_b_id
GROUP BY lang_rel_id
I have a relation of two languages in my lang_relation table. I need to query for each of them 2 subtables but the one of them is a relation table that contains the relation between the lang_data_id (= lang_rel_a_id OR lang_rel_b_id, = synt_df_lang_data) and text of the different language functions where multiple values are possible.
I do not understand why the group_concat in this subquery returns only one row. If I do only this query, I get all the results. But when I put it into this larger query, everything's fine but this.. is ..not.
My language_relation table
CREATE TABLE `lang_relation`
(
`lang_rel_id` int(11) NOT NULL,
`lang_rel_a_id` int(11) NOT NULL,
`lang_rel_b_id` int(11) NOT NULL,
`lang_ship` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The joined lang_data
CREATE TABLE `lang_data` (
`lang_data_id` int(11) NOT NULL,
`lang_data_pic_key` int(11) NOT NULL,
`lang_data_position` tinyint(1) NOT NULL,
`lang_data_lang_id` int(11) NOT NULL,
`lang_data_font_weight` tinyint(2) NOT NULL,
`lang_data_font_size` tinyint(2) NOT NULL,
`lang_data_font_color` tinyint(2) NOT NULL,
`lang_data_bg_color` tinyint(2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And the synt_data_func. The text_table is a simple 2-column-table with id + text.
CREATE TABLE `synt_data_func` (
`synt_df_id` int(11) NOT NULL,
`synt_df_lang_data` int(11) NOT NULL,
`synt_df_func` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
I tried different approachs. This seems to be the one that is the nearest of what I need. I don't know how many times I changed the GROUP BY clauses, I even tried to do the CONCAT_GROUP in the parent SELECT..
I even wonder if this is possible because the subqueries are going for 2 different IDs .. is this the problem?
Thanks for any hint in advance.
I finally got it. Maybe it will help somebody with a similar question. I changed my approach for this query.
SELECT
lrel.lang_rel_pic_key,
lrel.lang_rel_id,
langdata_a.lascore,
lasdf.func_a,
langdata_a.latext,
lasf.score_astyle,
SUM(lasf.score_astyle) + (langdata_a.lascore) AS atotal,
lang_ship,
langdata_b.lbtext,
langdata_b.lbscore,
lbsdf.func_b,
lbsf.bformat,
lbsf.score_bstyle,
SUM(lbsf.score_bstyle) + (langdata_b.lbscore) AS btotal
FROM lang_relation lrel
INNER JOIN
(
SELECT DISTINCT
lald.lang_data_id,
lafw.field_value AS lafweight,
lafs.field_value AS lafsize,
lafc.field_value AS laffc,
lafbg.field_value AS lafbg,
lapos.field_value AS laposa,
tla.text_lang_t AS latext,
SUM(lafw.field_value) + (lafs.field_value) + (lafc.field_value) + (lafbg.field_value) + (lapos.field_value) AS lascore
FROM lang_data lald
LEFT JOIN text_lang tla ON lald.lang_data_lang_id = tla.text_lang_id
LEFT JOIN `fields` lafw ON lald.lang_data_font_weight = lafw.field_id
LEFT JOIN `fields` lafs ON lald.lang_data_font_size = lafs.field_id
LEFT JOIN `fields` lafc ON lald.lang_data_font_color = lafc.field_id
LEFT JOIN `fields` lafbg ON lald.lang_data_bg_color = lafbg.field_id
LEFT JOIN `fields` lapos ON lald.lang_data_position = lapos.field_id
GROUP BY lald.lang_data_id
)
langdata_a ON langdata_a.lang_data_id = lrel.lang_rel_a_id
LEFT JOIN
(SELECT sdf.synt_df_lang_data, GROUP_CONCAT(latf.text_func_t) AS func_a
FROM synt_data_func sdf
INNER JOIN text_func latf ON latf.text_func_id = sdf.synt_df_func
GROUP BY sdf.synt_df_lang_data
)
lasdf ON lasdf.synt_df_lang_data = lrel.lang_rel_a_id
LEFT JOIN
(
SELECT sfb.synt_format_lang_data,
sfb.synt_format_fields_id,
GROUP_CONCAT(sfbf.field_text SEPARATOR ', ') AS aformat,
SUM(sfbf.field_value) AS score_astyle
FROM synt_format sfb
INNER JOIN `fields` sfbf ON sfbf.field_id = sfb.synt_format_fields_id
GROUP BY sfb.synt_format_lang_data
)
lasf ON lasf.synt_format_lang_data = lrel.lang_rel_a_id
INNER JOIN
(
SELECT DISTINCT
lbld.lang_data_id,
lbfw.field_value AS lbfweight,
lbfs.field_value AS lbfsize,
lbfc.field_value AS lbffc,
lbfbg.field_value AS lbfbg,
lbpos.field_value AS lbposa,
tlb.text_lang_t AS lbtext,
SUM(lbfw.field_value) + (lbfs.field_value) + (lbfc.field_value) + (lbfbg.field_value) + (lbpos.field_value) AS lbscore
FROM lang_data lbld
LEFT JOIN text_lang tlb ON lbld.lang_data_lang_id = tlb.text_lang_id
LEFT JOIN `fields` lbfw ON lbld.lang_data_font_weight = lbfw.field_id
LEFT JOIN `fields` lbfs ON lbld.lang_data_font_size = lbfs.field_id
LEFT JOIN `fields` lbfc ON lbld.lang_data_font_color = lbfc.field_id
LEFT JOIN `fields` lbfbg ON lbld.lang_data_bg_color = lbfbg.field_id
LEFT JOIN `fields` lbpos ON lbld.lang_data_position = lbpos.field_id
GROUP BY lbld.lang_data_id
)
langdata_b ON langdata_b.lang_data_id = lrel.lang_rel_b_id
LEFT JOIN
(SELECT sdfb.synt_df_lang_data, GROUP_CONCAT(lbtf.text_func_t) AS func_b
FROM synt_data_func sdfb
INNER JOIN text_func lbtf ON lbtf.text_func_id = sdfb.synt_df_func
GROUP BY sdfb.synt_df_lang_data
)
lbsdf ON lbsdf.synt_df_lang_data = lrel.lang_rel_b_id
LEFT JOIN
(
SELECT sfb.synt_format_lang_data,
sfb.synt_format_fields_id,
GROUP_CONCAT(sfbf.field_text SEPARATOR ', ') AS bformat,
SUM(sfbf.field_value) AS score_bstyle
FROM synt_format sfb
INNER JOIN `fields` sfbf ON sfbf.field_id = sfb.synt_format_fields_id
GROUP BY sfb.synt_format_lang_data
)
lbsf ON lbsf.synt_format_lang_data = lrel.lang_rel_b_id
GROUP BY lrel.lang_rel_id
Maybe a bit long, but output is exactly what was needed :-)

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

Left join with multiple status

Lets say i have two stores. Store A(22) and Store B(21). This is the query to fetch the things that matched with the Store ID:
SELECT c . * , s.s_name, s.logo, s.s_slug, cm.c_code, cm.c_shorturl, cm.c_shorturl_id
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
LEFT JOIN ci_cptbl_mapper cm ON cm.c_id = c._id
WHERE c.coupon_id <> ''
AND c.store_id in ('22', '21')
AND s.s_status = '1'
AND c.c_status = '1'
AND DATE( c.c_end_date ) >= '2014-10-04'
ORDER BY c.c_id DESC
ci_cptbl Has the collection of product including the store_id. And ci_stores holds the store name, etc.. including s_status(0, 1).
CREATE TABLE `ci_stores` (
`store_id` int(10) NOT NULL AUTO_INCREMENT,
`cat_id` int(10) NOT NULL,
`s_name` varchar(255) NOT NULL,
`s_slug` varchar(255) NOT NULL,
`logo` varchar(255) NOT NULL,
`display_name` varchar(255) NOT NULL,
`s_description` text NOT NULL,
`network_id` int(10) NOT NULL,
`s_status` tinyint(1) NOT NULL DEFAULT '0',
`merged_stores` text NOT NULL,
`stat` bigint(20) NOT NULL,
PRIMARY KEY (`store_id`),
KEY `network_id` (`network_id`),
KEY `cat_id` (`cat_id`),
FULLTEXT KEY `display_name` (`display_name`)
) ENGINE=MyISAM AUTO_INCREMENT=127 DEFAULT CHARSET=utf8
Now condition is i have only first store id( 22 ) enabled and rest is disabled (21, .....) in my stores table and my AND s.s_status = '1' statement works only if all the stores are enabled but i want all stores including disabled.
BUT FIRST STORE ID MUST BE ENABLED
You could try changing you condition to test on store_id and enabled
(c.c_status = '1' or c.store_id <> '22')
so:
SELECT c . * , s.s_name, s.logo, s.s_slug, cm.c_code, cm.c_shorturl, cm.c_shorturl_id
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
LEFT JOIN ci_cptbl_mapper cm ON cm.c_id = c._id
WHERE c.coupon_id <> ''
AND c.store_id in ('22', '21')
AND s.s_status = '1'
AND (c.c_status = '1' or c.store_id <> '22')
AND DATE( c.c_end_date ) >= '2014-10-04'
ORDER BY c.c_id DESC
You need to move your "AND s.s_status = 1" clause to the LEFT JOIN part of your query. When you move it to the WHERE clause, it forces to an implied INNER JOIN and thus leaving out of you result set.
LEFT JOIN ci_stores s
ON c.store_id = s.store_id
AND s.s_status = '1'
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
The purpose of that LEFT JOIN is to allow unmatched records in the "joined to" table to be listed in the results
e.g. if there is a store 657 in ci_cptbl but no such store in ci_stores 657 would still be listed
However, using the WHERE clause:
if we then ALSO insist EVERY row has s.s_status = '1'
then store 657 would NOT be listed (how can it? it does not exist in ci_stores so ci_stores.s_status has to be NULL and can never ever be equal to 1
So; when using any outer join such as a LEFT OUTER JOIN you have to be very careful how you reference those tables in the WHERE clause. In many cases, such as here, it is better to move the additional condition(s) to the JOIN like this:
FROM ci_cptbl c
LEFT JOIN ci_stores s ON s.store_id = c.store_id
AND s.s_status = '1'

Optimising MySQL Query, Select within Select, Multiple of same

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;