Проблема SQL с 12 подзапросами - PullRequest
1 голос
/ 27 июня 2010

Существует такая схема БД: альтернативный текст http://img156.imageshack.us/img156/9017/2706.png

Мне нужно написать запрос.

Для каждого врача мне нужна средняя стоимость посещения по месяцам за 2009 год.Результат (name_of_doctor, январь, февраль, ..., декабрь)

Я знаю, как это сделать с 12 подзапросами.Существует еще один более удобный способ?

Ответы [ 3 ]

2 голосов
/ 27 июня 2010

Попробуйте следующее. Вам может потребоваться изменить функции даты в зависимости от вашей СУБД. Я предполагал MySQL, но остальное должно быть универсальным SQL.

SELECT doctors.name, monthly.average, monthly.month
FROM doctors JOIN (
  SELECT AVG(cost), MONTH(visit_date) AS month FROM visits
  WHERE YEAR(visit_date) GROUP BY MONTH(visit_date)
) AS monthly ON doctors.id = visits.id_doc

Обратите внимание, что это может включать только месяцы для врачей, которые посещают. Таким образом, вам может понадобиться использовать IFNULL или COALESCE для очистки вашего вывода.

1 голос
/ 27 июня 2010

Я бы просто сделал обычный выбор с GROUP BY за месяц, и ваш дескриптор пользовательского интерфейса отображал бы его в виде 12 столбцов в поперечнике. Если вам действительно нужно это сделать, тогда это должно сработать:

SELECT
    D.name,
    AVG(CASE WHEN MONTH(V.visit_date) = 1 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 2 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 3 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 4 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 5 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 6 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 7 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 8 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 9 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 10 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 11 THEN V.cost ELSE NULL END),
    AVG(CASE WHEN MONTH(V.visit_date) = 12 THEN V.cost ELSE NULL END)
FROM
    Doctors D
INNER JOIN Visits V ON
    V.id_doc = D.id AND
    V.visit_date BETWEEN '2009-01-01' AND '2009-12-31'
GROUP BY
    D.name
ORDER BY
    D.name

Возможно, вам придется изменить функции даты в зависимости от вашей СУБД. Кроме того, вам, возможно, придется поэкспериментировать с крайними случаями - если у ваших дат есть компонент времени, он не будет ловить строки 12/31.

Наконец, я не знаю, если это изменится между RDBMS, и я не могу сейчас тестировать, но если AVG считает NULL как стоимость 0 вместо их дисконтирования, вам, возможно, придется сделать свое собственное усреднение - SUM (CASE ... стоимость ... 0) / СУММА (СЛУЧАЙ ... 1 ... 0). Я надеюсь, что это имеет смысл.

1 голос
/ 27 июня 2010

Вы можете сделать это легко, как показано ниже - но он будет перечислять стоимость каждого месяца в отдельной строке. Если вы хотите, чтобы затраты были в одной строке, вы можете использовать сводную инструкцию. Это для SQL 2008, если вам нужна сводка, мы можем Если у вас есть проблемы с производительностью, используйте сканирование диапазона по дате, а не datepart

select d.name,datepart(month,v.visit_date) as month,
avg(v.cost) as avgcost
from visits as v inner join 
doctors as d on v.id_doc=d.id
and datepart(year,v.visit_date)=2010
group by d.name,datepart(month,v.visit_date)
...