【问题标题】:Filter by date range (same month and day) across years跨年份按日期范围(同一月和同一天)过滤
【发布时间】:2012-08-10 07:11:34
【问题描述】:

我有一个 PostgreSQL 数据库,其中有一个保存日期的表。 现在我需要在所有年份中查找日期范围 15/0221/06(日/月)内的所有行。

示例结果:

1840-02-28
1990-06-21
1991-02-15
1991-04-25
1992-05-30
1995-03-04
1995-04-10
2001-02-03
2010-04-06

【问题讨论】:

  • 这是两者中更好的问题,所以请不要关闭它。反正另一家已经关门了。
  • 15/02 和 21/06 应该代表什么?日/月?不分年份?
  • 我冒昧地以我(和@kgrittn)理解的方式澄清了这个问题。如果我错了,请回滚。
  • 这是that one的后续问题。

标签: sql database postgresql date postgresql-8.4


【解决方案1】:

假设(充满信心)您希望在一年中的某些日子之间有日期,而不管年份如何(例如,如果您要发送一批生日贺卡或其他东西),您可以使用此设置测试:

CREATE TABLE d (dt date);
COPY d FROM STDIN;
1840-02-28
1990-06-21
1991-02-15
1991-04-25
1992-05-30
1995-03-04
1995-04-10
2001-02-03
2010-04-06
\.

并且您可以使用“行值构造函数”轻松选择所需的范围:

SELECT * FROM d
  WHERE (EXTRACT(MONTH FROM dt), EXTRACT(DAY FROM dt))
           BETWEEN (2, 15) AND (6, 21);

产量:

dt ------------ 1840-02-28 1990-06-21 1991-02-15 1991-04-25 1992-05-30 1995-03-04 1995-04-10 2010-04-06 (8 行)

【讨论】:

  • +1 这是最好的答案。您不仅解决了实际问题(到目前为止其他问题都失败了),答案也非常好。 I posted a comparison
【解决方案2】:

使用带有BETWEEN 运算符的WHERE 子句。见:

http://www.postgresql.org/docs/current/static/functions-comparison.html#FUNCTIONS-COMPARISON

和:

http://www.postgresql.org/docs/current/static/sql-select.html http://www.postgresql.org/docs/current/static/tutorial.html

如果这没有帮助,请扩展您的问题:

  • 您正在使用的表的结构,来自 psql 的 \d tablename 命令或原始 CREATE TABLE 语句;
  • 一些示例内容
  • 您遇到问题的查询
  • 预期结果

【讨论】:

    【解决方案3】:

    您可以使用以下语法。

    SELECT * FROM tableName WHERE dateColumnName BETWEEN '2012.01.01' AND '2012.08.14';

    只需替换以下;

    tableName       - Name of the table you are going to access
    dateColumnName  - Name of the column whch contains dates
    2012.08.1       - Start date
    2012.08.21      - End date 
    

    输入两个日期时,请仔细检查上面的示例。以相同的格式输入,并将它们括在''s中。

    如果将* 标记替换为列名,则只能过滤掉该列的值。

    希望对您有所帮助..

    【讨论】:

      【解决方案4】:

      我很确定,@kgrittn 对问题的解释是准确的,我喜欢他对行构造函数的优雅使用。在我测试了几个替代方案之后更是如此,但没有一个可以与性能相匹配:

      使用 65426 行的真实表格进行测试; 32107 合格。 PostgreSQL 9.1.4,最好的五个EXPLAIN ANALYZE

      SELECT * FROM tbl
      WHERE  to_char(data, 'MMDD') BETWEEN '0215' AND '0621';
      

      总运行时间:251.188 毫秒

      SELECT * FROM tbl
      WHERE  to_char(data, 'MMDD')::int BETWEEN 215 AND 621;
      

      总运行时间:250.965 毫秒

      SELECT * FROM tbl
      WHERE  to_char(data, 'MMDD') COLLATE "C" BETWEEN '0215' AND '0621';
      

      总运行时间:221.732 毫秒
      使用“非语言环境”C 进行字符串比较的速度更快 - 在the manual about collation support 中更多。

      SELECT * FROM tbl
      WHERE  EXTRACT(MONTH FROM data)*100 + EXTRACT(DAY FROM data)
             BETWEEN 215 AND 621;
      

      总运行时间:209.965 毫秒

      SELECT * FROM tbl
      WHERE  EXTRACT(MONTH FROM data) BETWEEN 3 AND 5
      OR     EXTRACT(MONTH FROM data) = 2 AND EXTRACT(DAY FROM data) >= 15
      OR     EXTRACT(MONTH FROM data) = 6 AND EXTRACT(DAY FROM data) <= 21;
      

      总运行时间:160.169 毫秒

      SELECT * FROM tbl
      WHERE  EXTRACT(MONTH FROM data) BETWEEN 2 AND 6
      AND    CASE EXTRACT(MONTH FROM data) 
             WHEN 2 THEN EXTRACT(DAY FROM data) >= 15
             WHEN 6 THEN EXTRACT(DAY FROM data) <=21
             ELSE TRUE END;
      

      总运行时间:147.390 毫秒

      SELECT * FROM tbl
      WHERE  CASE EXTRACT(MONTH FROM data) 
             WHEN 3 THEN TRUE
             WHEN 4 THEN TRUE
             WHEN 5 THEN TRUE
             WHEN 2 THEN EXTRACT(DAY FROM data) >= 15
             WHEN 6 THEN EXTRACT(DAY FROM data) <= 21
             ELSE FALSE END;
      

      总运行时间:131.907 毫秒

      @Kevin 的行构造函数解决方案:

      SELECT * FROM tbl
      WHERE (EXTRACT(MONTH FROM data), EXTRACT(DAY FROM data))
             BETWEEN (2, 15) AND (6, 21);
      

      总运行时间:125.460 毫秒
      起首。


      功能索引更快

      唯一的方法就是使用索引。以上查询都不能在data 上使用普通索引。但是,如果读取性能至关重要(并且写入性能的成本很小),您可以求助于functional index

      CREATE INDEX ON tbl(EXTRACT(MONTH FROM data), EXTRACT(DAY FROM data));
      
      SELECT * FROM tbl
      WHERE (EXTRACT(MONTH FROM data), EXTRACT(DAY FROM data))
             BETWEEN (2, 15) AND (6, 21);
      

      总运行时间:85.895 毫秒

      这就是我最终可以击败 Kevin 的查询的地方:使用单列索引而不是他的案例所需的多列索引。

      CREATE INDEX ON tbl(
      CAST(EXTRACT(MONTH FROM data) * 100 + EXTRACT(DAY FROM data) AS int));
      
      SELECT * FROM tbl
      WHERE  (EXTRACT(MONTH FROM data) * 100 + EXTRACT(DAY FROM data))::int
             BETWEEN 215 AND 621;
      

      总运行时间:84.215 毫秒

      【讨论】:

        【解决方案5】:

        您可以使用简单的条件 >= 和

        【讨论】:

          猜你喜欢
          • 2016-01-08
          • 1970-01-01
          • 1970-01-01
          • 2012-12-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-01
          • 1970-01-01
          相关资源
          最近更新 更多