2013-04-03 3 views
0

У меня есть таблица под названием patients. Я пытаюсь запросить среднемесячные дневные пациенты. Например, если я запрашиваю сегодня:Запрос среднемесячного населения

select count(*) from patients where active=1

возвращает:

----------- 
213 

(1 row(s) affected) 

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

Если я хочу получать среднее ежедневное количество активных пациентов в месяц, как бы я это сделал?

редактировать:

Некоторые примеры данных, показывающие Patient ID и их creation_date.

Patient ID creation_date 
----------- ----------------------- 
48   2011-11-16 08:59:34.000 
55   2011-11-16 09:09:20.000 
82   2011-11-16 09:32:48.000 
110   2011-11-16 09:42:38.000 
111   2011-11-16 09:42:53.000 
123   2011-11-16 09:47:01.000 
138   2011-11-16 09:58:02.000 
188   2011-11-16 10:20:03.000 
225   2011-11-16 10:32:53.000 
231   2011-11-16 10:34:48.000 
241   2011-11-16 10:38:13.000 
259   2011-11-16 10:44:35.000 
377   2011-12-17 10:26:21.000 
536   2012-02-02 16:10:57.000 
551   2012-02-05 11:42:22.000 
591   2012-02-12 12:14:57.000 

Моих желаемые результаты были бы что-то вроде:

Month       Month Number Year  ADP 
------------------------------ ------------ ----------- ----------- 
November      11   2011  240 
December      12   2011  280 
January      1   2012  220 
February      2   2012  225 
March       3   2012  241 
April       4   2012  212 
May       5   2012  210 

EDIT: кажется, что ответы, представленных дает мне среднесуточное количество для новых пациентов, что является меньшим числом. Мне нужен средний дневной подсчет для ВСЕГО населения.

EDIT: Я узнал, что отслеживание состояния пациента отслеживается в таблице под названием patient_booking_data с колонкой release. Это может помочь. Ниже примеры данных из этой таблицы:

id   pid   booking_no      date     release     active facility date_created   temporary temporary_no 
----------- ----------- -------------------------------- ----------------------- ----------------------- ------ -------- ----------------------- --------- -------------------------------- 
1   1   12345       2011-11-03 00:00:00.000 2011-11-15 10:45:00.000 0  11535 2011-11-03 12:45:36.000 0   NULL 
2   2   7890        2011-11-14 12:00:00.000 2011-11-21 07:01:00.000 1  11535 2011-11-14 08:45:33.000 0   NULL 
3   3   100        2011-11-14 09:00:00.000 2011-11-21 07:00:00.000 1  11535 2011-11-14 08:45:34.000 0   NULL 
4   4   111        2011-11-14 09:00:00.000 2011-11-21 07:01:00.000 1  11535 2011-11-14 08:45:34.000 0   NULL 
5   5   12        2011-11-14 10:20:00.000 2011-11-21 07:02:00.000 1  11535 2011-11-14 10:21:25.000 0   NULL 
6   6   1234        2011-11-14 00:00:00.000 2011-11-21 07:02:00.000 1  11535 2011-11-14 10:25:10.000 0   NULL 
7   7   1123        2011-11-14 11:14:00.000 2011-11-21 07:01:00.000 1  11535 2011-11-14 11:15:44.000 0   NULL 
+0

Что такое название поля даты в этой таблице? Вам нужно использовать все дни (включая праздники), чтобы получить среднее? – shahkalpesh

+1

Есть ли временная шкала, связанная с каждым пациентом, который детализирует дату начала и окончания его или ее пребывания? Если нет, и у вас есть только ваш логический «активный» столбец без данных истории, вам не повезло. –

+0

Пожалуйста, разместите некоторые данные образца и желаемый результат – Quassnoi

ответ

2
SELECT m, 
     cnt * 1./DATEDIFF(day, m, DATEADD(month, 1, m)) AS adp 
FROM (
     SELECT DATEADD(month, DATEDIFF(month, 0, creation_date), 0) AS m, COUNT(*) AS cnt 
     FROM mytable 
     GROUP BY 
       DATEADD(month, DATEDIFF(month, 0, creation_date), 0) 
     ) q 

Update:

Создать таблицу, которая будет отслеживать изменения статусов пациентов:

