Alternative to Where Exist clause MySQL - mysql

I have this select statement that is taking quite a while to run on a larger dataset
select lookup_svcscat_svcscatnew.SVCSCAT_NEW_DESC as svc_type,
enrolid, msclmid, dx1, dx2, dx3,
proc1,msk_cpt_mapping.surg_length_cd as SL_CD,
msk_cpt_mapping.days as day_window,o.svcdate_form, pay,
table_label
from ccaeo190_ky o
left join lookup_svcscat_svcscatnew on o.svcscat = lookup_svcscat_svcscatnew.svcscat
left join msk_cpt_mapping on o.proc1 = msk_cpt_mapping.cpt_code
where EXISTS
(
select 1
from eoc_op_mapping e
where e.msclmid = o.msclmid
and e.enrolid = o.enrolid
and proc1 =27447
)
ORDER BY svcdate_form, msclmid;
I want to return any row in my ccaeo190_ky table that meets the requirements of the where EXISTS clause on table eoc_op_mapping. Is there any way to achieve these results using joins or select statements?
I was thinking something like:
select lookup_svcscat_svcscatnew.SVCSCAT_NEW_DESC as svc_type,
o.enrolid, o.msclmid, dx1, dx2, dx3,
proc1,msk_cpt_mapping.surg_length_cd as SL_CD,
msk_cpt_mapping.days as day_window,o.svcdate_form, pay,
table_label
from ccaeo190_ky o
left join lookup_svcscat_svcscatnew on o.svcscat = lookup_svcscat_svcscatnew.svcscat
left join msk_cpt_mapping on o.proc1 = msk_cpt_mapping.cpt_code
inner join
(select msclmid, SUM(IF(proc1 = 27447,1,0)) AS cpt
from eoc_op_mapping
group by enrolid
HAVING cpt > 0) e
on e.enrolid = o.enrolid
group by o.enrolid;
But I don't know if this is in the right direction

Usually EXISTS performs better than a join.
If you want to try a join, this the equivalent to your WHERE EXISTS:
.......................................................
inner join (
select distinct msclmid, enrolid
from eoc_op_mapping
where proc1 = 27447
) e on e.msclmid = o.msclmid and e.enrolid = o.enrolid
.......................................................
You can remove distinct if there are no duplicate msclmid, enrolid combinations in eoc_op_mapping.

Related

How to access outer table data in join where

How can I access data from an outer table in a SELECT, and use it in an WHERE inside a JOIN estructure?
Below is the current query:
SELECT
cvl.id caracteristica_valor_id,
cvl.nome caracteristica_valor_nome,
cvl.valor caracteristica_valor_valor,
ctp.id caracteristica_tipo_id,
ctp.nome caracteristica_tipo_nome,
ctp.codigo caracteristica_tipo_codigo,
ctp.tipo caracteristica_tipo_tipo,
COUNT(DISTINCT var.id_perfil_produto) quantidade_itens
FROM
caracteristica_variacao cvr
INNER JOIN caracteristica_valor cvl ON cvl.id = cvr.id_caracteristica_valor
INNER JOIN caracteristica_tipo ctp ON ctp.id = cvl.id_caracteristica_tipo
INNER JOIN variacao var ON var.id = cvr.id_variacao
INNER JOIN(
SELECT DISTINCT
ppr.id perfil_produto_id
FROM
perfil_produto ppr
INNER JOIN produto pro ON pro.id = ppr.id_produto
INNER JOIN(
SELECT ppr2.id AS id_perfil_sub,a
COUNT(var.id) AS qtd_variacoes,
SUM(var.quantidade_estoque) AS quantidade_estoque,
COALESCE(SUM(var.quantidade_estoque_reservada),0) AS quantidade_estoque_reservada,
MIN(var.disponibilidade) AS disponibilidade,
MIN(var.frete_gratis) AS frete_gratis,
MIN(var.preco_venda) AS preco_venda,
MAX(var.preco_listagem) AS preco_listagem
FROM
variacao var
LEFT JOIN perfil_produto ppr2 ON ppr2.id = var.id_perfil_produto
LEFT JOIN caracteristica_variacao cvr_1 ON cvr_1.id_variacao = var.id
LEFT JOIN caracteristica_valor cvl_1 ON cvl_1.id = cvr_1.id_caracteristica_valor
LEFT JOIN caracteristica_tipo ctp_1 ON ctp_1.id = cvl_1.id_caracteristica_tipo
WHERE
var.disponibilidade = 1
AND(
ctp_1.codigo = 'tamanho' AND cvl_1.valor IN('p')
)
GROUP BY
ppr2.id
) AS grp_var ON grp_var.id_perfil_sub = ppr.id
INNER JOIN produto_categoria prc ON pro.id = prc.produto_id
INNER JOIN categoria cat ON prc.categoria_id = cat.id
WHERE
pro.disponibilidade = 1 AND prc.categoria_id IN (164, 165, 166)
) AS produto ON produto.perfil_produto_id = var.id_perfil_produto
GROUP BY
cvl.id
ORDER BY
ctp.tipo ASC,
ctp.id
I need the field ctp.codigo from the outer table inside thist part:
WHERE
var.disponibilidade = 1
AND(
ctp_1.codigo = 'tamanho' AND cvl_1.valor IN('p')
)
for this section to be as follows:
WHERE
var.disponibilidade = 1
AND(
(ctp.codigo != 'tamanho' AND ctp_1.codigo = 'tamanho' AND cvl_1.valor IN('p'))
OR
(ctp.codigo = 'tamanho')
)
It's not possible to reference columns from the outer query from inside an inline view query.
In the MySQL venacular, the inline view query is called a "derived table". And that name makes sense, because of the way MySQL processes it. The execution plan first materializes the inline view query into a temporary(-ish) table. Once that is done, then the outer query can run, referencing the contents of the derived table.
MySQL doesn't have available the columns from the outer query at the time the inline view query runs.
It is possible to reference columns from the outer query inside a subquery that appears for example in the SELECT list, or in the WHERE clause. We call a subquery that references columns from outer query a "correlated subquery".

