创建由多列和连续日期分区的序列

时间:2015-07-23 07:19:49

标签: sql sql-server sql-server-2005

我试图弄清楚如何创建一个由多列分区的序列,其中序列必须在另一个(基于日期的)列不连续时重置。

问题:医院ADT(入院/出院/转院)事件发生在特定时间点,但我们希望将这些事件转变为具有持续时间(时间跨度)的活动,即我们有开始日期,但不要# 39; t具有结束日期,该日期基于下一个适当的ADT事件。我们已在代码中完成此操作,但也希望在SQL中执行此操作以提高性能。例如找到在ICU中度过超过48小时的患者。

我们希望记录六个不同级别的站点位置:设施,护理点,建筑物,楼层,房间和床。

示例:

Stream  Event  Started           Facility    PointOfCare  ...
1       1      2015-01-01 09:05  Hospital-A  ICU           
1       2      2015-01-02 13:10  Hospital-A  WARD-1
2       3      2015-02-10 12:00  Hospital-A  ICU           
2       4      2015-02-11 12:00  Hospital-A  ICU
2       5      2015-02-12 04:30  Hospital-A  WARD-2

因此,对于每个事件,我们想知道它们在每个特定站点位置的时间长度。每个流中最后一个活动的结束日期为空(仍为住院病人)或病人出院的日期。

这是我目前的解决方案:

-- Create a sequence for each site location
INSERT INTO ADT_Activity_Sequence
SELECT 
  [Stream], 
  [Event],
  [Started],
  [Facility], 
  ROW_NUMBER() OVER (PARTITION BY [Stream], 
    ISNULL([Facility], [Event]) 
    ORDER BY [Started]) AS [FacilitySequence], 
  [PointOfCare], 
  ROW_NUMBER() OVER (PARTITION BY [Stream], 
    ISNULL([Facility], [Event]), 
    ISNULL([PointOfCare], [Event]) 
    ORDER BY [Started]) AS [PointOfCareSequence]
  -- and so on for all site locations
FROM ADT_Event
INNER JOIN ADT_Stream ON ADT_Event.Stream = Stream.Id

示例:

Stream  Event  Started           Facility    FacilitySequence  PointOfCare  PointOfCareSequence ...
1       1      2015-01-01 09:05  Hospital-A  1                 ICU          1 
1       2      2015-01-02 13:10  Hospital-A  2                 WARD-1       1
2       3      2015-02-10 12:00  Hospital-A  1                 ICU          1
2       4      2015-02-11 12:00  Hospital-A  2                 ICU          2
2       5      2015-02-12 04:30  Hospital-A  3                 WARD-2       1

然后从序列创建持续时间:

INSERT INTO ADT_Activity_Duration
SELECT 
    [Stream], 
    [Event],
    [Started],
    [Facility], 
    [Sequence].[FacilitySequence],
    (
        -- Find most recent activity which is the first in current sequence
        SELECT TOP 1 [FacilitySequence].[Started] 
        FROM [ADT_Activity_Sequence] [FacilitySequence]
        WHERE [FacilitySequence].[Stream] = [Event].[Stream] AND [FacilitySequence].[FacilitySequence] = 1 AND [FacilitySequence].[Started] <= [Event].[Started]
        ORDER BY [FacilitySequence].[Started] DESC
    ) AS [FacilityStarted],
    (
        -- Find first activity in next sequence as this activities end date
        -- Last activity returns null, so activity uses stream end date if set
        ISNULL((                
            SELECT TOP 1 [FacilitySequence].[Started]
            FROM [ADT_Activity_Sequence] [FacilitySequence]
            WHERE [FacilitySequence].[Stream] = [Event].[Stream] AND [FacilitySequence].[FacilitySequence] = 1 AND [FacilitySequence].[Started] > [Event].[Started]
            ORDER BY [FacilitySequence].[Started]), [Stream].[Ended])
    ) AS [FacilityEnded],
    [PointOfCare], 
    [Sequence].[PointOfCareSequence],
    (
        SELECT TOP 1 [PointOfCareSequence].[Started] 
        FROM [ADT_Activity_Sequence] [PointOfCareSequence]
        WHERE [PointOfCareSequence].[Stream] = [Event].[Stream] AND [PointOfCareSequence].[PointOfCareSequence] = 1 AND [PointOfCareSequence].[Started] <= [Event].[Started]
        ORDER BY [PointOfCareSequence].[Started] DESC
    ) AS [PointOfCareStarted],
    (
        ISNULL((
            SELECT TOP 1 [PointOfCareSequence].[Started]
            FROM [ADT_Activity_Sequence] [PointOfCareSequence]
            WHERE [PointOfCareSequence].[Stream] = [Event].[Stream] AND [PointOfCareSequence].[PointOfCareSequence] = 1 AND [PointOfCareSequence].[Started] > [Event].[Started]
            ORDER BY [PointOfCareSequence].[Started]), [Stream].[Ended])
    ) AS [PointOfCareEnded]
    -- and so on for all site locations
