优化SQL查询以从大量MySQL数据库中获取数据

时间:2017-11-08 08:04:19

标签: php mysql query-optimization

我在从大量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>';
    }
}
?>

1 个答案:

答案 0 :(得分:1)

首先,这个子查询可能不会按照你的想法做到。

SELECT patient_id, MAX(start_date) AS lastAppDate 
FROM appointments WHERE appointment_status = 4

如果没有GROUP BY子句,该子查询将只使用start_date获取所有约会的最大appointment_status=4,然后随意选择一个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 patientSHOW CREATE TABLE appointments以获得与效果相关的进一步帮助。

此外,尝试上面的查询,不带AND NOT EXISTS子句,以及您使用的第二个查询。在这种情况下,运行2个查询可能比尝试一起运行更快。

请注意,我使用INNER JOIN来查找最新约会。这将导致所有从未预约的患者不被包括在查询中。如果您需要添加这些,只需通过从未预约过的患者中进行选择来获得结果。