在MYSQL查询中优化ORDER BY

时间:2018-03-06 04:25:47

标签: mysql select sql-order-by

我有以下查询使用order by with limit。需要2分25秒才能获得16k左右的数据。我也做了正确的指数,但仍然保持缓慢。当仅应用LIMIT 20时也采用相同的时间。当删除ORDER BY时,查询在17秒内获取相同的数据。所有表都在latin1字符集中。请建议任何可能的解决方案。

SELECT 
a.customer,
a.division AS division,
a.noitaziraa_id AS noitaziraaId,
DATE_FORMAT(a.request_date, '%m/%d/%Y') AS RequestDate,
a.request_date AS RequestDateSort,
DATE_FORMAT(noita.date_of_birth, '%m/%d/%Y') AS dob,
noita.date_of_birth AS dobSort,
IF(
a.noita_type = 'Noita Stay',
a.length_of_stay,
NULL
) AS requestedDays,
IF(
a.noita_type = 'Noita Stay',
CONCAT_WS(
  ',',
  a.facility_provider_city,
  a.facility_provider_state
),
''
) AS facilityCityState,
IF(
a.noita_type = 'Noita Stay',
IFNULL(
  DATE_FORMAT(aips.admission_date, '%m/%d/%Y'),
  ''
),
''
) AS admitDate,
IF(
a.noita_type = 'Noita Stay',
aips.admission_date,
''
) AS admitDateSort,
IF(
a.noita_type = 'Noita Stay',
IFNULL(
  DATE_FORMAT(
    aipsd.discharge_date,
    '%m/%d/%Y'
  ),
  ''
),
''
 ) AS dischargeDate,
  IF(
a.noita_type = 'Noita Stay',
aipsd.discharge_date,
''
 ) AS dischargeDateSort,
  IF(
a.noita_type = 'Noita Stay',
IFNULL(dl1.`description`, ''),
''
 ) AS dischargeDisposition,
 a.gender,
 a.age,
  a.relationship AS relationship,
  noita.groupid,
  a.request_type AS requestType,
  a.prog_status AS programStatus,
 dl.description AS billingDetails,
 a.referred_to_npi AS NPI,
 a.program AS program,
 CASE
  WHEN a.status = 'OPEN' 
  THEN DATEDIFF(NOW(), a.auth_request_date) 
  ELSE 0 
  END AS 'daysSinceRequest',
  a.first_name AS firstName,
 a.last_name AS lastName,
dl2.description AS levelOfUrgency,
 a.member_id AS memberId,
a.created_full_name AS createdFullName,
CONCAT_WS(
',',
COALESCE(a.assigned_to, NULL),
COALESCE(
  a.auth_review_assigned_user_name,
  NULL
),
COALESCE(
  a.auth_con_review_assigned_user_name,
  NULL
),
COALESCE(a.assigned_queue, NULL),
COALESCE(
  a.auth_review_assigned_queue_name,
  NULL
 ),
 COALESCE(
  a.auth_con_review_assigned_queue_name,
  NULL
 )
 ) AS assignedTo,
a.status,
DATE_FORMAT(a.opened_date, '%m/%d/%Y') AS openDate,
 a.opened_date AS openDateSort,
 DATE_FORMAT(a.closed_date, '%m/%d/%Y') AS closedDate,
 a.closed_date AS closedDateSort,
 a.noita_type AS authType,
 a.facility_provider AS facilityProvider,
 a.length_of_stay AS lengthOfStay,
 DATE_FORMAT(a.requested_from, '%m/%d/%Y') AS authFromDate,
 a.requested_from AS authFromDateSort,
 DATE_FORMAT(a.requested_through, '%m/%d/%Y') AS authToDate,
 a.requested_through AS authToDateSort,
 a.pended,
  a.diagnosis AS diagnosis,
 a.diagnosis_desc AS diagDesc,
 a.auth,
a.denied,
a.excluded,
a.admit_type AS admitType,
a.service_type AS serviceType,
a.proc,
a.proc_desc AS procDesc,
a.plan 
FROM
main_table a 
INNER JOIN noitaciary noita 
ON noita.id = a.noitaciary_id 
INNER JOIN usermanagement.`user` usr 
ON a.created_by = usr.id 
AND 
CASE
  WHEN CONCAT(usr.firstname, ' ', usr.lastname) IN ('a', 'b *', 'c', 
   'd', 'd', 'f') 
  THEN 1 = 1 
  ELSE (
    COALESCE(usr.`employer`, '') NOT IN ('r', 's')
  ) 
  END 
    LEFT JOIN noitaziraa_ips AS aips 
    ON aips.noitaziraa_id = a.auth_id 
  LEFT JOIN db1.`noitaziraa_history` ah 
   ON ah.noitaziraa_id = a.noitaziraa_id 
 LEFT JOIN noitaziraa_ips_discharge AS aipsd 
  ON aipsd.noitaziraa_ips_id = aips.id 
 LEFT JOIN noitaziraa_phr AS aphr 
  ON aphr.noitaziraa_id = a.auth_id 
  LEFT JOIN noitaziraa_sp AS asp 
  ON asp.noitaziraa_id = a.auth_id 
  LEFT JOIN noitaziraa_decisions AS auth_dec 
 ON a.auth_id = auth_dec.noitaziraa_id 
 LEFT JOIN mytable AS aa 
 ON a.noitaziraa_id = aa.noitaziraa_id 
LEFT JOIN db1.dw_lookup dl 
 ON auth_dec.details = dl.code 