CREATE TABLE 
     status 
     (
     id INT NOT NULL PRIMARY KEY, 
     patient INT NOT NULL, 
     active BIT NOT NULL, 
     ts DATETIME NOT NULL 
     ) 

CREATE INDEX 
     ix_status_patient_ts 
ON  status (patient, ts) INCLUDE (active) 

и записывать каждое изменение статуса в пациент.

Затем запустить этот запрос:

WITH months (mon) AS 
     (
     SELECT '2012-01-01' 
     UNION ALL 
     SELECT DATEADD(month, 1, mon) 
     FROM months 
     WHERE m < '2014-12-01' 
     ) 
SELECT mon, COUNT(*) 
FROM patient p 
CROSS JOIN 
     months m 
CROSS APPLY 
     (
     SELECT TOP 1 
       active 
     FROM status s 
     WHERE s.patient = p.id 
       AND s.ts <= m.mon 
     ORDER BY 
       ts DESC, id DESC 
     ) s 
WHERE s.active = 1 
GROUP BY 
     mon 
+0

Похоже, что это возвращает средний новый пациент за каждый день, я бы хотел ОБЩЕЕ количество пациентов в день. – etm124

+0

+1 и [Пример в SQL-скрипте] (http://sqlfiddle.com/#!6/08198/15/0), @ etm124: Это показывает среднемесячное значение общего числа пациентов в день – Andomar

+0

@ etm124: пожалуйста разрабатывать. Является ли желаемый результат, который вы предоставили в своем примере, который должен исходить из данных, которые вы предоставили? На вашем выходе есть записи за март, апрель, май и т. Д., Которые, кажется, отсутствуют на входе. – Quassnoi

0

Вероятно наименее элегантны запросами я когда-либо написал, но это даст вам желаемый результат, который среднее число пациентов на дни в месяц:

SELECT DATENAME(MONTH, creation_date) AS [Month], 
     DATEPART(MONTH, creation_date) AS [Month Number], 
     DATEPART(YEAR, creation_date) AS Year, 
     CAST(COUNT(*) AS FLOAT)/DATEDIFF(DAY, DATEADD(DAY, 1 - DAY(creation_date), creation_date), DATEADD(MONTH, 1, DATEADD(DAY, 1 - DAY(creation_date), creation_date))) AS ADP 
    FROM patients 
    GROUP BY DATENAME(MONTH, creation_date), 
     DATEPART(month, creation_date), 
     DATEPART(year, creation_date), 
     DATEDIFF(DAY, DATEADD(DAY, 1 - DAY(creation_date), creation_date), DATEADD(MONTH, 1, DATEADD(DAY, 1 - DAY(creation_date), creation_date))) 
    ORDER BY 3, 2 

Здесь он находится на SQLFiddle.

+0

Похоже, что это возвращает новых новых пациентов за каждый день, я бы хотел ОБЩЕЕ количество пациентов в день. – etm124

+0

Ваше первоначальное сообщение запрашивает _среднее ежедневное количество активных пациентов в месяц, которое вам должно дать - количество ежедневных пациентов в день за соответствующий месяц. –

+0

Не нужно группироваться по конструкции 'датифф'; вы можете просто использовать 'day (max (create_date))', так как все даты будут в том же месяце – Andomar

0

Это, кажется, ответ на ваш вопрос:

SELECT YEAR(thedate), MONTH(thedate), datename(month, thedate()), AVG(cnt*1.0) 
FROM (SELECT cast(creation_date as date) as thedate, count(*) as cnt 
     FROM patients 
     where active = 1 
     GROUP BY cast(creation_date as date) 
    ) t 
group by YEAR(thedate), MONTH(thedate), datename(month, thedate()) 
order by 1, 2; 

Это versino предполагает, что у вас есть данные на каждый день. Более пристальный взгляд на ваших данных показывает, что это не так, то эта версия сближается:

SELECT YEAR(thedate), MONTH(thedate), datename(month, thedate()), 
     sum(cnt*1.0)/count(*) as average 
FROM (SELECT cast(creation_date as date) as thedate, count(*) as cnt 
     FROM patients 
     where active = 1 
     GROUP BY cast(creation_date as date) 
    ) t 
group by YEAR(thedate), MONTH(thedate), datename(month, thedate()) 
order by 1, 2; 

Если вы действительно хотите, чтобы считать дни, и вы не имеете таблицу календаря, то SQL получает немного больше сложно.Далее предполагается, что у вас есть по крайней мере одну запись в каждом месяце (для создания таблицы Mons):

with pc as (
     SELECT cast(creation_date as date) as thedate, count(*) as cnt, 
      MIN(YEAR(creation_date)*12+MONTH(creation_date)) as monnum 
     FROM patients 
     where active = 1 
     GROUP BY cast(creation_date as date) 
    ), 
    mons as (
     select distinct YEAR(creation_date) as yr, MONTH(creation_date) as mon, DATENAME(month, creation_date) as monname, 
      CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(creation_date)-1),creation_date),101) as FirstDay, 
      CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,creation_date))),DATEADD(mm,1,creation_date)),101) as LastDay, 
      YEAR(creation_date)*12+MONTH(creation_date) as monnum 
     from patients 
    ) 
