优化 SQL 查询以从大量 MySQL 数据库中获取数据
Posted
技术标签:
【中文标题】优化 SQL 查询以从大量 MySQL 数据库中获取数据【英文标题】:Optimizing the SQL Query to get data from large amount MySQL database 【发布时间】:2017-11-08 08:04:19 【问题描述】:我在从大量 mysql 数据库中获取数据时遇到问题。
使用下面的代码可以获取 10K 患者和 5K 预约的列表,这是我们的测试服务器。
但是,在我们的实时服务器上,患者数量超过 100K,预约数量超过 300K,当我运行代码一段时间后,它会出现 500 错误。
我需要患者的治疗状态为 1 或 3 并且在上次预约后一个月内没有预约的患者列表。 (以下代码适用于少量患者和预约)
如何优化第一个数据库查询,以便在 foreach 循环中不需要第二个数据库查询?
<?php
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
require_once('Db.class.php');
$patients = $db->query("
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
LEFT JOIN (SELECT patient_id, MAX(start_date) AS lastAppDate FROM appointments WHERE appointment_status = 4) LatestApp ON p.id = LatestApp.patient_id
WHERE
p.patient_treatment_status = 1 OR p.patient_treatment_status = 3
ORDER BY
p.id
");
foreach ($patients as $row)
$one_month_after_the_last_appointment = date('Y-m-d', strtotime($row['lastAppDate'] . " +1 month"));
$appointment_check = $db->single("SELECT COUNT(id) FROM appointments WHERE patient_id = :pid AND appointment_status = :a0 AND (start_date >= :a1 AND start_date <= :a2)", array("pid"=>"$row['id']","a0"=>"1","a1"=>"$row['lastAppDate']","a2"=>"$one_month_after_the_last_appointment"));
if($appointment_check == 0)
echo $patient_id = $row['id'].' - '.$row['lastAppDate'].' - '.$one_month_after_the_last_appointment. '<br>';
?>
【问题讨论】:
如果是我,我会暂时摆脱所有的 php,而专注于 sql。如果您同意,请参阅meta.***.com/questions/333952/… 理想情况下,您不应运行两个查询——尤其是循环中的一个。 我认为速度问题与子查询和LEFT JOIN有关。我认为您最好使用 INNER JOIN 来加快查询速度,删除您的子查询,正常 JOINING 另一个表,并按约会 id (如果有)使用分组,并在使用 MAX 的选择中获取最新约会. 【参考方案1】:首先,这个子查询可能不会像你想象的那样做。
SELECT patient_id, MAX(start_date) AS lastAppDate
FROM appointments WHERE appointment_status = 4
如果没有 GROUP BY 子句,该子查询将简单地采用与 appointment_status=4
的所有约会中的最大值 start_date
,然后任意选择一个 patient_id
。要获得您想要的结果,您需要GROUP BY patient_id
。
对于您的整体问题,请尝试以下查询:
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
INNER JOIN (
SELECT patient_id,
MAX(start_date) AS lastAppDate
FROM appointments
WHERE appointment_status = 4
GROUP BY patient_id
) LatestApp ON p.id = LatestApp.patient_id
WHERE
(p.patient_treatment_status = 1
OR p.patient_treatment_status = 3)
AND NOT EXISTS (
SELECT 1
FROM appointments a
WHERE a.patient_id = p.patient_id
AND a.appointment_status = 1
AND a.start_date >= LatestApp.lastAppDate
AND a.start_date < DATE_ADD(LatestApp.lastAppDate,INTERVAL 1 MONTH)
)
ORDER BY
p.id
添加以下索引,如果它尚不存在:
ALTER TABLE appointments
ADD INDEX (`patient_id`, `appointment_status`, `start_date`)
报告其执行情况以及数据是否正确。提供SHOW CREATE TABLE patient
和SHOW CREATE TABLE appointments
以获得与性能相关的进一步帮助。
另外,试试上面没有AND NOT EXISTS
子句的查询,以及您使用的第二个查询。在这种情况下,运行 2 个查询可能比尝试同时运行它们更快。
请注意,我使用INNER JOIN
查找最新约会。这将导致所有从未预约过的患者不包括在查询中。如果您需要添加这些,只需将通过从从未预约过的患者中选择的结果进行合并即可。
【讨论】:
@RickJames 你确定你没有忽略我在派生表中添加了GROUP BY patient_id
的事实吗?否则,我会同意你的。
糟糕;第一个派生表的更正建议:INDEX(appointment_status, patient_id, start_date)
.以上是关于优化 SQL 查询以从大量 MySQL 数据库中获取数据的主要内容,如果未能解决你的问题,请参考以下文章