使用 LEFT JOINS MYSQL 优化子查询

Optimize subqueries with LEFT JOINS MYSQL

希望你能帮帮我!我目前有这个查询:

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

查询有效,但性能不如预期,有时需要 10 秒才能检索超过 1000 条记录,我查询此数据的主要 table 大约有 210,000 条记录,有人可以吗请帮助我使其更优化?

这是我的解释:

EXPLAIN

更新我的查询但性能没有变化:

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
  • 这些索引可能有帮助:

    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)
    

添加复合索引时,删除具有相同前导列的索引。 也就是当你同时拥有INDEX(a)和INDEX(a,b)时,把前者扔掉。

  • 我看到一些情况下同一个子查询被执行了两次,因为你需要两列。建议您使用 LEFT JOIN 同时获取两列。

  • 如果您经常需要在 JSON 列中测试内容,特别是如果类型需要 CASTing,请考虑向 table 添加一个额外的列以使其更容易没有所有 Json 开销的 testable。

  • (这个可以去掉一个排序,不改变效果。)把ORDER BY servicio.id DESC换成ORDER BY fecha_ingreso DESC, servicio.id DESC

  • ON CAST(servicio.problema_generico AS UNSIGNED) = prob.id 可能会阻止使用索引。查看是否可以修复数据类型以避免需要 CAST.