【问题标题】:How to put Case in Where Statement for Oracle SQL如何在 Oracle SQL 的 Where 语句中添加大小写
【发布时间】:2020-02-24 14:44:05
【问题描述】:

对于下面的查询,我试图根据当月的当天提取特定的日期范围。如果是 20 日或更少(例如“2/7/2020”),那么我想要 1 月的日期范围。否则,我想要二月的日期范围。是否可以通过案例陈述来完成?还是有更好的办法?

SELECT
    account,
    start_date,
    amount
FROM
    table1
WHERE
    CASE
            WHEN (
                SELECT
                    CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
                FROM
                    dual
            ) <= 20 THEN
                    start_date
            BETWEEN '2020-01-01' AND '2020-01-31'
        ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
    END

【问题讨论】:

  • 附带说明:(SELECT CAST(EXTRACT(DAY FROM sysdate) AS NUMBER) FROM dual) 只不过是EXTRACT(DAY FROM sysdate)start_date BETWEEN '2020-01-01' AND '2020-01-31' 应该是 start_date BETWEEN DATE '2020-01-01' AND DATE '2020-01-31'。结果是一个布尔值,如果 Oracle 在 SQL 中提供 BOOLEAN,这将起作用,不幸的是它没有。

标签: sql oracle case


【解决方案1】:

您可以通过避免使用 case 语句并使用截断日期 - 20 到月份来做到这一点,例如:

SELECT account,
     start_date,
     amount
FROM   table1
WHERE  start_date >= TRUNC(SYSDATE - 20, 'mm')
AND    start_date < add_months(TRUNC(dt - 20, 'mm'), 1);

如果您真的必须使用 CASE 表达式(您不能在 SQL 中使用 CASE 语句),您需要执行以下操作:

SELECT account,
     start_date,
     amount
FROM   table1
WHERE  start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND    start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;

注意如果使用的是函数,则不需要将其包裹在select .. from dual中,可以直接在SQL语句中使用。

我还假设您想要一个动态范围,例如如果该月的日期为 20 或更少,则范围为上个月,否则为当前月份。


ETA:如果start_date 列上有索引,您将使用上述两个查询,否则您可以简单地这样做:

SELECT account,
     start_date,
     amount
FROM   table1
WHERE  TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');

【讨论】:

  • to_char(SYSDATE, 'dy') 输出mon (db<>fiddle);您可能希望 dd 作为格式模型(Oracle 将执行隐式字符串到数字的转换),或者像 OP 在他们的问题中那样使用 EXTRACT( DAY FROM SYSDATE)
【解决方案2】:

Case 语句返回单个值。因此,您应该提取开始日期,并且需要两个案例陈述。

select account, start_date, amount
from table1 where
start_date between 
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01' 
else '2020-02-01'
end) and 
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31' 
else '2020-02-29'
end)

【讨论】:

  • 您不需要(select cast( extract( day from sysdate ) as number ) from dual),您可以使用extract( day from sysdate )。此外,'2020-01-01' 是字符串文字,不是日期; Oracle 将隐式尝试使用 NLS_DATE_FORMAT 会话参数将字符串转换为日期,但每个用户都可以在任何时候为此格式模型设置自己的值,因此您永远不应依赖默认格式并使用日期文字 (@987654326 @) 或 TO_DATE 函数使用显式格式模型。
【解决方案3】:

一种方法减去 20 天,然后得到月份边界:

where start_date >= trunc(sysdate - interval '20' day, 'MON') and
      start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month

这种方法对索引(和分区)友好——可以使用start_date 上的适当索引。如果start_date 有时间分量也是安全的。

注意:您可以使用sysdate 而不必使用子查询。

【讨论】:

    【解决方案4】:

    您可以将or 运算符与last_day 函数一起使用,如下所示:

    Select * from your_table
    Where (
           start_date <= trunc(sysdate,'mm') + 20
           and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
          )
       Or
          (
           start_date > trunc(sysdate,'mm') + 20 
           and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
          )
    

    此方法将使用start_date 上的索引(如果有)。

    干杯!!

    【讨论】:

      【解决方案5】:
          select account, amount, start_date
            from table1
           where (   (    (select cast (extract (day from sysdate) as number) from dual) <= 20
                          and start_date between date '2020-01-01' and date '2020-01-31')
                  or (    (select cast (extract (day from sysdate) as number) from dual) > 20
                          and start_date between date '2020-02-01' and date '2020-02-29')
                 );
      

      【讨论】:

        【解决方案6】:

        使用 CASE 表达式作为 BETWEEN 操作数:

        SELECT account
             , start_date
             , amount
        FROM table1
        WHERE start_date BETWEEN CASE
                                   WHEN extract(day from sysdate) <= 20
                                     THEN trunc(sysdate -interval '1' month, 'month')
                                     ELSE trunc(sysdate, 'month')
                                 END
                             AND CASE
                                   WHEN extract(day from sysdate) <= 20
                                     THEN last_day(sysdate -interval '1' month)
                                     ELSE last_day(sysdate)
                                 END
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-01-23
          • 2019-06-26
          • 1970-01-01
          • 1970-01-01
          • 2014-10-19
          • 2013-08-01
          • 2010-09-17
          • 1970-01-01
          相关资源
          最近更新 更多