【问题标题】:SQL JOIN table with a date range具有日期范围的 SQL JOIN 表
【发布时间】:2012-10-15 15:54:44
【问题描述】:

假设,我有一个包含 C 列和 N 行的表格。我想生成一个选择语句,表示该表的“连接”,其数据范围包括 M 天。生成的结果集应该有 C+1 列(最后一个是日期)和 NXM 行。

简单的例子来澄清事情: 给定下表A

select * from A;
avalue  |
--------+
"a"     |

日期范围是 2012 年 10 月 10 日到 12 日,我想要以下结果集:

avalue  |  date
--------+-------
"a"     | 2012-10-10
"a"     | 2012-10-11
"a"     | 2012-10-12

(这是我最终计算任何一天的库存水平所需的垫脚石,给定起始值和增量)

【问题讨论】:

  • 你对 CNM 的使用让我头晕目眩。您要动态查询吗?
  • 但不管 RDBMS,我建议使用日历表(例如,如果您的业务模型需要,您也可以将其用于工作日管理)和简单的笛卡尔积将日历表放到表中一个
  • 谢谢,我试图避免使用日历表。我正在使用 PostgreSQL 顺便说一句。
  • @foampile:PostgreSQL 很少需要日历表,请改用generate_series
  • 似乎是合法的功能。虽然我不是 PostgreSQL 用户,但我会检查一下

标签: sql postgresql date


【解决方案1】:

Postgres 方法很简单:CROSS JOIN 到函数 generate_series()

SELECT t.*, g.day::date
FROM   tbl t
CROSS  JOIN generate_series(timestamp '2012-10-10'
                          , timestamp '2012-10-12'
                          , interval  '1 day') AS g(day);

准确地产生所要求的输出。

generate_series() 是一个生成派生表的集合返回函数(又名“表函数”)。有几个重载的变体,这就是我选择timestamp 输入的原因:

对于任意日期,将generate_series() 替换为VALUES 表达式。无需持久化表:

SELECT *
FROM   tbl t
CROSS  JOIN (
   VALUES
     (date '2012-08-13')  -- explicit type in 1st row
   , ('2012-09-05')
   , ('2012-10-10')
   ) g(day);

【讨论】:

    【解决方案2】:

    如果日期表中的日期比您感兴趣的要多,那么就这样做

    select a.avalue, b.date from a, b where b.date between '2012-10-10' and '2012-10-12'
    

    否则,如果日期表仅包含您感兴趣的日期,则笛卡尔连接将完成此操作:

    select * from a,b;
    

    【讨论】:

    • 实际上,如果可能的话,我会尽量避免定义日期表。没有特别的原因,求知欲。
    【解决方案3】:
    declare
        @Date1 datetime = '20121010',
        @Date2 datetime = '20121012';
    
    with Dates
    as
    (
        select @Date1 as [Date]
        union all
        select dateadd(dd, 1, D.[Date]) as [Date]
        from Dates as D
        where D.[Date] <= DATEADD(dd, -1, @Date2)
    )
    select 
        A.value, D.[Date]
    from Dates as D
        cross join A
    

    【讨论】:

      【解决方案4】:

      对于 MySQL

      架构/数据:

      CREATE TABLE someTable
      (
          someCol varchar(8) not null
      );
      
      INSERT INTO someTable VALUES ('a');
      
      CREATE TABLE calendar
      (
          calDate datetime not null,
          isBus bit
      );
      
      ALTER TABLE calendar
      ADD CONSTRAINT PK_calendar
      PRIMARY KEY (calDate);
      
      INSERT INTO calendar VALUES ('2012-10-10', 1);
      INSERT INTO calendar VALUES ('2012-10-11', 1);
      INSERT INTO calendar VALUES ('2012-10-12', 1);
      

      查询:

      select s.someCol, c.calDate from someTable s, calendar c;
      

      【讨论】:

        【解决方案5】:

        对于你想要做的事情,你确实有两种选择。

        1. 如果您的 RDBMS 支持它(我知道 SQL Server 支持,但我不知道其他),您可以创建一个 table-valued function,它接受一个日期范围并返回所有离散的结果集该范围内的日期。您将在表和函数之间进行笛卡尔连接。

        2. 您可以创建一个包含日期值的静态表,然后在两个表之间进行笛卡尔连接。

        第二个选项的效果会更好,尤其是在您处理较大的日期范围时,但是,该解决方案将无法处理任意日期范围。但是,您应该知道您的最短日期,并且随着时间的推移,您可以随时将更多日期添加到您的表格中。

        【讨论】:

          【解决方案6】:

          我对你的 M 表不是很清楚。假设您有这样一个带日期的表(M),则交叉连接将带来结果。

          SELECT C.*, M.date FROM C CROSS JOIN M
          

          【讨论】:

          • 我试图避免使用数据表。如果可能的话,我想将我的表与缺少表表示的“数据范围”连接起来。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-06
          • 2021-03-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多