关于postgresql:带有日期范围的SQL JOIN表

SQL JOIN table with a date range

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

澄清事情的简单例子:
鉴于下表A:

1
2
3
4
SELECT * FROM A;
avalue  |
--------+
"a"     |

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

1
2
3
4
5
avalue  |  DATE
--------+-------
"a"     | 2012-10-10
"a"     | 2012-10-11
"a"     | 2012-10-12

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


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

1
2
3
4
5
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()是产生派生表的集合返回函数(a.k.a。"表函数")。有几个重载变体,这就是我选择timestamp输入的原因:

  • 生成PostgreSQL中两个日期之间的时间序列

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

1
2
3
4
5
6
7
8
SELECT *
FROM   tbl t
CROSS  JOIN (
   VALUES
     (DATE '2012-08-13')  -- explicit type in 1st row
   , ('2012-09-05')
   , ('2012-10-10')
   ) g(DAY);

如果日期表中包含的日期多于您感兴趣的日期,那么请执行此操作

1
SELECT a.avalue, b.date FROM a, b WHERE b.date BETWEEN '2012-10-10' AND '2012-10-12'

另外明智的是,如果日期表只包含您感兴趣的日期,则笛卡尔联接将实现此目的:

1
SELECT * FROM a,b;


你真的有两个选择,你想要做什么。

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

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

  • 第二个选项将表现更好,特别是如果您正在处理大日期范围,但是,该解决方案将无法处理任意日期范围。但是,你应该知道你的最短约会,随着时间的推移,你总是可以在你的桌子上添加更多日期。


    对于MySQL

    架构/数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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);

    查询:

    1
    SELECT s.someCol, c.calDate FROM someTable s, calendar c;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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

    我对你的M桌不是很清楚。如果你有这样一个带有日期的表(M),那么在交叉连接之后会带来结果。

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