Postgres 9.x加入重叠日期和;时间范围

时间:2013-06-27 00:40:04

标签: sql postgresql postgresql-9.1 postgresql-9.2

我有几个包含TIMESTAMP WITH TIME ZONE列的表以及一些其他信息。我需要能够加入这些表,以便每行的所有信息在给定的日期和时间窗口(在所需的结果集中标记为beginend)是“有效的”。

我目前的做法:

选项#1

  1. 创建一个唯一的时间列表。
  2. 将每个时间点转换为唯一列表(“有效”时间窗口)和每个原始表的时间窗口。 [LEAD(...) OVER (...)]
  3. 将原始表格加入唯一的时间列表。
  4. 选项#2

    1. 将每个时间点(TIMESTAMP WITH TIME ZONE)转换为每个表的时间窗口。 [LEAD(...) OVER (...)]
    2. 加入窗口重叠的表格。
    3. 返回GREATEST(foo.start, bar.start)&从每个窗口LEAST(foo.stop, bar.stop)找到真正的“有效”窗口。
    4. 例如:

      表:foo

        fooid  |  description  |       datetime
      ---------|---------------|-----------------------
          1    |   Varsion 1   |  2010-01-01 00:00:00  
          2    |   Varsion 2   |  2010-07-01 00:00:00 
      

      表:栏

        barid  |  fooid  |  description  |       datetime
      ---------|---------|---------------|-----------------------
          1    |    1    |   Varsion A   |  2010-01-01 00:00:00
          2    |    1    |   Varsion B   |  2010-02-01 00:00:00
          3    |    1    |   Varsion C   |  2010-03-01 00:00:00
          4    |    1    |   Varsion D   |  2010-04-01 00:00:00
          5    |    1    |   Varsion E   |  2010-05-01 00:00:00
          6    |    1    |   Varsion F   |  2010-06-01 00:00:00
          7    |    2    |   Varsion A   |  2010-07-01 00:00:00
          8    |    2    |   Varsion B   |  2010-08-01 00:00:00
          9    |    2    |   Varsion C   |  2010-09-01 00:00:00
          10   |    2    |   Varsion D   |  2010-10-01 00:00:00
          11   |    2    |   Varsion E   |  2010-11-01 00:00:00
          12   |    2    |   Varsion F   |  2010-12-01 00:00:00
      

      简化所需结果

              begin          |          end          |  fooid  |   foo_desc    |       foostart        |        foostop        |  barid  |   bar_desc   |       foostart        |        foostop        
      -----------------------|-----------------------|---------|---------------|-----------------------|-----------------------|---------|--------------|-----------------------|-----------------------
               ...           |         ...           |   ...   |      ...      |         ...           |         ...           |   ...   |     ...      |         ...           |         ...           
        2010-05-01 00:00:00  |  2010-06-01 00:00:00  |    1    |   Varsion 1   |  2010-01-01 00:00:00  |  2010-07-01 00:00:00  |    5    |  Varsion E   |  2010-05-01 00:00:00  |  2010-06-01 00:00:00
        2010-06-01 00:00:00  |  2010-07-01 00:00:00  |    1    |   Varsion 1   |  2010-01-01 00:00:00  |  2010-07-01 00:00:00  |    6    |  Varsion F   |  2010-06-01 00:00:00  |        infinity
        2010-07-01 00:00:00  |  2010-08-01 00:00:00  |    2    |   Varsion 2   |  2010-07-01 00:00:00  |       infinity        |    7    |  Varsion A   |  2010-07-01 00:00:00  |  2010-08-01 00:00:00
        2010-08-01 00:00:00  |  2010-09-01 00:00:00  |    2    |   Varsion 2   |  2010-07-01 00:00:00  |       infinity        |    8    |  Varsion B   |  2010-08-01 00:00:00  |  2010-09-01 00:00:00
               ...           |         ...           |   ...   |      ...      |         ...           |         ...           |   ...   |     ...      |         ...           |         ...           
      

      我的问题:

      实现这一目标的最佳方法是什么?我已经创建了一个显示两种不同解决方案的fiddle,我希望听到关于每种解决方案的想法以及不在小提琴中的可能解决方案。

      更新#1:

      在该示例中,只需要连接两个表...但是,在某些情况下,我可能需要连接多个表3,4或更多。

      更新#2:

      使用选项#1 ,我的问题是,当我有大型结果集时,我发现初始子查询可能很大而postgres不能使用索引。这会导致大的性能损失。另一方面,我发现它是最准确的,因为我可以LEFT OUTER JOIN反对它并获得相关的NULL数据。

      使用选项#2 ,查询计划程序可以使用TIMESTAMP WITH TIME ZONE列上的索引;但是,在FROM子句中加入两个以上的表变得更加复杂。我可以将(table1.start, table1.stop) OVERLAPS (table2.start, table2.stop)移到WHERE子句中,但随后我会丢失相关的NULL数据。

      所有这些让我想知道是否有更好的方法......

1 个答案:

答案 0 :(得分:1)

  • 使用窗口函数lead()和滞后()的默认值,而不是coalesce()

lead(datetime, 1, 'infinity') OVER (ORDER BY fooid, datetime) AS stop

而不是

COALESCE(LEAD(datetime) OVER (ORDER BY fooid, datetime), 'infinity'::TIMESTAMP) AS stop
  • 当你PARTITION BY fooid ORDER BY fooid
  • 时,它毫无意义

PARTITION BY fooid ORDER BY datetime

而不是:

(PARTITION BY fooid ORDER BY fooid, datetime)
  • 您(或某些工具)会对每个标识符进行双重引用,即使它们都是合法的,也没有引号。使查询更加难以阅读。散发噪音。

除此之外:您的问题对于stackoverflow来说太宽泛,而且说明很难理解。

考虑:https://codereview.stackexchange.com/