How to access outer table data in join where - mysql

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

Related

Alternative to Where Exist clause 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.

MySQL join only on row existing

I have the following query
SELECT custconcompany, custconfirstname, custconlastname, custconemail, custconphone, shipaddress1, shipaddress2, shipcity, stateabbrv, shipzip, countryname, websitecheck.formfieldfieldvalue websitevalue, excludecheck.formfieldfieldvalue excludevalue
FROM obcisc_customers
JOIN ( (obcisc_shipping_addresses JOIN obcisc_countries
ON obcisc_shipping_addresses.shipcountryid = obcisc_countries.countryid)
LEFT JOIN obcisc_country_states
ON obcisc_shipping_addresses.shipstateid = obcisc_country_states.stateid
LEFT JOIN obcisc_formfieldsessions websitecheck
ON obcisc_shipping_addresses.shipformsessionid = websitecheck.formfieldsessioniformsessionid
LEFT JOIN obcisc_formfieldsessions excludecheck
ON obcisc_shipping_addresses.shipformsessionid = excludecheck.formfieldsessioniformsessionid)
ON obcisc_customers.customerid = obcisc_shipping_addresses.shipcustomerid
WHERE custgroupid = 11
AND websitecheck.formfieldfieldid = 24
AND excludecheck.formfieldfieldid = 30
AND excludecheck.formfieldfieldvalue != 'a:1:{i:0;s:3:"Yes";}'
ORDER BY shipstate, shipcity
This works great except I also need it to return rows where "excludecheck.formfieldfieldid=30" does not exist... right now it's not returning them
When writing a LEFT JOIN, any criteria on the table you're joining with should be put into the ON clause. If you put it into the WHERE clause, you'll filter out the results in the first table that don't have a matching row in the second table, because the NULL value that comes from the outer join will not match the criteria.
SELECT custconcompany, custconfirstname, custconlastname, custconemail, custconphone, shipaddress1, shipaddress2, shipcity, stateabbrv, shipzip, countryname, websitecheck.formfieldfieldvalue websitevalue, excludecheck.formfieldfieldvalue excludevalue
FROM obcisc_customers
JOIN obcisc_shipping_addresses
ON obcisc_customers.customerid = obcisc_shipping_addresses.shipcustomerid
JOIN obcisc_countries
ON obcisc_shipping_addresses.shipcountryid = obcisc_countries.countryid)
LEFT JOIN obcisc_country_states
ON obcisc_shipping_addresses.shipstateid = obcisc_country_states.stateid
LEFT JOIN obcisc_formfieldsessions websitecheck
ON obcisc_shipping_addresses.shipformsessionid = websitecheck.formfieldsessioniformsessionid
AND websitecheck.formfieldfieldid = 24
LEFT JOIN obcisc_formfieldsessions excludecheck
ON obcisc_shipping_addresses.shipformsessionid = excludecheck.formfieldsessioniformsessionid
AND excludecheck.formfieldfieldid = 30
AND excludecheck.formfieldfieldvalue != 'a:1:{i:0;s:3:"Yes";}')
WHERE custgroupid = 11
ORDER BY shipstate, shipcity
Another way to do it is by putting (excludecheck.formfieldfieldid = 30 OR excludecheck.formfieldfieldid IS NULL) in the WHERE clause. But this is more verbose and also I believe it's harder for MySQL to optimize, especially if you have several tables you're joining like this.

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.

Mysql - How to alias a whole table in a left join

