измените подзапрос MySQL на соединение для повышения эффективности

#mysql #subquery

#mysql #подзапрос

Вопрос:

Можно ли изменить следующий запрос MySQL, чтобы использовать соединение вместо подзапроса для повышения эффективности (или другого способа повышения эффективности)? У меня есть таблица с посещениями пациентов в отделении неотложной помощи. В таблице указано время прибытия и отправления. Мне нужно, чтобы запрос возвращал общее количество пациентов, которые уже присутствовали в отделении неотложной помощи («перепись»), когда пациент прибыл.

Моя таблица выглядит примерно так:

  ------ ------ --------------------- --------------------- 
| id   | name |       arrival       |      departure      |
 ------ ------ --------------------- --------------------- 
|    1 | Joe  | 2010-01-01 00:00:00 | 2010-01-01 02:00:00 |
|    2 | John | 2010-01-01 00:05:00 | 2010-01-01 03:00:00 |
|    3 | Jane | 2010-01-01 01:00:00 | 2010-01-01 04:00:00 |
...
  

С желаемым результатом, подобным этому:

  ------ -------- 
| name | census |
 ------ -------- 
| Joe  |   0    |
| John |   1    |
| Jane |   2    |
...
  

Следующий запрос работает, но довольно медленно (около 3,5 секунд на 180 000 строк). Есть ли способ повысить эффективность этого запроса (с помощью какого-либо соединения или другого метода)?

 select name, arrival,
    (SELECT count(*)
FROM patient_arrivals as b
WHERE b.arrival <= a.arrival and b.departure >= a.departure) as census
FROM patient_arrivals as a
  

Ответ №1:

Я не думаю, что a join поможет. Вместо этого вам нужно реструктурировать запрос. Следующее получает количество пациентов в палате в любой конкретный момент времени:

   select t, sum(num) as num, @total := @total   num as total
  from (select arrival as t, 1 as num
        from patient_arrivals
        union all
        select departure, -1
        from patient_arrivals
       ) t cross join
       (select @total := 0) vars
  group by t
  order by t
  

Затем вы можете использовать это как подзапрос для соединения:

 select pa.*, t.total as census
from patient_arrivals pa join
     (select t, sum(num) as num, @total := @total   num as total
      from (select arrival as t, 1 as num
            from patient_arrivals
            union all
            select departure, -1
            from patient_arrivals
           ) t cross join
           (select @total := 0) vars
      group by t
      order by t
     ) tnum
     on pa.arrival = tnum.t;
  

Это дает номер, когда пациент прибывает. Для общего количества, которое перекрывается:

 select pa.*, max(t.total) as census
from patient_arrivals pa join
     (select t, sum(num) as num, @total := @total   num as total
      from (select arrival as t, 1 as num
            from patient_arrivals
            union all
            select departure, -1
            from patient_arrivals
           ) t cross join
           (select @total := 0) vars
      group by t
      order by t
     ) tnum
     on tnum.t between pa.arrival and pa.departure
group by pa.id