FROM ADT_Event AS [Event]
INNER JOIN [ADT_Stream] AS [Stream] ON [Event].[Stream] = [Stream].[Id]
INNER JOIN [ADT_Activity_Sequence] [Sequence] ON [Event].[Id] = [Sequence].[Event]

示例:

Stream  Event  Started           Facility    FacilitySequence  FacilityStarted  FacilityEnded     PointOfCare  PointOfCareSequence  PointOfCareStarted  PointOfCareEnded  ...
1       1      2015-01-01 09:05  Hospital-A  1                 2015-01-01 09:05 2015-01-03 12:00  ICU          1                    2015-01-01 09:05    2015-01-02 13:10  
1       2      2015-01-02 13:10  Hospital-A  2                 2015-01-01 09:05 2015-01-03 12:00  WARD-1       1                    2015-01-02 13:10    2015-01-03 12:00
2       3      2015-02-10 12:00  Hospital-A  1                 2015-02-10 12:00 <NULL>            ICU          1                    2015-02-10 12:00    2015-02-12 04:30
2       4      2015-02-11 12:00  Hospital-A  2                 2015-02-10 12:00 <NULL>            ICU          2                    2015-02-10 12:00    2015-02-12 04:30
2       5      2015-02-12 04:30  Hospital-A  3                 2015-02-10 12:00 <NULL>            WARD-2       1                    2015-02-12 04:30    <NULL>

我的问题是当连续的日期序列被破坏时,当患者从任何站点位置转移,然后再次转回时,所有这些都发生在同一个流中:

Stream  Event  Started           Facility    PointOfCare  ...
3       1      2015-03-01 09:05  Hospital-A  ICU           
3       2      2015-03-02 13:10  Hospital-A  WARD-1
3       3      2015-03-02 10:00  Hospital-A  ICU           

示例:

Stream  Event  Started           Facility    FacilitySequence  PointOfCare  PointOfCareSequence ...
3       1      2015-03-01 09:05  Hospital-A  1                 ICU          1 
3       2      2015-03-02 13:10  Hospital-A  2                 WARD-1       1
3       3      2015-03-02 10:00  Hospital-A  3                 ICU          2

注意事件#3的护理点序列为2,这是不正确的,由于事件#2位于不同的位置,需要将其重置为1.

我已经在圈子里走了一段时间了:)所以任何帮助表示赞赏,谢谢!

1 个答案:

答案 0 :(得分:5)

如果我理解你的问题,你需要连续ROW_NUMBER()。您可以使用流ROW_NUMBER()和单个序列之间的行数差异来生成一个组,在该组上可以为设施和护理点订购行号。

由于这些不是使用FacilityPointofCare直接分组,而是基于它们的顺序,如果患者再次切换回相同的设施或护理点,则重置序列。

使用类似的东西。 SQL Fiddle

;WITH CTE as 
(
    SELECT *,
    ROW_NUMBER() OVER(PARTITION BY Stream ORDER BY [Started]) as StreamSequence,
    ROW_NUMBER() OVER(PARTITION BY Stream ORDER BY [Started]) - ROW_NUMBER() OVER(PARTITION BY Facility ORDER BY [Started]) as FacilityGroup,
    ROW_NUMBER() OVER(PARTITION BY Stream ORDER BY [Started]) - ROW_NUMBER() OVER(PARTITION BY PointOfCare ORDER BY [Started]) as PointOfCareGroup
    FROM Stream
)
SELECT 
Stream, Event, Started, Facility, PointOfCare, StreamSequence,
ROW_NUMBER() OVER(PARTITION BY Stream,FacilityGroup ORDER BY [Started]) as FacilitySequence,
ROW_NUMBER() OVER(PARTITION BY Stream,PointOfCareGroup ORDER BY [Started]) as PointOfCareSequence
FROM CTE
ORDER BY Event;

您可以根据需要根据这些顺序生成日期范围。

<强>输出

| Stream | Event |                    Started |   Facility | PointOfCare | StreamSequence | FacilitySequence | PointOfCareSequence |
|--------|-------|----------------------------|------------|-------------|----------------|------------------|---------------------|
|      1 |     1 |  January, 01 2015 09:05:00 | Hospital-A |         ICU |              1 |                1 |                   1 |
|      1 |     2 |  January, 02 2015 13:10:00 | Hospital-A |      WARD-1 |              2 |                2 |                   1 |
|      2 |     3 | February, 10 2015 12:00:00 | Hospital-A |         ICU |              1 |                1 |                   1 |
|      2 |     4 | February, 11 2015 12:00:00 | Hospital-A |         ICU |              2 |                2 |                   2 |
|      2 |     5 | February, 12 2015 04:30:00 | Hospital-A |      WARD-2 |              3 |                3 |                   1 |
|      2 |     6 | February, 12 2015 05:30:00 | Hospital-A |         ICU |              4 |                4 |                   1 |