LEFT JOIN db1.`dw_lookup` dl1 
ON dl1.`code` = aipsd.`discharge_diposition` 
 AND dl1.`data_type` = 'dataTypeName' 
 LEFT JOIN db1.dw_lookup dl2 
 ON aa.level_of_urgency = dl2.code 
 AND dl2.data_type = 'dataTypeName1' 
LEFT JOIN 
    (SELECT 
     * 
   FROM
  (SELECT 
    hh.noitaziraa_id,
    hh.`status` 
  FROM
    db1.`noitaziraa_history` hh,
    main_table a 
  WHERE hh.noitaziraa_id = a.noitaziraa_id 
    AND hh.client = 'certainValue' 
    AND DATE(hh.last_updated) < '2017-12-01 00:00:00' 
  GROUP BY hh.`last_updated` 
  ORDER BY hh.last_updated DESC) tmp 
GROUP BY noitaziraa_id) AS tps 
ON tps.noitaziraa_id = a.noitaziraa_id 
  WHERE a.customer LIKE 'certainValue%' 
   AND a.status <> 'VOID' 

  AND DATE(auth_dec.requested_through) >= '2017-12-01 00:00:00' 
AND DATE(auth_dec.requested_through) <= '2017-12-05 00:00:00' 
AND DATE(a.opened_date) <= '2017-12-05 00:00:00' 
 AND (
 (
    DATE(ah.last_updated) BETWEEN '2017-12-01 00:00:00' 
  AND '2017-12-05 00:00:00' 
   AND ah.status IN (
    'OPEN',
    'CLOSED',
    'REOPENED',
    'CANCELED'
     )
    ) || (
    tps.noitaziraa_id = a.noitaziraa_id 
     AND tps.status IN (
    'OPEN',
    'CLOSED',
    'REOPENED',
    'CANCELED'
  )
   )
  ) 
  GROUP BY a.auth_id 
  ORDER BY groupid ASC 
  LIMIT 0, 20 

noitaziraa_history表包含大量行,必须保持连接以满足我的要求,这需要花费大量时间。

使用EXPLAIN提供以下内容: enter image description here

2 个答案:

答案 0 :(得分:4)

这需要逐步解决。

        SELECT  *
            FROM  
            (
                SELECT  hh.noitaziraa_id, hh.`status`
                    FROM  db1.`noitaziraa_history` hh, main_table a
                    WHERE  hh.noitaziraa_id = a.noitaziraa_id
                      AND  hh.client = 'certainValue'
                      AND  DATE(hh.last_updated) <  '2017-12-01 00:00:00'
                    GROUP BY  hh.`last_updated`
                    ORDER BY  hh.last_updated DESC
            ) tmp
            GROUP BY  noitaziraa_id

内部ORDER BY将被忽略;摆脱它。然后询问GROUP BY的两个级别是否真的有意义。

AND  DATE(hh.last_updated) <  '2017-12-01 00:00:00'

将其更改为

AND hh.last_updated < '2017-12-01'

原因:在函数(DATE)中隐藏可能已编入索引的列会使其无法使用索引。

然后将此综合索引添加到hh

INDEX(client, noitaziraa_id, last_updated, status) 

与此同时,你可能有一个严重的错误:为什么你在这个子查询和外部区域都指定了main_table a?这是一个错误吗?

      AND DATE(auth_dec.requested_through) >= '2017-12-01 00:00:00'
      AND DATE(auth_dec.requested_through) <= '2017-12-05 00:00:00'

- &GT;

      AND auth_dec.requested_through >= '2017-12-01'
      AND auth_dec.requested_through  < '2017-12-01' + INTERVAL 5 DAY

这些都没有使用,所以摆脱它们。这可能需要您在构造查询的代码上投入更多精力。 (或者是用手写的?)

    LEFT JOIN  noitaziraa_phr AS aphr  ON aphr.noitaziraa_id = a.auth_id
    LEFT JOIN  noitaziraa_sp AS asp  ON asp.noitaziraa_id = a.auth_id

LEFT JOIN - 除非您需要,否则不要使用它。您不需要其中的一些 - 可以通过引用auth_dec子句中的WHERE来发现。

dldl1dl2 - 这些位于LEFT JOINs链的末尾。删除它们,并删除对它们中的列的引用。然后在执行SELECTORDER BY之后,在外面添加一个额外的LIMIT图层以进入。这将减少来自&#34; lot&#34;只有20岁。

EXPLAIN显示表格caseload;查询没有这样的。请修理。

修正AND AND错字。

我现在退出了。

答案 1 :(得分:1)

根据这个问题,您也应用了正确的索引。我想你是对的。然后,请避免在noitaziraa_history表中使用LEFT JOIN,因为@Rick James也提到了这一点。如果可能,请确保在此表中加载数据,以便主表中的所有noitaziraa_id也在历史表中。现在,您可以应用INNER JOIN而不是正在使用的LEFT JOIN并查看结果。此外,根据Rick James的建议,重构目前似乎不合适或目前无用的所有内容。我确信如果主表中的同一行有多行数据,INNER JOIN将减少LEFT JOIN所用的时间。 更重要的是,如果您可以在JOIN期间使用nnoitaziraa_history表格,请使用任何条件过滤器,就像您在下面的子查询中所做的那样:

    INNER JOIN db1.`noitaziraa_history` ah 
    ON ah.noitaziraa_id = a.noitaziraa_id AND  hh.client = 'certainValue' AND  DATE(hh.last_updated) <  '2017-12-01 00:00:00'

请更新,如果它适合你:)谢谢!