【问题标题】:Postgresql select between month rangePostgresql 在月份范围之间选择
【发布时间】:2012-07-02 10:47:07
【问题描述】:

我有一个表格,其中一列是“YYYY-MM-DD”格式的日期。我可以使用 select 获取每月范围内的所有数据吗?假设我想要从 2012-01-xx 到 2013-04-xx 的所有数据。所以我基本上是在寻找如下所示的 SQL 查询:

SELECT * FROM table WHERE date IN BETWEEN '2012-01' AND '2013-04' (INVALID QUERY)

由于每个月都以'01'开头,我可以修改上面的查询来调整开始条件。

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND '2013-04' (INVALID QUERY)

现在问题与结束日期有关。我必须手动计算给定月份的最后日期,同时考虑所有因素,如月份长度、闰年等,因为如果给定日期无效,查询将失败。所以目前我正在做这样的事情:

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND 'VALID_MONTH_END_DATE' (VALID Query)

我想知道是否有任何方法可以避免这种有效的结束日期计算?

澄清

我已经想到了下个月的第一天,但​​即使这样我也必须应用一些逻辑,如果是 12 月,下个月将是明年的 1 月。我想知道是否可以使用仅 SQL 的解决方案?

【问题讨论】:

    标签: sql postgresql date select date-range


    【解决方案1】:

    在比较日期范围时最好避免使用 BETWEEN。更好地使用>=<,因为它同样适用于日期和日期时间列/值。

    一种方式(如果您可以在外部构建日期):

    WHERE date >= DATE '2012-01-01' 
      AND date < DATE '2013-05-01'      --- first date of the next month
    

    你也可以使用日期算术:

    WHERE date >= DATE '2012-01-01' 
      AND date < DATE ('2013-04-01' + INTERVAL '1 MONTH')
    

    OVERLAPS 运算符:

    WHERE (date, date) OVERLAPS
          (DATE '2012-01-01', DATE '2013-05-01')
    

    您还应该阅读 Postgres 文档:Date/Time Functions and Operators

    manual explains here 为什么OVERLAPS 会这样工作:

    每个时间段都被认为代表半开区间 start

    【讨论】:

    • 当我知道最后一个日期时,重叠效果很好。最后一个月是 12 月,我被困住了。
    • 从哪里获取参数?一个应用程序?网络?另一个查询?您将它们作为字符串还是整数(年、月)?
    • interval 添加到date 时必须小心。结果是timestamp。但是,在上面的示例中有效。将结果投射到date,如果重要的话。添加间隔“n 月”总是会产生同一天的月份,无论所涉及月份中的实际天数是多少 - 或者当结果月份的天数较少时,最大可用天数是多少。要添加确切的天数,请将integer 添加到date,这将生成date
    • 大多数现代优化器是否会将“介于”转换为 ">= 和
    • +1 表示重叠...但这些选项仍然不能满足操作的需求。如果提供了两个日期(开始日期和结束日期)...并且您必须弄清楚月底?
    【解决方案2】:

    没试过,但值得一试

    SELECT * 
    FROM some_table 
    WHERE some_date 
    BETWEEN '2012-01-01' AND date('2013-04-01') - integer '1'
    

    http://www.postgresql.org/docs/9.1/static/functions-datetime.html

    【讨论】:

    • ypercube 有更好的答案,不需要在之间使用
    【解决方案3】:

    这是报告环境中非常常见的需求。我创建了几个函数来适应这些日期操作

    CREATE OR REPLACE FUNCTION public.fn_getlastofmonth (
      date
    )
    RETURNS date AS
    $body$
    begin
        return (to_char(($1 + interval '1 month'),'YYYY-MM') || '-01')::date - 1;
    end;
    $body$
    LANGUAGE 'plpgsql'
    VOLATILE
    CALLED ON NULL INPUT
    SECURITY INVOKER
    COST 100;
    

    那么你就可以使用...

    WHERE date >= '2012-01-01' 
      AND date < fn_getlastofmonth('2013-04-01') 
    

    【讨论】:

      【解决方案4】:

      以上所有答案都提供了一个可行的解决方案,但以某种方式不完整。由于我一直在寻找仅 SQL 的解决方案(无功能),因此我结合了上述解决方案中的最佳技巧。

      我的问题的理想解决方案是:

      SELECT * FROM table
      WHERE date >= '2012-01-01' AND date < date('2013-04-01') + interval '1 month'
      

      编辑

      我在这里没有使用重叠函数,因为我将开始和结束的默认日期值作为“纪元”和“现在”传递。如果用户没有指定任何时间范围,则查询变为:

      SELECT * FROM table
      WHERE date >= 'epoch' AND date < 'now'
      

      Overlap 函数无法处理 'epoch' 和 'now' 并给出 SQL 错误,而上面的代码在这两种情况下都能完美运行。

      PS:我已经对所有正确的答案投了赞成票,并引导我找到了这个解决方案。

      【讨论】:

      • 我认为您需要删除 - interval '1 day' 部分。按原样试一试,在桌子上注明'2013-04-30' 日期。
      • 我正在寻找月度数据。假设我需要从某年、某月到 yyyy-mm 获取所有数据。我的结束日期始终是 yyyy-mm-01,我会在其中添加一个月并减去一天。
      • 只要测试查询:SELECT * FROM table WHERE (date, date) OVERLAPS('2012-01-01' , date('2013-04-01') + interval '1 month' - interval '1 day'),当表中有一个date,其值为2013-04-30。退货了吗?
      • 您好,我刚刚查过了。它不起作用。感谢您指出。这是由于重叠函数的性质。
      【解决方案5】:
      SET search_path=tmp;
      
      DROP TABLE zdates;
      CREATE TABLE zdates
              ( zdate timestamp NOT NULL PRIMARY KEY
              , val INTEGER NOT NULL
              );
      -- some data
      INSERT INTO zdates(zdate,val)
      SELECT s, 0
      FROM generate_series('2012-01-01', '2012-12-31', '1 day'::interval ) s
              ;
      
      UPDATE zdates
      SET val = 1000 * random();
      
      DELETE FROM zdates
      WHERE random() < 0.1;
      
      -- CTE to round the intervals down/up to the begin/end of the month
      WITH zope AS (
              SELECT date_trunc('month', zdate)::date AS zbegin
              ,  date_trunc('month', zdate+interval '1 month')::date AS zend
              , val AS val
              FROM zdates
              )
      SELECT z.zbegin
              , z.zend
              , COUNT(*) AS zcount
              , SUM(val) AS zval
      FROM zope z
      GROUP BY z.zbegin, z.zend
      ORDER BY z.zbegin, z.zend
              ;
      

      结果:

      CREATE TABLE
      INSERT 0 366
      UPDATE 366
      DELETE 52
         zbegin   |    zend    | zcount | zval  
      ------------+------------+--------+-------
       2012-01-01 | 2012-02-01 |     28 | 13740
       2012-02-01 | 2012-03-01 |     28 | 14923
       2012-03-01 | 2012-04-01 |     26 | 13775
       2012-04-01 | 2012-05-01 |     25 | 11880
       2012-05-01 | 2012-06-01 |     25 | 12693
       2012-06-01 | 2012-07-01 |     25 | 11082
       2012-07-01 | 2012-08-01 |     26 | 13254
       2012-08-01 | 2012-09-01 |     28 | 13632
       2012-09-01 | 2012-10-01 |     28 | 16461
       2012-10-01 | 2012-11-01 |     23 | 12622
       2012-11-01 | 2012-12-01 |     24 | 12554
       2012-12-01 | 2013-01-01 |     28 | 14563
      (12 rows)
      

      【讨论】:

        猜你喜欢
        • 2012-03-16
        • 1970-01-01
        • 2013-05-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-19
        • 1970-01-01
        相关资源
        最近更新 更多