【问题标题】:Querying the monthly average population查询月平均人口
【发布时间】:2013-03-25 03:23:25
【问题描述】:

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

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

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

【问题讨论】:

  • 此表中日期字段的名称是什么?您需要使用所有天数(包括节假日)来获得平均值吗?
  • 是否有与每位患者相关的时间表,详细说明他或她住院的开始和结束日期?如果没有,并且您只有布尔 active 列而没有历史数据,那么您就不走运了。
  • 请发布一些示例数据和所需的输出
  • @shahkalpesh,有一个creation_date 字段。
  • @Quassnoi,我已经添加了示例数据和所需的输出。

标签: sql sql-server tsql


【解决方案1】:
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 和Example at SQL Fiddle, @etm124:这显示了每天患者总数的月平均值
  • @etm124:请详细说明。您在示例中提供的所需输出是否应该源自您提供的数据?在您的输出中,输入中似乎缺少 3 月、4 月、5 月等的条目。
  • @Quassnoi,所需的输出不是来自我提供的数据。例如,如果我运行 select count(*) from patients where active=1,我会返回 213 值。这是今天 2013/04/03 系统中的活跃患者总数。如果我明天运行这个查询,它可能会增加或减少一些病人,因为一些病人离开,或者我们接纳新病人。平均而言,该设施每天有约 200 名活跃患者,这反映在我想要的输出中。我从您的查询中得到的输出看起来只是每天入院的新患者。 (1/2)。
  • @etm124:请发布一些示例数据和输出,这些数据来自它们。
【解决方案2】:

可能是我写过的最不优雅的查询,但它确实给了你想要的结果,即每月每天的平均患者数:

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

【讨论】:

  • 看起来这是每天返回的平均新患者数,我想要每天的患者总数。
  • 您的原始帖子要求提供每月活跃患者的平均每日人数,这应该给您 - 相关月份每天的每日患者人数。
  • 无需对datediff 结构进行分组;你可以直接使用day(max(creation_date)),因为所有日期都在同一个月份
  • @Andomar 对此表示感谢 - 但我认为查询没有提供 etm124 要求的数据。我可能误解了这个要求。
  • @MartinParkin,我在上面的 Quassnoi 的回答中进行了详细说明。谢谢你。=你。
【解决方案3】:

试试这个代码:

    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')

【讨论】:

  • 恐怕问题是针对 SQL Server,而不是 Oracle :)
【解决方案4】:

您可以通过以下方式确定一个月的第一天:

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.

【讨论】:

    【解决方案5】:

    这似乎回答了你的问题:

    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;
    

    基本上,它会为每个病人记录创建一个开始和结束日期——由于没有新病人进来,计数将保持不变的时间段。然后它会做一堆摆弄和算术来计算病人的数量每个月的患者天数和每个月的天数。我没有对此进行测试,因此它很容易受到语法错误和非一错误的影响。我这里举个例子。

    但是,您计算的是过去几个月当前活跃的患者数量,而不是那些月份的活跃患者数量。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-29
      • 1970-01-01
      相关资源
      最近更新 更多