SQL order by the result of one operation - mysql

I need to order the result of this query by the result of (LIKES (puntuacion=1) - DISLIKE (puntuacion=0).
This is the old query where I order by the sum of likes (puntuacion=1).
"SELECT entradas.* , SUM(puntuacion) AS total_likes
FROM entradas
LEFT JOIN valoraciones ON valoraciones.entradas_id = entradas.id
and valoraciones.puntuacion=1
WHERE fecha>=:fecha1 AND aceptada=1
GROUP BY entradas.id
ORDER BY `total_likes` DESC
limit 5";
Tried this, but total_likes / total_dislikes are temporal variables and can't operation with them.
SELECT entradas.* , SUM(puntuacion=1) AS total_likes, SUM(puntuacion=0) AS total_dislikes, total_likes-total_dislikes AS TOTAL
FROM entradas
LEFT JOIN valoraciones ON valoraciones.entradas_id = entradas.id
WHERE aceptada=1
GROUP BY entradas.id
ORDER BY `total_likes` DESC
limit 5

SELECT entradas.* , (SUM(v1.puntuacion) - SUM(v0.puntuacion)) AS total_likes
FROM entradas
LEFT JOIN valoraciones v1 ON v1.entradas_id = entradas.id and v1.puntuacion=1
LEFT JOIN valoraciones v0 ON v0.entradas_id = entradas.id and v0.puntuacion=0
WHERE fecha >= :fecha1 AND aceptada=1
GROUP BY entradas.id
ORDER BY `total_likes` DESC
limit 5
[EDIT]
Sorry mate, the query abover is not quite alright. I think the right answer for what you are looking for is this one below:
SELECT entradas.* , SUM(IF(v.puntuacion = 1, 1, -1)) AS total_likes
FROM entradas
LEFT JOIN valoraciones v ON v.entradas_id = entradas.id
WHERE fecha >= :fecha1 AND aceptada=1
GROUP BY entradas.id
ORDER BY `total_likes` DESC
LIMIT 5

Related

Optimize subqueries with LEFT JOINS MYSQL [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 11 months ago.
Improve this question
I hope you can help me! I currently have this query:
SELECT servicio.*,
c.num_cliente,c.nombre,c.operador,
mdl.modelo_marca,mdl.modelo_telcel,
col.colores,
prob.problema,
diag.notas,diag.solucion,diag.tipo_servicios AS tipo_servicios_diagnostico,diag.nuevo_imei AS nuevo_imei_diagnostico,
diag.nivel_repar,diag.notasqc AS diagnos_notasqc,diag.fecha AS diagnos_fecha,diag.notas AS diagnos_notas,
user_name.nombre AS name_user,user_name.apellido,
(SELECT revision.status2_ser FROM revision WHERE revision.status2_ser IN('ENTREGADO') AND servicio.id = revision.id_servicio ORDER BY revision.id DESC LIMIT 1) AS status2_ser,
(SELECT revision.fecha_status FROM revision WHERE revision.status2_ser IN('ENTREGADO') AND servicio.id = revision.id_servicio ORDER BY revision.id DESC LIMIT 1) AS fecha_status_revision,
(SELECT revision.status2_ser FROM revision WHERE revision.status2_ser IN('REPARADO') AND servicio.id = revision.id_servicio ORDER BY revision.id DESC LIMIT 1) AS status2_ser_repadado,
(SELECT revision.fecha_status FROM revision WHERE revision.status2_ser IN('REPARADO') AND servicio.id = revision.id_servicio ORDER BY revision.id DESC LIMIT 1) AS revi2_fecha_status_revision,
(SELECT env.guia_entrega FROM envios AS env WHERE JSON_CONTAINS(env.envio_grupal, JSON_ARRAY(CAST(servicio.id AS CHAR))) OR env.id_servicio = servicio.id ORDER BY env.id DESC LIMIT 1) as guia_entrega_envio,
(SELECT env2.fecha_envio FROM envios AS env2 WHERE JSON_CONTAINS(env2.envio_grupal, JSON_ARRAY(CAST(servicio.id AS CHAR))) OR env2.id_servicio = servicio.id ORDER BY env2.id DESC LIMIT 1) as guia_entrega_envio_fecha
FROM servicio
LEFT JOIN usuarios AS user_name ON servicio.id_user = user_name.id
LEFT JOIN clientes AS c ON servicio.id_cac = c.id
LEFT JOIN modelos AS mdl ON servicio.id_modelo = mdl.id
LEFT JOIN colores AS col ON servicio.id_color = col.id
LEFT JOIN problemas_genericos AS prob ON CAST(servicio.problema_generico AS UNSIGNED) = prob.id
LEFT JOIN diagnostico AS diag ON diag.id = (SELECT id FROM diagnostico AS diag WHERE diag.id_servicio = servicio.id AND diag.tipo_servicios <> '' ORDER BY diag.id DESC LIMIT 1)
WHERE
servicio.fecha_ingreso >= '2022-03-07 00:00:00' AND servicio.fecha_ingreso <= '2022-03-16 23:59:59' AND servicio.status_ser IN('ENTREGADO') AND servicio.id_marca = 1
ORDER BY servicio.id DESC
the query works, but the performance is not as expected, it sometimes takes up to 10 seconds to retrieve more than 1000 records, the main table where I consult this data has approximately 210,000 records, could someone help me to make it more optimal please?
This is my explain:
EXPLAIN
Update my query but not there changes in the performance:
SELECT servicio.*,
c.num_cliente,c.nombre,c.operador,
mdl.modelo_marca,mdl.modelo_telcel,
col.colores,
prob.problema,
diag.notas,diag.solucion,diag.tipo_servicios AS tipo_servicios_diagnostico,diag.nuevo_imei AS nuevo_imei_diagnostico,
diag.nivel_repar,diag.notasqc AS diagnos_notasqc,diag.fecha AS diagnos_fecha,diag.notas AS diagnos_notas,
user_name.nombre AS name_user,user_name.apellido,
(SELECT env.guia_entrega FROM envios AS env WHERE JSON_CONTAINS(env.envio_grupal, JSON_ARRAY(CAST(servicio.id AS CHAR))) OR env.id_servicio = servicio.id ORDER BY env.id DESC LIMIT 1) as guia_entrega_envio,
(SELECT env2.fecha_envio FROM envios AS env2 WHERE JSON_CONTAINS(env2.envio_grupal, JSON_ARRAY(CAST(servicio.id AS CHAR))) OR env2.id_servicio = servicio.id ORDER BY env2.id DESC LIMIT 1) as guia_entrega_envio_fecha,
(CASE WHEN x.sta = 'ENTREGADO' THEN x.sta END) AS status2_ser,
(CASE WHEN x.sta = 'ENTREGADO' THEN x.g_fecha_status END) AS fecha_status_revision,
(CASE WHEN w.sta = 'REPARADO' THEN w.sta END) AS revi2_fecha_status_revision,
(CASE WHEN w.sta = 'REPARADO' THEN w.g_fecha_status END) AS revi2_fecha_status_revision
FROM servicio
LEFT JOIN usuarios AS user_name ON servicio.id_user = user_name.id
LEFT JOIN clientes AS c ON servicio.id_cac = c.id
LEFT JOIN modelos AS mdl ON servicio.id_modelo = mdl.id
LEFT JOIN colores AS col ON servicio.id_color = col.id
LEFT JOIN problemas_genericos AS prob ON CAST(servicio.problema_generico AS UNSIGNED) = prob.id
LEFT JOIN diagnostico AS diag ON diag.id = (SELECT id FROM diagnostico AS diag WHERE diag.id_servicio = servicio.id AND diag.tipo_servicios <> '' ORDER BY diag.id DESC LIMIT 1)
LEFT JOIN
(SELECT revision.status2_ser AS sta, revision.id_servicio,max(revision.fecha_status) AS g_fecha_status
FROM revision
WHERE revision.status2_ser IN("ENTREGADO")
GROUP BY revision.id_servicio) x ON servicio.id = x.id_servicio
LEFT JOIN
(SELECT revision.status2_ser AS sta, revision.id_servicio,max(revision.fecha_status) AS g_fecha_status
FROM revision
WHERE revision.status2_ser IN("REPARADO")
GROUP BY revision.id_servicio) w ON servicio.id = w.id_servicio
WHERE
servicio.fecha_ingreso >= '2022-03-07 00:00:00' AND servicio.fecha_ingreso <= '2022-03-16 23:59:59' AND servicio.status_ser IN('ENTREGADO') AND servicio.id_marca = 1
ORDER BY servicio.id DESC
These indexes may help:
servicio: INDEX(id_marca, fecha_ingreso, status_ser)
servicio: INDEX(id_marca, status_ser, fecha_ingreso)
revision: INDEX(status2_ser, id_servicio, id, fecha_status)
envios: INDEX(envio_grupal, id_servicio, id, guia_entrega)
envios: INDEX(envio_grupal, id_servicio, id, fecha_envio)
diag: INDEX(id_servicio, tipo_servicios, id)
When adding a composite index, DROP index(es) with the same leading columns.
That is, when you have both INDEX(a) and INDEX(a,b), toss the former.
I see some cases of the same subquery being performed twice because you needed two columns. Recommend you use a LEFT JOIN to get both columns at the same time.
If you regularly need to test things inside a JSON column, especially if the types are need CASTing, consider adding an extra column to the table to make it easily testable without all the Json overhead.
(This may eliminate a sort without changing the effect.) Replace ORDER BY servicio.id DESC with ORDER BY fecha_ingreso DESC, servicio.id DESC
ON CAST(servicio.problema_generico AS UNSIGNED) = prob.id probably prevents use of an index. See if you can fix the datatypes to avoid the need for CAST.

Optimizing MySQL query with multiple joins and Sub query

I am using the following query to get data from 10 table, It is working fine but quite slow, Is there any way to Optimizing the query.
Query: SELECT emi.emi_due_date,users.usr_mobile,users.usr_id,concat_ws(" ",users.usr_fname,users.usr_mname,users.usr_lname) as borrower,users.usr_status,users.usr_curnt_city, users.usr_email,emi.loan_id,emi.emi_show_date,sum(emi.emi_amount)-sum(ifnull(emi.settled_amount,0)) as due_amount,cb.cb_type,blr.bloan_collection_executive_id,blr.pp_allow,blr.bloan_legal_team_id,blr.bloan_legal_team_status,concat_ws(" ",cp.cp_fname,cp.cp_lname) as cp_name,cp.cp_mobile,cp.cp_firm_name,cp.cp_type,bg.guarantor_name,bg.guarantor_contact,pl.ecs_date,pd.p2p_date,
(SELECT instrument FROM borrower_payment_master WHERE loan_id = emi.loan_id order by id desc limit 0,1) as last_pmode,
(SELECT IFNULL(DATE_FORMAT(emi_show_date - INTERVAL 1 MONTH,"%m-%Y"),"") FROM emi AS e WHERE e.loan_id=emi.loan_id and e.emi_status < 2 ORDER by e.emi_show_date ASC limit 1) as paid_till,
(select payment_date from borrower_payment_master as bp where bp.loan_id=emi.loan_id order by bp.id desc limit 1) as last_emi_paid FROM emi AS emi
INNER JOIN borrower_loan_reg_requests AS blr ON emi.loan_id=blr.bloan_id
INNER JOIN users AS users ON users.usr_id=blr.bloan_user_id
INNER JOIN borrower_loan_disbursed_funds AS blf ON blf.df_bloan_id=emi.loan_id
LEFT JOIN channel_partners AS cp ON cp.cp_id=users.usr_cp_referral_id
LEFT JOIN borrower_posted_loans AS pl ON pl.pl_bloan_id=emi.loan_id
LEFT JOIN collection_bucket AS cb ON cb.cb_loan_id=emi.loan_id AND cb.cb_status = 1
LEFT JOIN borrower_guarantors AS bg ON bg.guarantor_borrower_id=users.usr_id
LEFT JOIN p2p_dates AS pd ON pd.p2p_loan_id=emi.loan_id AND pd.p2p_status = 1
WHERE emi.emi_status<2 AND emi.emi_amount != 0
AND (SELECT count(*) FROM borrower_payment_master as pm WHERE pm.loan_id = emi.loan_id
AND MONTH(pm.payment_date) = "'.date('m').'" AND YEAR(pm.payment_date) = "'.date('Y').'") = 0
AND (select s.settlement_date as sdate from settlement as s WHERE emi.loan_id=s.loan_id limit 1) !=""
group by emi.loan_id order by emi.loan_id desc

MySQL join on substring is slow

I have a query where I do a join on a substring, the problem is that this is really slow to complete. Is there a more effecient way to write this?
SELECT *, SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk AS s
JOIN butikk_ordre AS bo ON ordreId=bo.ecs_ordre_id AND butikkNr=bo.site_id
JOIN ecs_supplier AS l ON SUBSTRING( s.artikkelId, 1,2 )=l.lev_id
WHERE s.salgsDato>='2016-6-01' AND s.salgsDato<='2016-09-30'
GROUP BY l.lev_id ORDER BY total DESC
First, I would check indexes. For this query:
SELECT *, SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk s JOIN
butikk_ordre bo
ON s.ordreId = bo.ecs_ordre_id AND
s.butikkNr = bo.site_id JOIN
ecs_supplier l
ON SUBSTRING(s.artikkelId, 1, 2 ) = l.lev_id
WHERE s.salgsDato >= '2016-06-01' AND s.salgsDato <= '2016-09-30'
GROUP BY l.lev_id
ORDER BY total DESC ;
You want indexes on ecs_statistikk(salgsDato, ordreId, butikkNr, artikkelId), butikk_ordre(ecs_ordre_id, site_id), and ecs_supplier(lev_id)`.
Next, I would question whether you need the last JOIN at all. Does this do what you want?
SELECT LEFT(s.artikkelId, 2) as lev_id, *,
SUM(s.pris*s.antall) AS total, SUM(s.antall) AS antall
FROM ecs_statistikk s JOIN
butikk_ordre bo
ON s.ordreId = bo.ecs_ordre_id AND
s.butikkNr = bo.site_id
WHERE s.salgsDato >= '2016-06-01' AND s.salgsDato <= '2016-09-30'
GROUP BY LEFT(s.artikkelId, 2)
ORDER BY total DESC ;

mysql select 3 random rows of each type

I have created the following query:
SELECT random.aid, random.rand_pid, r3.filepath, r3.filename, r3.pid
FROM
(SELECT r.aid,
(SELECT r2.pid
FROM cpg_pictures r2
WHERE r2.aid = r.aid
ORDER BY RAND() LIMIT 1) AS 'rand_pid'
FROM cpg_pictures r
GROUP BY r.aid
ORDER BY r.aid DESC
LIMIT 10
) random
LEFT JOIN cpg_pictures AS r3 ON r3.pid = rand_pid
cpg_pictures is a table that has pictures
and aid is the album id
this query will get 1 random picture from each album id,
I would like to be able to modify the query so I can get 3 random pictures for each album id,
so any help would be appreciated.
I also would like to join this final result with the cpg_albums table where cpg_pictures.aid = cpg_albums.aid
Have you tried this?
SELECT random.aid, random.rand_pid, r3.filepath, r3.filename, r3.pid
FROM
(SELECT r.aid,
(SELECT r2.pid
FROM cpg_pictures r2
WHERE r2.aid = r.aid
ORDER BY RAND() LIMIT 1) AS 'rand_pid'
FROM cpg_pictures r
GROUP BY r.aid
ORDER BY r.aid DESC
LIMIT 10
) random
LEFT JOIN cpg_pictures AS r3 ON r3.pid = rand_pid
You can also simulate 1 and 2 of this http://beyondrelational.com/modules/2/blogs/70/posts/10845/return-top-n-rows.aspx

Lots of nested queries

SELECT
productos.prod_id,
productos.prod_codigo1,
productos.prod_descripcion,
(SELECT SUM(cotdetalle.cotd_cantidad)
FROM cotdetalle
WHERE cotdetalle.cotd_codigo = productos.prod_codigo1
AND cotdetalle.cotd_cote_id IN(
SELECT cotencabezado.cote_id
FROM cotencabezado
WHERE cotencabezado.cote_status = 'cerrada'
AND MONTH(cotencabezado.cote_cierre) = MONTH(NOW()) AND YEAR(cotencabezado.cote_cierre) = YEAR(NOW())
)
) AS cuantos,
(SELECT SUM(cotdetalle.cotd_cantidad * cotdetalle.cotd_precio)
FROM cotdetalle
WHERE cotdetalle.cotd_codigo = productos.prod_codigo1
AND cotdetalle.cotd_cote_id IN(
SELECT cotencabezado.cote_id
FROM cotencabezado
WHERE cotencabezado.cote_status = 'cerrada'
AND MONTH(cotencabezado.cote_cierre) = MONTH(NOW()) AND YEAR(cotencabezado.cote_cierre) = YEAR(NOW())
)
) AS monto
FROM productos
ORDER BY monto DESC
LIMIT 0, 50
Came out with this query last night, I guess my question is: is it too much? I'm pretty sure there are other ways to get the same results without all that nested queries.. It works but it takes some time on my development environment, I bet it will take a lot more on a production environment... Any suggestions?
Please let me know if you need the table structure...
Edit: Actually that second SELECT is what bothers me most, it is exactly the same as the first one but I need that second result, but if I try to get that second result in the first SELECT it give me the "Operand should contain 1 column(s)" error...
A little more info: I need to be able to order by monto, cuantos, productos.prod_codigo1, productos.prod_descripcion
sqlfiddle.com example: http://sqlfiddle.com/#!2/c4391/1
You should do this as a simple join with a group by:
SELECT p.prod_id, p.prod_codigo1, p.prod_descripcion,
t.quantos, t.monto
FROM productos p left outer join
(SELECT cotdetalle.cotd_codigo, SUM(cotdetalle.cotd_cantidad) as quantos,
SUM(cotdetalle.cotd_cantidad * cotdetalle.cotd_precio) as monto
FROM cotdetalle
WHERE cotdetalle.cotd_codigo = productos.prod_codigo1 and
cotdetalle.cotd_cote_id IN
(SELECT cotencabezado.cote_id
FROM cotencabezado
WHERE cotencabezado.cote_status = 'cerrada' and
MONTH(cotencabezado.cote_cierre) = MONTH(NOW()) AND
YEAR(cotencabezado.cote_cierre) = YEAR(NOW()
)
group by cotdetalle.cotd_codigo
) t
on t.cotd_codigo = p.prod_codigo1
ORDER BY monto DESC
LIMIT 0, 50
This should improve things. However, MySQL does a poor job with IN and a subquery. So, instead of "IN" in the subquery, we want to change that to a join. Note the addition of "distinct" in the subquery. This isn't necessary for IN but it is for the join:
SELECT p.prod_id, p.prod_codigo1, p.prod_descripcion,
t.quantos, t.monto
FROM productos p join
(SELECT cd.cotd_codigo, SUM(cd.cotd_cantidad) as quantos,
SUM(cd.cotd_cantidad * cd.cotd_precio) as monto
FROM cotdetalle cd join
(SELECT distinct cc.cote_id
FROM cotencabezado cc
WHERE cc.cote_status = 'cerrada' and
MONTH(cc.cote_cierre) = MONTH(NOW()) AND
YEAR(cc.cote_cierre) = YEAR(NOW()
) cc
on cd.cotd_cote_id = cc.cote_id
group by cd.cotd_codigo
) t
on t.cotd_codigo = p.prod_codigo1
ORDER BY monto DESC
LIMIT 0, 50
I didn't test this on SQL Fiddle, so there may be syntax errors.
Why not simplify the statement as
SELECT productos.prod_id,
productos.prod_codigo1,
productos.prod_descripcion,
( SELECT SUM(cotdetalle.cotd_cantidad) AS cuantos,
SUM(cotdetalle.cotd_cantidad * cotdetalle.cotd_precio) AS monto
FROM cotdetalle
WHERE cotdetalle.cotd_codigo = productos.prod_codigo1
AND cotdetalle.cotd_cote_id IN( SELECT cotencabezado.cote_id
FROM cotencabezado
WHERE cotencabezado.cote_status = 'cerrada'
AND MONTH(cotencabezado.cote_cierre) = MONTH(NOW())
AND YEAR(cotencabezado.cote_cierre) = YEAR(NOW())
)
)
FROM productos
ORDER BY monto DESC
LIMIT 0, 50
Ok, it ended like this:
SELECT p.prod_id, p.prod_codigo1, p.prod_descripcion, t.cuantos, t.monto
FROM productos AS p
LEFT JOIN(
SELECT cd.cotd_codigo,
SUM(cd.cotd_cantidad) AS cuantos,
SUM(cd.cotd_cantidad * cd.cotd_precio) AS monto
FROM cotdetalle AS cd
JOIN(
SELECT DISTINCT ce.cote_id
FROM cotencabezado AS ce
WHERE ce.cote_status = 'cerrada'
AND MONTH(ce.cote_cierre) = MONTH(NOW())
AND YEAR(ce.cote_cierre) = YEAR(NOW())
) AS ce
ON cd.cotd_cote_id = ce.cote_id
GROUP BY cd.cotd_codigo
) AS t
ON cd.cotd_codigo = p.prod_codigo1
ORDER BY monto DESC
LIMIT 0, 50
This works a lot better an it's based on Gordon Linoff's suggestions, Thanks man!