计算按日历月分组的住宿时长

时间:2013-01-21 04:43:11

标签: sql sql-server-2008 group-by aggregation

这是我正在处理的事情: 我给了一个表(访问),其中包含以下带有示例数据的模式。

访问

VisitNo   Location  AdmissionDate  DischargeDate LengthOfStay
1         A         2012-04-28     2012-05-30     32
2         A         2012-04-20     2013-05-20     90
3         B         2012-04-01     2012-05-01     30
4         B         2012-05-01     2012-05-03     2
.....................................................

要求是返回的数据集采用以下结构。按日历月(YYYYMM)计算每个位置的总长度。

CalendarMonth  TotalLengthOfStayEachMonth(AdmissionToDate)  Location
201204          xxx                                          x
201205          yyyy                                         y
201206          zzzz                                         z
.........      ...............

TotalLengthOfStayEachMonth的计算有点棘手。自AdmissionDate(admissionToDate)以来的日期计算,而不是每月30天。 例如,VISIT表中的第一条记录有;

  • 201204年入住2天
  • 2 + 30 = 201205年的32天等等......

提前感谢您的建议......仅供参考,我们有数百个地点,数千个视频和5年的数据。

2 个答案:

答案 0 :(得分:1)

虽然我希望看到你想要的输出,但是这样的事情可能会帮助你开始:

SELECT 
    CONVERT(char(6), AdmissionDate, 112)  as CalendarMonth  ,
    SUM(DAY(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,AdmissionDate)+1,0)))-DAY(AdmissionDate)) as TotalLengthOfStayEachMonth,
    Location
FROM VISIT
GROUP BY CONVERT(char(6), AdmissionDate, 112), Location

添加位置可能会给你重复的CalendarMonths,但我认为没问题。

编辑 -

由于我意识到,如果DischargeDate比AdmissionDate超过1个月,我会意识到有几个月会错过日期,并且想出了这个 - 它使用了spt_values表:

select 
   CONVERT(char(6), AdmissionDate+v.number, 112)  as CalendarMonth,
   COUNT(*) Days,
   Location
from Visit t
inner join master..spt_values v
  on v.type='P' and v.number <= DATEDIFF(d, AdmissionDate, DischargeDate)
group by Location, CONVERT(char(6), AdmissionDate+v.number, 112)
order by CONVERT(char(6), AdmissionDate+v.number, 112), Location

这是SQL Fiddle

- 另一个编辑

在查看另一个答案时,并没有真正了解您的情况(需要所需的输出),如果您的第一个记录的所需输出是4月2天,那么您只需要添加和v.number&lt;&gt; 0到上面的连接 - 很简单,只是不确定你要求的是什么。这应该是最简单的解决方案:

select 
   CONVERT(char(6), AdmissionDate+v.number, 112)  as CalendarMonth,
   COUNT(*) Days,
   Location
from Visit t
inner join master..spt_values v
  on v.type='P' and v.number <= DATEDIFF(d, AdmissionDate, DischargeDate) and v.number <> 0
group by Location, CONVERT(char(6), AdmissionDate+v.number, 112)
order by CONVERT(char(6), AdmissionDate+v.number, 112), Location

更多fiddle

希望其中一些有用。

祝你好运。

答案 1 :(得分:1)

这样做(SQLFiddle):

with everyday (VisitNo, Location, dateOfStay)
AS (
  SELECT VisitNo, Location, dateadd(dd, 1, AdmissionDate)
  FROM VISIT
  UNION ALL
  SELECT e.VisitNo, e.Location, dateadd(dd, 1, e.dateOfStay)
  FROM VISIT v INNER JOIN everyday e ON v.VisitNo = e.VisitNo and
    e.dateofStay < v.DischargeDate
  )
SELECT CONVERT(VARCHAR(7), dateofstay, 121), VisitNo, Location, count(*) 
FROM everyday 
GROUP BY CONVERT(VARCHAR(7), dateofstay, 121), VisitNo, Location
ORDER BY 2,1
OPTION (MAXRECURSION 500);

如果您只想按位置分组,请使用以下内容:

with everyday (VisitNo, Location, dateOfStay)
AS (
  SELECT VisitNo, Location, dateadd(dd, 1, AdmissionDate)
  FROM VISIT
  UNION ALL
  SELECT e.VisitNo, e.Location, dateadd(dd, 1, e.dateOfStay)
  FROM VISIT v INNER JOIN everyday e ON v.VisitNo = e.VisitNo and
    e.dateofStay < v.DischargeDate
  )
SELECT CONVERT(VARCHAR(7), dateofstay, 121), Location, count(*) 
FROM everyday 
GROUP BY CONVERT(VARCHAR(7), dateofstay, 121), Location
ORDER BY 2,1
OPTION (MAXRECURSION 500);