Оптимизировать этот запрос для 1000 пациентов в таблице пациентов - PullRequest
0 голосов
/ 19 февраля 2019

Что лучше всего оптимизировать этот запрос для 1000 пациентов в таблице пациентов:

SELECT patientid,firstname,lastname,mobilephone,email,
       Format(Coalesce((SELECT Sum(ammount) - Sum(( ammount * ( discount / 100 )))
FROM   invoice
WHERE  invoice.patientid = patient.patientid
       AND invoicednumber > 0) - (SELECT Sum(ammount) FROM payment WHERE payment.patientid = patient.patientid), 0), 0) AS answer,
       Date_format((SELECT Max(paymentdate) FROM payment WHERE  payment.patientid = patient.patientid), '%d-%m-%Y')
       AS
       lastpaymentdate
FROM   patient
WHERE  1 


[Patient Table][1]
[Invoice Table][2]
[Payment Table][3]
[Result Data][4]


  [1]: https://i.stack.imgur.com/rtCnm.png
  [2]: https://i.stack.imgur.com/czWKk.png
  [3]: https://i.stack.imgur.com/DRJnQ.png
  [4]: https://i.stack.imgur.com/XakbX.png

это занимает 10 секунд на amazon ec2 t2.micro

ЭтоПример sql fiddle

Ответы [ 3 ]

0 голосов
/ 19 февраля 2019

Хотя в качестве примера, ваше предложение WHERE в основном возвращает ВСЕХ пациентов.Вероятно, что убивает вашу производительность, так это то, что вы делаете 3 коррелированных запроса в своих списках полей, каждый раз запрашивая у каждого человека.Один раз для счета-фактуры и два раза для платежей.

Вместо этого я реструктурировал запрос на основе предварительных запросов LEFT-JOINS в таблицы счетов-фактур и платежей.Если вы посмотрите, таблица счетов-фактур предварительно суммируется с GROUP BY каждого идентификатора пациента.Аналогично по таблице платежей GROUP BY каждого пациента.Таким образом, в худшем случае каждый подзапрос будет возвращать по САМОЙ одной записи для данного пациента с суммой всех счетов и соответствующих скидок.Для платежей - сумма всех платежей и самая последняя дата.

Итак, начиная с таблицы пациентов, я могу присоединиться к общему идентификатору пациента, чтобы увидеть упрощенные итоговые значения до окончательной.выход и будет применим для всех пациентов.Да, вы все равно можете добавить критерии WHERE для дальнейшего ограничения, но это должно работать значительно лучше.

SELECT 
        p.patientid, 
        p.firstname, 
        p.lastname, 
        p.mobilephone, 
        p.email,
        coalesce( PatInv.JustSumOfAmount - PatInv.DiscountedAmounts, 0 )
            - coalesce( PatPay.PaidAmounts, 0 ) Answer,
        case when PatPay.PatientID IS NULL
            then ' '
            else Date_format( PatPay.LastPaymentDate, '%d-%m-%Y') end lastpaymentdate
    FROM 
        patient p
            LEFT JOIN
            ( select 
                    i.patientID,
                    sum( i.ammount ) justSumOfAmount,
                    sum( i.ammount * ( discount / 100 )) as discountedAmounts
                from
                    invoice i
                where
                    i.invoicedNumber > 0
                group by
                    i.patientID
                order by
                    i.patientid ) PatInv
                on p.patientid = patInv.patientID
            LEFT JOIN
            ( SELECT
                    pay.patientID, 
                    Sum(pay.ammount) paidAmounts,
                    max( pay.paymentDate ) LastPaymentDate
                FROM
                    payment pay
                group by
                    pay.patientID 
                order by
                    pay.patientid ) PatPay
                on p.patientID = patPay.PatientID

Я разместил этот запрос на SQL Fiddle Кроме того, просматривая ваши таблицы, вашиТаблицы счетов-фактур и платежей ДОЛЖНЫ КАЖДЫЙ иметь индекс идентификатора пациента, чтобы помочь оптимизировать запросы.Я не знаю, был ли это пример недосмотра с вашего sql-fiddle против производства, но это также повлияло бы на производительность.

Опция SQLFiddle, которую я имею, занимает 5 мс против ваших 2-3 мс, но этотолько для нескольких предоставленных записей.Могу поспорить, что против ваших 1000+ пациентов и 1000 транзакций в каждом счете и таблице платежей это сделает этот вариант запроса быстрее, чем коррелированные запросы.

Думайте о своем запросе как об этом.У меня 1000 пациентов.Запросите таблицу счетов 1000 раз, запросите таблицу платежей 2000 раз (1000 для суммы платежей, еще 1000 раз для самой последней даты для каждого пациента).

Мой запрос - один раз запросите таблицу счетов и суммируйте1 запись на пациента.Предварительно запросите таблицу платежей и суммируйте 1 запись на пациента.Присоедините пациентов к этим двум таблицам напрямую по ID.

0 голосов
/ 19 февраля 2019

Низкая производительность в любой системе RDBM обусловлена ​​плохим дизайном.В своей скрипте SQL вы сохраняете числа в поле varchar.Ваш текущий движок таблиц - MyISAM, который не поддерживает отношения !?No relationships => no index => no quick lookups

Чтобы улучшить производительность, я предлагаю вам изменить дизайн стола.Таблица счетов может иметь следующие изменения.Change Engine to InnoDB и

ALTER TABLE `invoice` ENGINE = Innodb;

ALTER TABLE `invoice` 
CHANGE COLUMN `patientid` `patientid` INT(12) UNSIGNED NOT NULL ,
CHANGE COLUMN `ammount` `ammount` DECIMAL(14,2) NOT NULL ,
CHANGE COLUMN `discount` `discount` DECIMAL(3,2) NULL DEFAULT '0' ,
ADD INDEX `fk_invoice_patient_idx` (`patientid` ASC);
ALTER TABLE `invoice` 
ADD CONSTRAINT `fk_invoice_patient`
  FOREIGN KEY (`patientid`)
  REFERENCES `test`.`patient` (`patientid`)
  ON DELETE RESTRICT
  ON UPDATE CASCADE;

, которые мгновенно улучшают любые поиски между пациентом и таблицей счетов.Сделайте те же изменения в своей таблице платежей, а затем сравните свои результаты.

После внесенных выше изменений вы можете создавать представления для платежей и счетов-фактур или подзапросов, как и другие, упомянутые выше, чтобы ускорить его.

0 голосов
/ 19 февраля 2019
SELECT patientid, firstname,lastname,mobilephone,email, FORMAT( COALESCE(
    ( SELECT SUM(ammount)-SUM((ammount * (discount/100))) 
    FROM invoice 
    INNER JOIN patient
    ON invoice.patientid = patient.patientid 
    and invoicednumber >0) - 
    ( SELECT SUM(ammount) 
      FROM payment 
      INNER JOIN patient 
      ON payment.patientid = patient.patientid ),0),0) AS answer, 
    DATE_FORMAT(( SELECT max(paymentdate) 
                  FROM payment 
                  INNER JOIN patient 
                  ON payment.patientid = patient.patientid ),'%d-%m-%Y') As lastpaymentdate 
from patient WHERE 1 

Это будет работать быстрее !!!!

Попробуйте и дайте мне знать

ОБНОВЛЕНИЕ
У меня есть предложение WHERE с внутренним соединением, которое поможет быстро получить данные.
Я бы также добавил, что если индексов по первичному ключу нет, добавьте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...