查询月平均人口

时间:2013-04-03 12:45:32

标签: sql sql-server tsql

我有一张名为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

编辑:似乎提交的答案给了我新患者的平均每日计数,这是一个较小的数字。我需要TOTAL人口的平均每日计数。

编辑:我了解到患者状态跟踪会在名为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

5 个答案:

答案 0 :(得分: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

更新

创建一个跟踪患者状态变化的表格:

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

答案 1 :(得分: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

答案 2 :(得分: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;

这个版本假设您每天都有数据。仔细看看你的数据表明情况并非如此,这个版本接近:

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;

基本上,它会为每个患者记录创建一个开始日期和结束日期 - 计数将保持不变的时间段,因为没有新患者进入。然后它会进行一系列摆弄和算术来计算每个患者的数量。每月患者天数和每月的天数。我没有对此进行测试,因此很容易出现语法错误和逐个错误。我在这里举一个例子。

然而,您所依据的是过去几个月当前活跃的患者数量,而不是那几个月活跃患者的数量。

要获得活跃患者的数量,您需要提供“激活”和“停用”的日期。

答案 3 :(得分: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')

答案 4 :(得分: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.