优化 MYSQL 查询中的 ORDER BY
Optimise ORDER BY in MYSQL Query
我有以下查询,它使用 order by with limit。拉动 16k 需要 2 分 25 秒 data.I 也完成了正确的索引,但仍然执行缓慢。仅应用 LIMIT 20 时也需要相同的时间。删除 ORDER BY 后,查询在 17 secs.All 中获取相同的数据 tables 在 latin1 charset.Please 中提出任何可能的解决方案。
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 table 包含大量行,必须将其左连接以满足我的要求,这会花费很多时间。
使用 EXPLAIN 给出以下结果:
这需要逐步解决。
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'
-->
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
-- 除非你需要,否则不要使用它。您不需要其中的一些——可以通过在 WHERE
子句中引用 auth_dec
来发现。
dl
、dl1
、dl2
-- 这些在 LEFT JOINs
链的末端。删除它们,并删除对其中列的引用。然后在 完成 ORDER BY
和 LIMIT
之后,在外面添加一个额外的 SELECT
层以进入它们 。这会将它们的引用数量从 "lots" 减少到仅 20.
EXPLAIN
显示一个tablecaseload
;查询没有这样的。请修复。
并修正 AND AND
拼写错误。
暂时退出
根据问题,您也应用了适当的索引。我想你是对的。然后,请避免将 LEFT JOIN 与 noitaziraa_history
table 一起使用,因为@Rick James 也提到了这一点。如果可能,请确保在这个 table 中加载数据,以便您的主要 table 中的所有 noitaziraa_id 都在历史记录中 table too.Now,您可以应用 INNER JOIN 而不是您正在使用的 LEFT JOIN 并查看结果。另外,按照 Rick James 的建议,重构所有看起来不合适或目前没有用的东西。如果您的主 table 中的同一行有多行数据,我相信 INNER JOIN 会减少 LEFT JOIN 所花费的时间。
还有一件事,如果可以,请在使用 nnoitaziraa_history
table 的 JOIN 期间应用任何条件过滤器,就像您在下面的子查询中所做的那样:
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'
如果对你有用,请更新:) 谢谢!
我有以下查询,它使用 order by with limit。拉动 16k 需要 2 分 25 秒 data.I 也完成了正确的索引,但仍然执行缓慢。仅应用 LIMIT 20 时也需要相同的时间。删除 ORDER BY 后,查询在 17 secs.All 中获取相同的数据 tables 在 latin1 charset.Please 中提出任何可能的解决方案。
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 table 包含大量行,必须将其左连接以满足我的要求,这会花费很多时间。
使用 EXPLAIN 给出以下结果:
这需要逐步解决。
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'
-->
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
-- 除非你需要,否则不要使用它。您不需要其中的一些——可以通过在 WHERE
子句中引用 auth_dec
来发现。
dl
、dl1
、dl2
-- 这些在 LEFT JOINs
链的末端。删除它们,并删除对其中列的引用。然后在 完成 ORDER BY
和 LIMIT
之后,在外面添加一个额外的 SELECT
层以进入它们 。这会将它们的引用数量从 "lots" 减少到仅 20.
EXPLAIN
显示一个tablecaseload
;查询没有这样的。请修复。
并修正 AND AND
拼写错误。
暂时退出
根据问题,您也应用了适当的索引。我想你是对的。然后,请避免将 LEFT JOIN 与 noitaziraa_history
table 一起使用,因为@Rick James 也提到了这一点。如果可能,请确保在这个 table 中加载数据,以便您的主要 table 中的所有 noitaziraa_id 都在历史记录中 table too.Now,您可以应用 INNER JOIN 而不是您正在使用的 LEFT JOIN 并查看结果。另外,按照 Rick James 的建议,重构所有看起来不合适或目前没有用的东西。如果您的主 table 中的同一行有多行数据,我相信 INNER JOIN 会减少 LEFT JOIN 所花费的时间。
还有一件事,如果可以,请在使用 nnoitaziraa_history
table 的 JOIN 期间应用任何条件过滤器,就像您在下面的子查询中所做的那样:
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'
如果对你有用,请更新:) 谢谢!