I have a situation where a property table holds an address id (from the g_addresses table) and an applicant table also holds an address id from the g_addresses.
I'd like to left join these together but select all the fields in the table.
I know of using 'as' to make an alias for fields, but is there any way to produce an alias for a whole table?
SELECT *
FROM (`reference`)
LEFT JOIN `applicants` ON `applicants`.`id` = `reference`.`applicant_id`
LEFT JOIN `g_people` applicant_person ON `applicant_person`.`id` = `applicants`.`person_id`
LEFT JOIN `g_addresses` applicant_address ON `applicant_address`.`id` = `applicants`.`address_id`
LEFT JOIN `properties` ON `properties`.`id` = `reference`.`property_id`
LEFT JOIN `g_addresses` property_address ON `property_address`.`id` = `properties`.`address_id`
WHERE `reference`.`id` = 4
This produces a result containing only one address row and not both,
The row that is returned is the row from the final join and not the one previously, indicating it is overwriting when it is returned.
I don't think you should use masked references, like * or `reference`.*, in your case, because you may end up with a row set containing identical column names (id, address_id).
If you want to pull all the columns from the joined tables, you should probably specify them individually in the SELECT clause and assign a unique alias to every one of them:
SELECT
ref.`id` AS ref_id,
ref.`…` AS …,
…
app.`id` AS app_id,
…
FROM `reference` AS ref
LEFT JOIN `applicants` AS app ON app.`id` = ref.`applicant_id`
LEFT JOIN `g_people` AS ape ON ape.`id` = app.`person_id`
LEFT JOIN `g_addresses` AS apa ON apa.`id` = app.`address_id`
LEFT JOIN `properties` AS pro ON pro.`id` = ref.`property_id`
LEFT JOIN `g_addresses` AS pra ON pra.`id` = pro.`address_id`
WHERE ref.`id` = 4
Be more specific about columns you select
SELECT
applicant_address.*,
property_address.*,
applicants.*,
applicant_person.*,
properties.*
FROM (`reference`)
LEFT JOIN `applicants` ON `applicants`.`id` = `reference`.`applicant_id`
LEFT JOIN `g_people` applicant_person ON `applicant_person`.`id` = `applicants`.`person_id`
LEFT JOIN `g_addresses` applicant_address ON `applicant_address`.`id` = `applicants`.`address_id`
LEFT JOIN `properties` ON `properties`.`id` = `reference`.`property_id`
LEFT JOIN `g_addresses` property_address ON `property_address`.`id` = `properties`.`address_id`
WHERE `reference`.`id` = 4

what's fails in this query MYSQL

The following query only work when I have a reference in the table wm_purchased_products.purchased_article_id but when this is empty mysql_num_rows return 0
The query is:
SELECT (SUM(wm_products_quantities.new_quantity) - SUM(wm_purchased_products.purchased_article_total) ) AS stock_restante,
wm_products_wall.nombre,
wm_products_wall.detalles,
wm_products_wall.price,
wm_products_wall.image_full,
wm_products_wall.fecha,
wm_products_wall.article_hashid
FROM wm_products_wall,
wm_products_quantities,
wm_purchased_products
WHERE wm_products_wall.categoria = '$new_rquery_xp'
AND wm_products_wall.article_hashid = wm_products_quantities.hashid_ref
AND wm_products_wall.article_hashid = wm_purchased_products.purchased_article_id
GROUP BY wm_products_wall.article_hashid
ORDER BY stock_restante ASC
How to build this query to work when I have no record in the table wm_purchased_products.purchased_article_id
Your code FROM wm_products_wall, wm_products_quantities, wm_purchased_products means all three table are INNER JOIN with each other.(that is, each and every row in the first table is joined to each and every row in the second table and so on.)
You can let wm_products_wall LEFT JOIN with wm_purchased_products, so do wm_purchased_products.
SELECT
(SUM(wm_products_quantities.new_quantity) - SUM(wm_purchased_products.purchased_article_total) ) AS stock_restante,
wm_products_wall.nombre, wm_products_wall.detalles, wm_products_wall.price,
wm_products_wall.image_full, wm_products_wall.fecha,
wm_products_wall.article_hashid
FROM wm_products_wall
LEFT OUTER JOIN wm_products_quantities
ON wm_products_wall.article_hashid = wm_products_quantities.hashid_ref
LEFT OUTER JOIN wm_purchased_products
ON wm_products_wall.article_hashid = wm_purchased_products.purchased_article_id
WHERE wm_products_wall.categoria = '$new_rquery_xp'
GROUP BY wm_products_wall.article_hashid
ORDER BY stock_restante ASC