I am trying to make a query for a sales report, the code I have works excellent in the aspect that it returns the correct result, however I think it can be greatly improved since I am using multiple queries to generate the columns "Total_Productos", "Total_Servicios " and "Total", I have tried some ways to adjust it but I would like to hear some advice, I attach the code and the result it shows
SELECT f.numero, f.fecha, CONCAT(c.nombre, ' ', c.apellido) AS Cliente,
(SELECT GROUP_CONCAT(' x', CONVERT(fp2.cantidad, CHAR(10)), ' ', nombre) AS Product
FROM facturas_productos fp2
INNER JOIN productos ON productos.codigo = fp2.productos_codigo
WHERE fp2.facturas_numero = fp.facturas_numero) AS Productos,
TRUNCATE((SELECT SUM(fp2.cantidad * productos.precio_venta) AS T_Producto
FROM facturas_productos fp2
INNER JOIN productos ON productos.codigo = fp2.productos_codigo
WHERE fp2.facturas_numero = fp.facturas_numero), 2) AS Total_Productos,
(SELECT GROUP_CONCAT(CONCAT(' x', CONVERT(fs2.cantidad, CHAR(10)), ' ', nombre))
FROM facturas_servicios fs2
INNER JOIN servicios ON servicios.codigo = fs2.servicios_codigo
WHERE fs2.facturas_numero = fs.facturas_numero) AS Servicios,
TRUNCATE((SELECT SUM(fs2.cantidad * servicios.precio) AS T_Servicio
FROM facturas_servicios fs2
INNER JOIN servicios ON servicios.codigo = fs2.servicios_codigo
WHERE fs2.facturas_numero = fs.facturas_numero), 2) AS Total_Servicios,
TRUNCATE((SELECT SUM(fs2.cantidad * servicios.precio) AS T_Servicio
FROM facturas_servicios fs2
INNER JOIN servicios ON servicios.codigo = fs2.servicios_codigo
WHERE fs2.facturas_numero = fs.facturas_numero) +
(SELECT SUM(fp2.cantidad * productos.precio_venta) AS T_Producto
FROM facturas_productos fp2
INNER JOIN productos ON productos.codigo = fp2.productos_codigo
WHERE fp2.facturas_numero = fp.facturas_numero), 2) AS Total
FROM facturas f
INNER JOIN clientes c ON c.ID = f.clientes_ID
INNER JOIN facturas_productos fp ON fp.facturas_numero = f.numero
INNER JOIN facturas_servicios fs ON fs.facturas_numero = f.numero
WHERE f.fecha BETWEEN "2023-01-01" AND "2023-02-04"
GROUP BY f.numero, fp.facturas_numero, fs.facturas_numero, f.fecha, c.nombre, c.apellido
ORDER BY f.numero ASC;
Result from the query above:
I tried to take from the previous queries the values to calculate the total, however it gave me another unexpected result (generated an error/wrong value), i.e:
(SELECT GROUP_CONCAT(CONCAT(' x', CONVERT(fs2.cantidad, CHAR(10)), ' ', nombre)), fs2.cantidad * servicios.precio AS Monto
FROM facturas_servicios fs2
INNER JOIN servicios ON servicios.codigo = fs2.servicios_codigo
WHERE fs2.facturas_numero = fs.facturas_numero) AS Servicios
generates: Error Code: 1241. Operand should contain 1 column(s)
More info: I created the database from a diagram using forward engineer and not from scripts (still learning at university)
Database EER Diagram
If you are running MySQL >= 8.0.14, you can move your correlated sub-queries to lateral derived tables -
SELECT f.numero, f.fecha, CONCAT(c.nombre, ' ', c.apellido) AS Cliente,
fp.Productos, fp.Total_Productos,
fs.Servicios, fs.Total_Servicios,
IFNULL(fs.Total_Servicios, 0) + IFNULL(fp.Total_Productos, 0) AS Total
FROM facturas f
INNER JOIN clientes c ON c.ID = f.clientes_ID
LEFT JOIN LATERAL (
SELECT
GROUP_CONCAT('x', fp2.cantidad, ' ', nombre SEPARATOR ', ') AS Productos,
SUM(fp2.cantidad * productos.precio_venta) AS Total_Productos
FROM facturas_productos fp2
INNER JOIN productos ON productos.codigo = fp2.productos_codigo
WHERE fp2.facturas_numero = f.numero
) fp ON 1 = 1
LEFT JOIN LATERAL (
SELECT
GROUP_CONCAT('x', fs2.cantidad, ' ', nombre SEPARATOR ', ') AS Servicios,
SUM(fs2.cantidad * servicios.precio) AS Total_Servicios
FROM facturas_servicios fs2
INNER JOIN servicios ON servicios.codigo = fs2.servicios_codigo
WHERE fs2.facturas_numero = f.numero
) fs ON 1 = 1
WHERE f.fecha BETWEEN '2023-01-01' AND '2023-02-04'
ORDER BY f.numero ASC;
I have changed the joins to LEFT JOIN LATERAL as I assume it is possible for an invoice to contain products but not services, and vice versa.
An alternative approach, which does not require MySQL >= 8.0.14, is to aggregate all the products and services in their derived tables, instead of doing the lateral join -
SELECT f.numero, f.fecha, CONCAT(c.nombre, ' ', c.apellido) AS Cliente,
fp.Productos, fp.Total_Productos,
fs.Servicios, fs.Total_Servicios,
IFNULL(fs.Total_Servicios, 0) + IFNULL(fp.Total_Productos, 0) AS Total
FROM facturas f
INNER JOIN clientes c ON c.ID = f.clientes_ID
LEFT JOIN (
SELECT
fp.facturas_numero,
GROUP_CONCAT('x', fp.cantidad, ' ', nombre SEPARATOR ', ') AS Productos,
SUM(fp.cantidad * productos.precio_venta) AS Total_Productos
FROM facturas_productos fp
INNER JOIN productos ON productos.codigo = fp.productos_codigo
GROUP BY fp.facturas_numero
) fp ON f.numero = fp.facturas_numero
LEFT JOIN (
SELECT
fs.facturas_numero,
GROUP_CONCAT('x', fs.cantidad, ' ', nombre SEPARATOR ', ') AS Servicios,
SUM(fs.cantidad * servicios.precio) AS Total_Servicios
FROM facturas_servicios fs
INNER JOIN servicios ON servicios.codigo = fs.servicios_codigo
GROUP BY fs.facturas_numero
) fs ON f.numero = fs.facturas_numero
WHERE f.fecha BETWEEN '2023-01-01' AND '2023-02-04'
ORDER BY f.numero ASC;
This may be a little bit faster when retrieving all invoices but will probably be slower when retrieving a small subset of a large number of invoices.
And here's a db<>fiddle
Related
If I run this query directly in PHPMyAdmin, it returns 13420 rows in 0.2091 second, but if I run the exact same query as a stored procedure, it returns the same amount of row but it takes forever and sometimes the SQL server returns an out of memory exception.
I'm at a total loss - any advice would be welcome, because I can't work out why this slows everything down?!
SELECT
el.UID as LUID,
se.UID as DUID,
el.event_title,
el.event_synopsis,
se.behind_the_scenes,
se.sub_event_title,
se.event_eventDateAndTime,
se.event_eventDateAndTimeEnd,
el.event_confirmed,
el.event_active,
(
SELECT GROUP_CONCAT(sp2.color SEPARATOR ',')
FROM setup__spaces sp2
LEFT JOIN events__assigned_spaces eas2 ON ( eas2.space_id = sp2.UID )
LEFT JOIN events__events_list el2 ON (el2.UID = eas2.event_id)
WHERE el2.UID = el.UID
) as spaceColors,
(
SELECT GROUP_CONCAT(sp2.name SEPARATOR ', ')
FROM setup__spaces sp2
LEFT JOIN events__assigned_spaces eas2 ON ( eas2.space_id = sp2.UID )
LEFT JOIN events__events_list el2 ON (el2.UID = eas2.event_id)
WHERE el2.UID = el.UID
) as spaceNames,
(
SELECT GROUP_CONCAT(sp2.UID SEPARATOR ',')
FROM setup__spaces sp2
LEFT JOIN events__assigned_spaces eas2 ON ( eas2.space_id = sp2.UID )
LEFT JOIN events__events_list el2 ON (el2.UID = eas2.event_id)
WHERE el2.UID = el.UID
) as spaceIds,
(
SELECT GROUP_CONCAT(t.UID SEPARATOR ',')
FROM setup__tags t
LEFT JOIN events__assigned_tags eat ON ( eat.tag_id = t.UID )
LEFT JOIN events__events_list el2 ON (el2.UID = eat.event_id)
WHERE el2.UID = el.UID
) as tagIds,
(
SELECT GROUP_CONCAT(t.tag_name SEPARATOR ', ')
FROM setup__tags t
LEFT JOIN events__assigned_tags eat ON ( eat.tag_id = t.UID )
LEFT JOIN events__events_list el2 ON (el2.UID = eat.event_id)
WHERE el2.UID = el.UID
) as tagNames
FROM events__events_list el
INNER JOIN events__sub_events se ON (el.UID = se.event_masterEvent)
WHERE ((el.event_active='1') OR (el.event_active='0' AND el.event_confirmed = '1'))
AND el.company_uid = sp_company_uid
With thanks to #Akina for pointing me in the right direction, I found help from the following places:
stackoverflow.com - Selecting multiple columns/fields in MySQL subquery
geeksengine.com - How to use subquery in JOIN operation in MySQL
Here's the revised code that's now lightning fast!
SELECT
el.UID as LUID,
se.UID as DUID,
el.event_title,
el.event_synopsis,
se.behind_the_scenes,
se.sub_event_title,
se.event_eventDateAndTime,
se.event_eventDateAndTimeEnd,
el.event_confirmed,
el.event_active,
tags.names as tagNames,
tags.ids as tagIds,
spaces.names as spaceNames,
spaces.colors as spaceColors,
spaces.ids as spaceIds
FROM events__events_list el
INNER JOIN events__sub_events se ON (el.UID = se.event_masterEvent)
LEFT JOIN (
SELECT
el3.UID as el2uid,
GROUP_CONCAT(s.name SEPARATOR ', ') as names,
GROUP_CONCAT(s.color SEPARATOR ',') as colors,
GROUP_CONCAT(s.UID SEPARATOR ',') as ids
FROM setup__spaces as s
LEFT JOIN events__assigned_spaces eas ON ( eas.space_id = s.UID )
LEFT JOIN events__events_list el3 ON ( el3.UID = eas.event_id )
GROUP BY el3.UID
) as spaces on spaces.el2uid = el.UID
LEFT JOIN (
SELECT
el2.UID as el2uid,
GROUP_CONCAT(t.tag_name SEPARATOR ', ') as names,
GROUP_CONCAT(t.UID SEPARATOR ',') as ids
FROM setup__tags as t
LEFT JOIN events__assigned_tags eat ON ( eat.tag_id = t.UID )
LEFT JOIN events__events_list el2 ON ( el2.UID = eat.event_id )
GROUP BY el2.UID
) as tags on tags.el2uid = el.UID
WHERE ((el.event_active='1') OR (el.event_active='0' AND el.event_confirmed = '1'))
AND el.company_uid = sp_company_uid
Query
(SELECT
pid,
visitdate,
GROUP_CONCAT(tooth, ' - ', problem, ' - ', recomendation SEPARATOR ', <br>')
FROM tbl_finds_d) V
WHERE V.pid='1' AND V.visitdate = '16-03-2020'
TABLE
Want to get WHERE pid=1 AND visitdate=16-03-2020
pid visitdate tooth problem recomendation
1 16-03-2020 13 ASX DFFF
1 16-03-2020 12 JHJ HJLP
2 12-03-2020 14 JKB IJLHJ
UPDATE (copied from the comment)
SELECT *
FROM pendingtreatment A
INNER JOIN tbl_finds_m B ON B.pid = '$pid'
AND B.visitdate = '$visitdate'
INNER JOIN treatmentadviced C ON C.pid = '$pid'
AND C.visitdate = '$visitdate'
INNER JOIN treatmentlist D ON D.pid = '$pid'
AND D.visitdate = '$visitdate'
INNER JOIN tbl_appointments E ON E.pid = '$pid'
AND E.visitdate = '$visitdate'
INNER JOIN ( SELECT pid,
visitdate,
GROUP_CONCAT(tooth, ' - ', problem, ' - ', recomendation SEPARATOR ', <br>')
FROM tbl_finds_d V
WHERE V.pid='$pid'
AND V.visitdate = '$visitdate') F
WHERE A.pid = '$pid'
AND A.visitdate = '$visitdate'
Currently I have a select done in SQL Server and must have the same result for MySQL, achieving a scrip that works in the two would be even better . Below the script:
select STUFF( (SELECT ', ' + m_e.NAME + '=' + CAST(v.VALUE AS VARCHAR(MAX)) FROM MMP_USER_METADATA_INSTANCE i
left outer join MMP_USER_METADATA_INST_VALUE v on (i.id=v.USER_METADATA_INSTANCE_ID)
left outer join MMP_METADATA_ENTRY m_e on (v.METADATAENTRY_ID=m_e.id)
WHERE (u.id = i.USER_ID)
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)') ,1,2,'') AS 'Campos Adicionais' from mmp_user u
Tables:
MMP_USER_METADATA_INSTANCE,
MMP_USER_METADATA_INST_VALUE,
MMP_METADATA_ENTRY,
MMP_USER.
Result:
<table border="1"><tr BGCOLOR="#CCCCFF"><th>Campos Adicionais</th></tr>
<tr><td>Skype=Testes, Centro de Custo=16, Endereço Bairro=Teste, Endereço Cidade=, Estado Civil=44, Filhos=38, Graduação Curso=, Graduação Faculdade=Teste, Nivel de Idioma Inglês=46, Nivel de Idioma Espanhol=46, Outro Idioma=Teste, Link Linkedin=, Link Facebook=Teste, Esporte=teste, Time de Futebol=teste</td></tr>
</table>
In MySQL, you want something like this:
select (SELECT group_concat(m_e.NAME, '=', value separator ',')
FROM MMP_USER_METADATA_INSTANCE i left outer join
MMP_USER_METADATA_INST_VALUE v
on i.id = v.USER_METADATA_INSTANCE_ID left outer join
MMP_METADATA_ENTRY m_e
on v.METADATAENTRY_ID = m_e.id
WHERE u.id = i.USER_ID
) `Campos Adicionais`
from mmp_user u;
Product name supplier
A Su1
A Su1
A Su2
B Su1
C Su3
I want like this
A - su1, A-su2, B-su1, C-su3
Query:
SELECT
vtiger_salesorder.salesorder_no,
(Select
group_concat(DISTINCT concat(vtiger_products.productname, '-', vtiger_vendor.vendorname ) SEPARATOR ', ')
FROM
vtiger_salesorder
LEFT Join vtiger_inventoryproductrel ON vtiger_salesorder.salesorderid = vtiger_inventoryproductrel.id
inner Join vtiger_products ON vtiger_products.productid = vtiger_inventoryproductrel.productid
inner Join softMax_SalesOrderVendorInfo ON softMax_SalesOrderVendorInfo.salesorderid = vtiger_salesorder.salesorderid
LEFT JOIN vtiger_vendor ON softMax_SalesOrderVendorInfo.vendorid = vtiger_vendor.vendorid
where (vtiger_salesorder.salesorderid = vtiger_inventoryproductrel.id
AND vtiger_salesorder.salesorderid = vtiger_crmentity.crmid
and (softMax_SalesOrderVendorInfo.status = '0') )Group by vtiger_salesorder.salesorderid Limit 0,1) As SuName1
FROM
vtiger_salesorder
INNER JOIN vtiger_inventoryproductrel ON vtiger_salesorder.salesorderid = vtiger_inventoryproductrel.id
Inner Join vtiger_crmentity ON vtiger_salesorder.salesorderid = vtiger_crmentity.crmid
Order by vtiger_salesorder.salesorder_no
Given your original data, you can simply do this with something like:
select distinct group_concat(ProductName, '-', Supplier separator ', ')
from table t;
I have no idea what the query has to do with the question, because you already seem to have the data in the appropriate format.
I have a working Select at the bottom of the page. As you can see, it has 53 lines and, in my opinion, this is too much. I have been told me that there is an 'if' condition available in MySQL, but I have not able to make it work. The working Select unions 4 selects, because in each select, I need to join another table. Is it possible to join tables according to their content?
SELECT *
FROM (
SELECT m.`identificator` , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.`name` AS link_name,
mld.`alt` , mld.`title` , cpd.`slug`
FROM `wf_menu` m
LEFT JOIN `wf_menu_link` ml ON m.`id` = ml.`menu_id`
LEFT JOIN `wf_menu_link_desc` mld ON ml.`id` = mld.`link_id`
LEFT JOIN `wf_cms_post_desc` cpd ON ml.destination = cpd.post_id
WHERE mld.`lang_id` =1
AND mld.`lang_id` = cpd.`lang_id`
AND (ml.`type` = 'page'
OR ml.`type` = 'article')
UNION
SELECT m.`identificator` , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.`name` AS link_name,
mld.`alt` , mld.`title` , cpd.`slug`
FROM `wf_menu` m
LEFT JOIN `wf_menu_link` ml ON m.`id` = ml.`menu_id`
LEFT JOIN `wf_menu_link_desc` mld ON ml.`id` = mld.`link_id`
LEFT JOIN `wf_cms_category_desc` cpd ON ml.destination = cpd.category_id
WHERE mld.`lang_id` =1
AND mld.`lang_id` = cpd.`lang_id`
AND ml.`type` = 'cmscat'
UNION
SELECT m.`identificator` , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.`name` AS link_name,
mld.`alt` , mld.`title` , spd.`slug`
FROM `wf_menu` m
LEFT JOIN `wf_menu_link` ml ON m.`id` = ml.`menu_id`
LEFT JOIN `wf_menu_link_desc` mld ON ml.`id` = mld.`link_id`
LEFT JOIN `wf_shop_category_desc` spd ON ml.destination = spd.category_id
WHERE mld.`lang_id` =1
AND mld.`lang_id` = spd.`lang_id`
AND ml.`type` = 'shopcat'
UNION
SELECT m.`identificator` , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.`name` AS link_name,
mld.`alt` , mld.`title` , 'link' as slug
FROM `wf_menu` m
LEFT JOIN `wf_menu_link` ml ON m.`id` = ml.`menu_id`
LEFT JOIN `wf_menu_link_desc` mld ON ml.`id` = mld.`link_id`
WHERE mld.`lang_id` =1
AND ml.`type` = 'link'
) a
ORDER BY `order` DESC
You could possibly just do a single query, with each of the left joins in it, and sort out the resulting 'slug' column using IF statements. However this depends on exactly what you want output, and whether there are multiple matching rows on the other tables.
Something like this:-
SELECT m.identificator , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.name AS link_name,
mld.alt , mld.title , cpd.slug
FROM wf_menu m
LEFT JOIN wf_menu_link ml ON m.id = ml.menu_id
LEFT JOIN wf_menu_link_desc mld ON ml.id = mld.link_id
LEFT JOIN wf_cms_post_desc cpd ON ml.destination = cpd.post_id AND (ml.type = 'page' OR ml.type = 'article')
LEFT JOIN wf_cms_category_desc cpd ON ml.destination = cpd.category_id AND ml.type = 'cmscat' AND mld.lang_id = cpd.lang_id
LEFT JOIN wf_shop_category_desc spd ON ml.destination = spd.category_id AND ml.type = 'shopcat' AND mld.lang_id = spd.lang_id
WHERE mld.lang_id =1
AND mld.lang_id = cpd.lang_id
I think you need to use Dynamic SQL. I mean, build your end SQL into a temporary variable and execute it:
set #query = (SELECT ...);
PREPARE st FROM #query;
EXECUTE st;
Your first case coould be something like:
SET #query= (SELECT CONCAT(GROUP_CONCAT(CONCAT('SELECT m.`identificator` , ml.id AS link_id, ml.parent, ml.type,
ml.destination, ml.disabled, ml.order, mld.`name` AS link_name,
mld.`alt` , mld.`title` , cpd.`slug` FROM `wf_menu` m LEFT JOIN `wf_menu_link` ml ON m.`id` = ml.`menu_id` ',
CASE WHEN TTYPES.type='article' THEN
CONCAT('LEFT JOIN `wf_menu_link_desc` mld ON ml.`id` = mld.`link_id` LEFT JOIN `wf_cms_post_desc` cpd ON ml.destination = cpd.post_id WHERE mld.`lang_id` =1 AND mld.`lang_id` = cpd.`lang_id` AND (ml.`type` = \'page\' OR ml.`type` = \'',TTYPES.type,'\')')
END )
SEPARATOR ' UNION '), ' SELECT NULL FROM wf_menu_link WHERE 1=2') FROM (SELECT DISTINCT wf_menu_link.`type` FROM wf_menu_link) TTYPES);
(You can complete the remainding cases)
Running that query will fill #query variable with a generated query. This generated query should be the same than you posted in your question. Then you can run it as you can see above in this answer.
This is an example/idea you have to complete it and/or check it works in your model.