如何在 Oracle SQL 的 Where 语句中添加大小写

Posted

技术标签:

【中文标题】如何在 Oracle SQL 的 Where 语句中添加大小写【英文标题】:How to put Case in Where Statement for Oracle SQL 【发布时间】: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,这将起作用,不幸的是它没有。 【参考方案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

【讨论】:

以上是关于如何在 Oracle SQL 的 Where 语句中添加大小写的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Oracle SQL 的 Where 语句中添加大小写

如何优化Oracle在where条件中用了自定义函数的SQL语句

Oracle SQL的Where子句中如何写case语句?

可以在 select 语句的 where 部分中使用的 ORACLE SQL 函数

oracle如何检查sql语法错误

如何优化SQL语句