MYSQL - CONCATENATE a lookup tables columns into one row

I have the following table structure:
tbl_catalogue_state
In tbl_catalogue there is a part number 58674 that has three states in the tbl_catalogue_state_lk table. Here is the result when I run a query inner joining the three tables.
As expected there are multiple rows returned.
Is there a way to only return one row having the values for each catalgue_state_id on the same row?
I would also like the ability to ignore a row for example:
select tbl_catalogue.catalogue_part, tbl_catalogue_state.catalogue_state_id from tbl_catalogue
inner join tbl_catalogue_state_lk on tbl_catalogue.catalogue_id = tbl_catalogue_state_lk.catalogue_id
inner join tbl_catalogue_state on tbl_catalogue_state_lk.catalogue_state_id = tbl_catalogue_state.catalogue_state_id
where tbl_catalogue_state_lk.catalogue_state_id <> 1;
The above select still returns two rows.
UPDATE
I was able to use GROUP_CONCAT:
select tbl_catalogue.catalogue_part, GROUP_CONCAT(tbl_catalogue_state.catalogue_state_id) as cat_state from tbl_catalogue
inner join tbl_catalogue_state_lk on tbl_catalogue.catalogue_id = tbl_catalogue_state_lk.catalogue_id
inner join tbl_catalogue_state on tbl_catalogue_state_lk.catalogue_state_id = tbl_catalogue_state.catalogue_state_id
where tbl_catalogue_state_lk.catalogue_state_id <> 1
group by tbl_catalogue.catalogue_id;
My issue is the above statement still returns a row. I need it to return nothing.
I was able to use not exists:
select tc.catalogue_part, GROUP_CONCAT(tcs.catalogue_state_id) as cat_state from tbl_catalogue as tc
inner join tbl_catalogue_state_lk as tcsl on tc.catalogue_id = tcsl.catalogue_id
inner join tbl_catalogue_state as tcs on tcsl.catalogue_state_id = tcs.catalogue_state_id
where
not exists
(
select tcsl2.catalogue_state_id from tbl_catalogue_state_lk as tcsl2
where tcsl2.catalogue_state_id = 6 and tcsl2.catalogue_id = tc.catalogue_id
)
and
not exists
(
select tcsl3.catalogue_state_id from tbl_catalogue_state_lk as tcsl3
where tcsl3.catalogue_state_id = 1 and tcsl3.catalogue_id = tc.catalogue_id
)
and
not exists
(
select tcsl3.catalogue_state_id from tbl_catalogue_state_lk as tcsl3
where tcsl3.catalogue_state_id = 2 and tcsl3.catalogue_id = tc.catalogue_id
)
group by tc.catalogue_id;