SELECT mons.yr, mons.mon, mons.monname, 
     (SUM(datediff(day, (case when pc.thedate < mons.FirstDay then mons.FirstDay else pc.thedate end), 
        (case when pc.thedate > mons.LastDay then mons.LastDay else pc.thedate end) 
        ) * pc.cnt 
      )/
     SUM(datediff(day, (case when pc.thedate < mons.FirstDay then mons.FirstDay else pc.thedate end), 
        (case when pc.thedate > mons.LastDay then mons.LastDay else pc.thedate end) 
        ) 
      ) 
     ) as avgday 
FROM mons join 
    (select pc.*, 
      (select top 1 pc2.monnum from pc pc2 where pc2.thedate > pc.thedate order by thedate 
      ) as nextmonnum 
     from pc 
    ) pc 
    on mons.monnum between pc.monnum and pc.nextmonnum 
group by mons.yr, mons.mon, mons.monname 
order by 1, 2; 

В основном, это создает начала и дату окончания для каждой записи пациента - период времени, когда счетчик будет постоянным, потому что новые пациенты не приходят. Затем он совершает кучу ворчания и арифметики, чтобы подсчитать количество дней пациента в месяц и количество дней в каждом месяце. Я не тестировал это, поэтому он восприимчив к ошибкам синтаксиса и ошибкам «один за другим». Я приведу его здесь в качестве примера.

Однако, что вы считаете, это число активных пациентов в последние месяцы, а не количество активных пациентов в эти месяцы.

Чтобы получить количество активных пациентов, вам необходимо указать дату «активации» и «деактивации».

0

попробовать этот код:

select to_char(creation_date, 'mon') Month, 
      to_char(creation_date, 'mm') Month_Number, 
      to_char(creation_date, 'yyyy') Year, 
      count(Patient_ID)/to_char(last_day(to_date(to_char(creation_date, 'yyyymm')),'DD') Avg_per_Month 
     from patients 
     where active=1 
    group by to_char(creation_date, 'mon'), 
      to_char(creation_date, 'mm'), 
      to_char(creation_date, 'yyyy'), 
      to_char(last_day(to_date(to_char(creation_date, 'yyyymm')),'DD') 

следующий код возвращает количество дней, в течение данного месяца:

to_char(last_day(to_date(to_char(creation_date, 'yyyymm')),'DD') 
+0

Я боюсь, что вопрос для SQL Server, а не Oracle :) – Andomar

+0

это происходит, когда я делаю слишком много вещей одновременно :) – mucio

0

Вы можете определить, в первый день месяца с:

dateadd(month, datediff(month, 0, @date), 0) 

И в первый день следующего месяца с:

dateadd(month, 1 + datediff(month, 0, @date), 0) 

Число дней в месяце - разница между ними. Применительно к вашему вопросу это становится:

select datepart(year, creation_date) as Year 
,  datepart(month, creation_date) as Month 
,  1.0 * count(*)/datediff(day, 
      dateadd(month, datediff(month, 0, min(creation_date)), 0), 
      dateadd(month, 1+datediff(month, 0, min(creation_date)), 0)) 
      as DailyAverage 
from Table1 
group by 
     datepart(year, creation_date) 
,  datepart(month, creation_date) 

Example at SQL Fiddle.

Смежные вопросы