【问题标题】:Filter records by year and month where year and month are columns in the table按年份和月份过滤记录,其中年份和月份是表中的列
【发布时间】:2014-08-04 01:49:51
【问题描述】:

所以我有这个特定的表结构

State_Code | ColA | ColB | Year | Month
----------  ------  ----  ------  -----
AK            5      3     2013   4
AK            6      1     2014   8
AK            3      4     2012   9
.
.
.
.
.

我无权更改表结构。 所需查询描述如下

查找过去 12 个月和过去 24 个月之间每个州的总 ColA、ColB。

问题 是否可以在不使用存储过程的情况下完成这样的查询。 我可以使用 add_months 构建查询以获得正确的年份,但我无法解决选择月份范围的逻辑问题。

【问题讨论】:

  • answer by Farhad Jabiyev(现已删除)只需要简单的修复。将“一个月中的某天”从 ExtractDay(sysdate) 更改为每月的 '01'(因为存储的数据只有 1 个月的粒度)。所需的修复是微不足道的。它确实强调了日期算术是棘手的事情,从日期中减去月份时必须始终牢记闰年和月末(什么日期对应于 2014-04-29 之前的 2 个月, 2014-04-30, 2014-08-31, 2016-04-29, 2016-04-30?)
  • 说实话,在 OP 不接受并否决我的回答后,我知道问题出在哪里。但是,那些日子我的工作太多,我不想用错误来回答。我删除了它。但是,我必须承认这是错误的。我刚刚在 meta SO 上无意中看到了您的 question。 :) 我取消了我的答案,因为它似乎以某种方式有所帮助。

标签: sql oracle


【解决方案1】:

社区 Wiki,因为它大量借鉴了其他答案。

Farhad Jabiyevanswer(现已删除 - 请参阅 Is it OK to unaccept an accepted answer after weeks)是:

SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
FROM TABLE_NAME 
WHERE to_date(ExtractDay(sysdate) ||  MONTH || YEAR, 'ddMMyyyy')
    between add_months(sysdate,-12) AND add_months(sysdate,-24)
GROUP BY STATE_CODE;

当当前日期在某些时间间隔的月底(-24..-12 个月范围,通常仅在闰日,但对于其他月份范围,它可以)时,此答案会遇到问题从本月 29 日开始遇到问题)。

但是,它坚定地走在正确的轨道上,只需要(相对)微不足道的修复。要求是得到当月减去12个月和当月减去24个月的年/月组合。

  • 将“日期”从 ExtractDay(sysdate) 更改为每月的 '01'(因为存储的数据只有一个月粒度,每个月都有一个月的第一天,但​​不是每个月都有 29 日、30 日或31 日)。

如果使用日期,则需要获取当前月份的第一天(两次),这是详细的:

SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
  FROM TABLE_NAME 
 WHERE to_date('01' ||  MONTH || YEAR, 'ddMMyyyy')
       BETWEEN to_date('01' || ExtractMonth(add_months(sysdate, -24)) ||
                               ExtractYear(add_months(sysdate,  -24)), 'ddMMyyyy')
       AND     to_date('01' || ExtractMonth(add_months(sysdate, -12)) ||
                               ExtractYear(add_months(sysdate,  -12)), 'ddMMyyyy')
 GROUP BY STATE_CODE;

另外,可能更好(如果只是因为更简单)是按照Gordon Linoff 在他的answer 中的建议转换年/月组合:

SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
  FROM TABLE_NAME 
 WHERE (MONTH + 100 * YEAR)
       BETWEEN (ExtractMonth(add_months(sysdate, -24)) + 100 *
                ExtractYear(add_months(sysdate,  -24)))
       AND     (ExtractMonth(add_months(sysdate, -12)) + 100 *
                ExtractYear(add_months(sysdate,  -12))
 GROUP BY STATE_CODE;

需要注意的一些遗留问题:

  1. x BETWEEN y AND z 的标准行为要求 y 小于或等于 z。也就是说,x BETWEEN y AND z 等价于 x >= y AND x <= z。 (是否有任何 DBMS 将其视为等同于 x >= MIN(y, z) AND z <= MAX(y, z)?)因此上面的代码切换 -12 和 -24,因此范围是正确的。
  2. x BETWEEN y AND z 的范围包括在内,因此 -24 到 -12 的范围涵盖 13 个月,而不是 12 个月.只要这是你想要的,没问题。否则,您可能需要 -24..-13 或 -23..-12。
  3. 您可以使用函数YEAR()MONTH() 代替ExtractYear()ExtractMonth()。您可能需要担心列名 YearMonth 与同名函数之间的歧义。这是否重要取决于 DBMS。

这个问题确实强调了日期算术是一个棘手的问题,从日期中减去月份时,必须始终牢记闰年和月末。例如,2014-04-29、2014-04-30、2014-08-31、2016-04-29、2016-04-30 前 2 个月对应的日期是什么?

警告:未经测试的 SQL:可能存在语法错误。

【讨论】:

    【解决方案2】:
    SELECT STATE_CODE, SUM(COLA) SUM_COLA, SUM(COLB) SUM_COLB
    FROM TABLE_NAME 
    WHERE to_date(ExtractDay(sysdate) ||  MONTH || YEAR, 'ddMMyyyy')
        BETWEEN add_months(sysdate,-12) AND add_months(sysdate,-24)
    GROUP BY STATE_CODE;
    

    【讨论】:

    • 感谢您为我指明正确的方向。我对您的查询和 between 子句进行了一些调整以使其正确。谢谢!
    • @Serendipity 欢迎您。祝你好运。如果需要,您可以编辑我的答案。
    • 虽然这有点晚了,但我今天才意识到(今天是 29 日),当日历月为“2”时,此代码无法提取数据。或者就此而言,日历中的天数超过了该月的允许天数。
    • @Serendipity 你试过把ddMMyyyy改成ddMyyyy吗?
    • 你终于回来了:)。我正在添加原始的赞成票。我意识到无论如何......这个答案确实把我推向了正确的方向。
    【解决方案3】:

    作为说明,我发现最简单的方法是使用日期。只需将年月表达式转换为 YYYYMM 格式的数字。例如,2014 年 5 月变为 201405。我还将使用条件聚合来获取包含历史摘要的两列:

    select state_code,
           sum(case when year * 100 + month > (year(sysdate) - 1) * 100 + month(sysdate) and
                         year * 100 + month <= year(sysdate) * 100 + month(sysdate)
                    then colA + colB else 0
               end) as AB_12,
           sum(case when year * 100 + month > (year(sysdate) - 2) * 100 + month(sysdate) and
                         year * 100 + month <= year(sysdate) * 100 + month(sysdate)
                    then colA + colB else 0
               end) as AB_24
    from table t
    group by state_code;
    

    【讨论】:

      【解决方案4】:
      select t.state_code, sum(t.cola) total_cola, sum(t.colb) total_colb
      from your_table t
      where to_date(t.year || lpad(t.month, 2, '0'), 'yyyymm')
            between add_months(sysdate, -12)
                and add_months(sysdate, -24)
      group by t.state_code
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-18
        • 2017-07-01
        • 1970-01-01
        • 2023-01-25
        • 1970-01-01
        • 2015-11-28
        • 2016-12-23
        • 2020-03-25
        相关资源
        最近更新 更多