Unable to FORCE INDEX on LEFT JOIN subquery?

I have a complicated query which involves a subquery inside a LEFT JOIN statement. The process time is ~2s.
If I replace the subquery with a temporary table, I hit the same ~2s.
If I create an index in the temporary table, I have ~0.05s.
I am trying to figure out why I can't work with Mysql USE INDEX or FORCE INDEX directly in the LEFT JOIN STATEMENT.
Here is a simple query which will return a 'check your mysql syntax' error.
SELECT *
FROM table1 t1
LEFT JOIN (SELECT id_project FROM table2) t2
FORCE INDEX FOR JOIN (id_project)
ON (t2.id_project = t1.id_project);
Why can't I use a FORCE INDEX with a LEFT JOIN subquery?
--edit: here is the actual query returning mysql error
SELECT
p.id_projet, pa.piste_affaire, ty.type, p.e_force, gr.groupe, p.client, p.intitule_affaire,
ag.agence, st.statut, p.winratio, p.justification_nogo, p.webdoc, sp.sponsor, dis.domain_is,
cons.constructeur, p.date_chgt_statut, p.date_creation,
p.ca_estimatif, p.tcv, p.marge,
tarp.target_price, p.marketable_price,
p.remise_propal_initiale, p.remise_propal_reel,
p.date_de_signature, p.debut_build, p.fin_build,
his.date, his.login, co.contrat,
p.duree_contrat, p.fqp, cap.capitalisation,
p.international, p.risk_management, p.asi,
GROUP_CONCAT(com.commentaire,',') AS COMMENT
FROM
piste_affaire pa, statut st, TYPE ty, agence ag,
domain_is dis, sponsor sp, constructeur cons, groupe gr,
target_price tarp, contrat co, historique his, capitalisation cap,
projet p
LEFT JOIN commentaire com ON (p.id_projet=com.id_projet)
LEFT JOIN bizdev bizd ON (p.id_bizdev = bizd.id_bizdev )
LEFT JOIN (SELECT eco_projet.id_projet,
GROUP_CONCAT(
CONCAT(ecosysteme)
ORDER BY ecosysteme SEPARATOR ', ' ) AS ecosysteme
FROM ecosysteme NATURAL JOIN eco_projet, projet
WHERE eco_projet.id_projet = projet.id_projet
GROUP BY eco_projet.id_projet) AS tmpeco
/* if I delete that line, everything works as intended with ~2s query*/
FORCE INDEX FOR JOIN (id_projet)
/* ** */
ON (tmpeco.id_projet = p.id_projet)
WHERE
ag.id_division != 2 AND
dis.id_division != 2 AND
st.id_division != 2 AND
pa.id_division != 2 AND
cons.id_division != 2 AND
p.type_division = 1 AND
pa.id_piste_affaire = p.id_piste_affaire AND
p.id_statut = st.id_statut AND
p.id_type = ty.id_type AND
p.id_agence = ag.id_agence AND
p.id_domain_is = dis.id_domain_is AND
p.id_sponsor = sp.id_sponsor AND
p.id_constructeur = cons.id_constructeur AND
p.id_groupe = gr.id_groupe AND
p.id_target_price = tarp.id_target_price AND
p.id_contrat = co.id_contrat AND
p.id_projet = his.id_projet AND
p.id_capitalisation = cap.id_capitalisation
GROUP BY p.id_projet;
Why the subquery? Try this:
SELECT *
FROM table1 t1
LEFT JOIN table2 t2
FORCE INDEX FOR JOIN (id_project)
ON (t2.id_project = t1.id_project);
You probably won't even need to force the index with the above query.

Update statement with nested joins not working

