-1

我重新发布这个问题,因为我还没有找到最佳解决方案。

我正在设计一个捕获患者血液样本信息的表格。它有一个患者 ID 和一个采集日期 - 采集患者血液样本的日期。

该表具有三个附加列 - episode_number、episode_start_date 和 episode_end_date。一集只不过是一个 30 天的时间窗口。在 30 天内收集的任何样本都属于同一集。例如,患者在 2013 年 1 月 1 日提交了他的第一份血样,在 2013 年 1 月 -19 日提交了下一份血样。由于两个收集日期都在同一个“30 天窗口”内,它们属于同一个 episode_number(第 1 集)。本集的开始日期将是第一个收集日期(2013 年 1 月 1 日),结束日期将是开始日期 + 30 天(2013 年 1 月 30 日)。在此日期范围内收集的任何数量的患者血液样本都属于 episode_number = 1。

假设同一位患者在 2013 年 2 月 4 日提交了另一份血液样本。由于此收集日期超出了 episode_number = 1 的 30 天窗口,因此它将属于新的 episode_number(第 2 集)。本集的开始日期为 2013 年 2 月 4 日,结束日期为 + 30 天,即 2013 年 3 月 2 日。

假设表格如下例所示:

------------------------------------------------------------------------------------------
Patient ID | Collection_Date | Episode_Number     |Episode_Start_Date | Episode_End_Date |
1          | 2013-01-01      |                    |                   |                  |
1          | 2013-01-01      |                    |                   |                  |   
1          | 2013-01-05      |                    |                   |                  |
1          | 2013-02-04      |                    |                   |                  |  
1          | 2013-02-06      |                    |                   |                  |   
1          | 2013-05-01      |                    |                   |                  |
1          | 2013-08-01      |                    |                   |                  |
-------------------------------------------------------------------------------------------

我需要一个查询,该查询将根据我上面文本中描述的逻辑填充 episode_number、episode_start_Date 和 episode_end_date。查询结果应填充下面提到的表值:

----------------------------------------------------------------------------------------
Patient ID | Collection_Date |Episode_number     |Episode_Start_Date| Episode_End_Date |
1          | 2013-01-01      |1                  |2013-01-01        | 2013-01-30       |
1          | 2013-01-01      |1                  |2013-01-01        | 2013-01-30       |
1          | 2013-01-05      |1                  |2013-01-05        | 2013-01-30       |
1          | 2013-02-04      |2                  |2013-02-04        | 2013-03-02       |
1          | 2013-02-06      |2                  |2013-02-04        | 2013-02-04       |
1          | 2013-05-01      |3                  |2013-05-01        | 2013-05-30       |
1          | 2013-08-01      |4                  |2013-08-01        | 2013-08-30       |
----------------------------------------------------------------------------------------

要记住的事情:

  1. 一集 = 30 天时间窗口
  2. 第一集开始日期 = 首次收集日期
  3. 第一集结束日期 = 第一集开始日期 + 30 天
  4. 在同一 30 天窗口内收集的所有样本都属于同一集。
  5. 如果任何样本的采集日期大于上一次采集的剧集结束日期,则属于新剧集,其中剧集开始日期=采集日期;剧集结束日期 = 开始日期 + 30 天。

我希望我的问题很清楚。我的表有超过 300 万条记录,因此我不仅需要一个有效的解决方案,还需要一个具有最佳性能的解决方案。任何帮助/建议将不胜感激。

提前致谢!

阿什什

4

2 回答 2

1

(我将暂时搁置这一点,但这并没有实现collection_date 必须在episode_start_date 和episode_end_date 之间的未表达约束。)

让我们看一下这张表的一部分。

Patient ID | Collection_Date |Episode_number     |Episode_Start_Date| Episode_End_Date |
1          | 2013-01-01      |1                  |2013-01-01        | 2013-01-30       |
1          | 2013-01-01      |1                  |2013-01-01        | 2013-01-30       |

重复的行。该表没有键。

这两个相同的行应该告诉我们什么不同的事情?

这种桌子需要真正的钥匙——而不仅仅是另一个 ID 号。

用于存储有关患者发作信息的表格可能需要看起来像这样。

create table patient_episodes (
  patient_id integer not null,
  episode_number integer not null
    check (episode_number > 0),
  primary key (patient_id, episode_number),
  foreign key (patient_id, episode_number) 
    references samples (patient_id, episode_number),

  episode_start_date date not null,
  episode_end_date date not null,
  check (episode_end_date = episode_start_date + interval '30 days')
);

您最初需要声明从 patient_episodes 到 samples 的外键引用,因为 patient_episodes 是空的。我不清楚在两个表都填充并稳定后是否应该保持这种状态。(可能不是,但我不想猜测。)

您的样本表在结构上仍然存在问题,因为它没有键。你如何解决这个问题将对 patient_episodes 表的结构产生一些影响。

于 2013-06-10T18:10:17.330 回答
1

If you have the option to change your table design, I recommend Mike Sherill's answer.

If you don't have the option, the following should work, but the performance may be poor:

with cte as
(select [Patient ID],
        min(Collection_Date) Collection_Date,
        1 Episode_Number,
        min(Collection_Date) Episode_Start_Date,
        Dateadd(d,29,min(Collection_Date)) Episode_End_Date
 from sampleTable
 group by [Patient ID]
 union all
 select s.[Patient ID],
        s.Collection_Date Collection_Date,
        c.Episode_Number+1 Episode_Number,
        s.Collection_Date Episode_Start_Date,
        Dateadd(d,29,s.Collection_Date) Episode_End_Date
 from cte c
 join sampleTable s 
   on c.[Patient ID] = s.[Patient ID] and 
      c.Episode_End_Date < s.Collection_Date and
      not exists (select null
                  from sampleTable i
                  where c.[Patient ID] = i.[Patient ID] and
                        c.Episode_End_Date < i.Collection_Date and
                        i.Collection_Date < s.Collection_Date)
 )
select cte.[Patient ID],
       st.Collection_Date,
       cte.Episode_Number,
       cte.Episode_Start_Date,
       cte.Episode_End_Date
from cte
join sampleTable st 
  on st.[Patient ID] = cte.[Patient ID] and 
     st.Collection_Date between cte.Episode_Start_Date and cte.Episode_End_Date 
option (maxrecursion 0)

SQLFiddle here.

于 2013-06-10T18:16:20.423 回答