I need to update multiple records in a table based upon the sum of some values in another table. Here is my query:
UPDATE aallinnot2 c SET c.Energ_Kcal = ( SELECT d.id1, SUM( c.Energ_Kcal)
FROM aaingred a
LEFT JOIN aaweight b ON a.unit = b.uni
LEFT JOIN aallinnot2 c ON a.mfdfsds = c.NDB_No
LEFT JOIN aalinfsds d ON a.fsdsnum = d.id1
WHERE d.own_id =42
GROUP BY id1 )
WHERE c.NDB_No
IN ( SELECT DISTINCT `fsdsnum`
FROM `aaingred`
WHERE `usernum` LIKE '42'
)
MySQL said:
#1093 - You can't specify target table 'c' for update in FROM clause
Unfortunately, I don't know how to get my values without referencing target table 'c'! Is there a workaround for this?
With the crazy table/column names and indecipherable logic, this might be the ugliest query I have ever seen. Congrats. :)
I think the following should work (or this approach). The main problem was untangling the group-by expression-- you need to give the database engine a dataset where each row in the target table is joined to a set that contains the updated value for that row. So here, select the new values in a sub-query, then join that sub-query to the original table.
EDIT Fixed some syntax
UPDATE
(
SELECT d.id1, SUM (c.Energ_Kcal) AS Sum_Energ_Kcal
FROM aaingred a
LEFT JOIN aaweight b ON a.unit = b.uni
LEFT JOIN aallinnot2 c ON a.mfdfsds = c.NDB_No
LEFT JOIN aalinfsds d ON a.fsdsnum = d.id1
WHERE d.own_id =42
GROUP BY id1
) d
,aaingred a, aallinnot2 d
SET Energ_Kcal = d.Sum_Energ_Kcal
WHERE d.id1 = a.fsdsnum
AND a.mfdfsds = aallinnot2.NDB_No
AND c.NDB_No IN (
SELECT DISTINCT `fsdsnum`
FROM `aaingred`
WHERE `usernum` LIKE '42'
);
I'm not sure about mysql, but with SQL Server the statement would be something like this:
UPDATE aallinnot2
SET Energ_Kcal = (
SELECT SUM( c.Energ_Kcal)
FROM aaingred a
LEFT JOIN aaweight b ON a.unit = b.uni
LEFT JOIN aallinnot2 c ON a.mfdfsds = c.NDB_No
LEFT JOIN aalinfsds d ON a.fsdsnum = d.id1
WHERE d.own_id =42)
WHERE c.NDB_No
IN ( SELECT DISTINCT `fsdsnum`
FROM `aaingred`
WHERE `usernum` LIKE '42')
You can't alias the table to be updated in the UPDATE clause, but you can in the FROM clause.

Troubles with nested queries

I have this query which works perfectly:
SELECT *
FROM Customer
WHERE SacCode IN
(
SELECT SacCode
FROM SacCode
WHERE ResellerCorporateID = 392
ORDER BY SacCode
)
AND CustomerID IN
(
SELECT CxID
FROM CustAppointments
WHERE AppRoomID IN
(
SELECT AppRoomID
FROM ClinicRooms
WHERE ClinID IN
(
SELECT ClinID
FROM AppClinics
WHERE ClinDate >='20090101'
AND ClinDate <='20091119'
)
)
)
However, I need to see the value of ClinDate (inside the last nested query)...
How do I do it?
Thanks.
I'd rewrite the query using joins. Then, you can access any data from any of the joined tables.
For example, you could rewrite your query like this:
SELECT c.*, ac.ClinDate
FROM Customer c
JOIN SacCode sc ON sc.SacCode = c.SacCode
JOIN CustAppointments ca ON ca.CustomerID = c.CustomerID
JOIN ClinicRooms cr ON cr.AppRoomID = ca.AppRoomID
JOIN AppClinic ac ON ac.ClinID = cr.ClinID
WHERE ac.ClinDate >='20090101'
AND ac.ClinDate <='20091119'
AND sc.ResellerCorporateID = 392
Think I'd use derived table in the FROM statement rather than 3 deep nested query, will allow you to access values and will look a LOT better.
You'll need to copy the subselects to the FROM clause or rewrite the query using JOINs.
it should look something like this:
SELECT c.*, a.ClinDate
FROM Customer c
inner join CustAppointments ca
inner join ClinicRooms cr
inner join AppClinics a
where c.SacCode IN
(
SELECT SacCode
FROM SacCode
WHERE ResellerCorporateID = 392
ORDER BY SacCode
)
and c.CustomerID = ca.CxID
and ca.AppRoomID = cr.AppRoomID
and cr.ClinID = a.ClinID
and a.ClinDate >='20090101'
and a.ClinDate <='20091119'