我想将日期间隔添加到 Oracle SQL 中的日期列表,即下周的星期一。但是,当我使用建议的
date + 1
类型的间隔时,我遇到了一个非常奇怪的错误,其中只有一天按预期加起来,即 Wednesday
。我在这里做错了什么?这是 Oracle SQL Developer 中的错误吗?
WITH data AS (
SELECT '29-JAN-23' dt FROM dual UNION
SELECT '30-JAN-23' dt FROM dual UNION
SELECT '31-JAN-23' dt FROM dual UNION
SELECT '01-FEB-23' dt FROM dual UNION
SELECT '02-FEB-23' dt FROM dual UNION
SELECT '03-FEB-23' dt FROM dual UNION
SELECT '04-FEB-23' dt FROM dual UNION
SELECT '05-FEB-23' dt FROM dual UNION
SELECT '06-FEB-23' dt FROM dual UNION
SELECT '07-FEB-23' dt FROM dual UNION
SELECT '08-FEB-23' dt FROM dual UNION
SELECT '09-FEB-23' dt FROM dual
)
SELECT
TO_DATE(dt) dt,
TO_CHAR(TO_DATE(dt),'DAY') dt_day,
CASE
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'MONDAY' THEN TO_DATE(dt)
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'TUSEDAY' THEN TO_DATE(dt) + 6
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'WEDNESDAY' THEN TO_DATE(dt) + 5
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'THURSDAY' THEN TO_DATE(dt) + 4
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'FRIDAY' THEN TO_DATE(dt) + 3
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'SATURDAY' THEN TO_DATE(dt) + 2
WHEN TO_CHAR(TO_DATE(dt),'DAY') = 'SUNDAY' THEN TO_DATE(dt) + 1
END new_dt
FROM data
ORDER BY TO_DATE(dt)
;
输出
| DT | DT_DAY | NEW_DT |
|-----------|-----------|-----------|
| 29-JAN-23 | SUNDAY | |
| 30-JAN-23 | MONDAY | |
| 31-JAN-23 | TUESDAY | |
| 01-FEB-23 | WEDNESDAY | 06-FEB-23 |
| 02-FEB-23 | THURSDAY | |
| 03-FEB-23 | FRIDAY | |
| 04-FEB-23 | SATURDAY | |
| 05-FEB-23 | SUNDAY | |
| 06-FEB-23 | MONDAY | |
| 07-FEB-23 | TUESDAY | |
| 08-FEB-23 | WEDNESDAY | 13-FEB-23 |
| 09-FEB-23 | THURSDAY | |
几件事:为 to_date 使用格式掩码,然后记住 'DAY' 返回一个字符串,该字符串填充了一定长度的空格。 因此:
WITH data AS (
SELECT '29-JAN-23' dt FROM dual UNION
SELECT '30-JAN-23' dt FROM dual UNION
SELECT '31-JAN-23' dt FROM dual UNION
SELECT '01-FEB-23' dt FROM dual UNION
SELECT '02-FEB-23' dt FROM dual UNION
SELECT '03-FEB-23' dt FROM dual UNION
SELECT '04-FEB-23' dt FROM dual UNION
SELECT '05-FEB-23' dt FROM dual UNION
SELECT '06-FEB-23' dt FROM dual UNION
SELECT '07-FEB-23' dt FROM dual UNION
SELECT '08-FEB-23' dt FROM dual UNION
SELECT '09-FEB-23' dt FROM dual
)
SELECT
TO_DATE(dt) dt,
TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY') dt_day,
CASE
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'MONDAY' THEN TO_DATE(dt,'dd-mon-rr')
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'TUSEDAY' THEN TO_DATE(dt,'dd-mon-rr') + 6
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'WEDNESDAY' THEN TO_DATE(dt,'dd-mon-rr') + 5
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'THURSDAY' THEN TO_DATE(dt,'dd-mon-rr') + 4
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'FRIDAY' THEN TO_DATE(dt,'dd-mon-rr') + 3
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'SATURDAY' THEN TO_DATE(dt,'dd-mon-rr') + 2
WHEN trim(TO_CHAR(TO_DATE(dt,'dd-mon-rr'),'DAY')) = 'SUNDAY' THEN TO_DATE(dt,'dd-mon-rr') + 1
END new_dt
FROM data
ORDER BY TO_DATE(dt,'dd-mon-rr')
;
您的转换可以简化为:
TRUNC(<original_date> + 6,'IW')
IW
format 元素 是 ISO 日历周,它始终从星期一开始,并截断为该周的每一天提供相同的周开始日期;基本上从星期二开始为您添加六个调整。
所以首先将您的字符串转换为日期:
TRUNC(TO_DATE(dt,'dd-mon-rr','nls_date_language=english') + 6,'IW')
(当然,并不是说您应该将日期存储为字符串;它们在您的示例数据 CTE 中,但可能会检查您真正使用的数据类型,并且您不只是假设您需要转换,因为您 在您的客户端中查看该格式的实际日期...)
使用您当前的 CTE 得到相同的结果:
SELECT
TO_DATE(dt) dt,
TO_CHAR(TO_DATE(dt,'dd-mon-rr','nls_date_language=english'),'FMDAY','nls_date_language=english') dt_day,
TRUNC(TO_DATE(dt,'dd-mon-rr','nls_date_language=english') + 6,'IW') new_dt
FROM data
ORDER BY TO_DATE(dt,'dd-mon-rr','nls_date_language=english')
;
DT | DT_DAY | NEW_DT |
---|---|---|
29-JAN-23 | 周日 | 23 年 1 月 30 日 |
23 年 1 月 30 日 | 星期一 | 23 年 1 月 30 日 |
31-JAN-23 | 星期二 | 06-FEB-23 |
01-FEB-23 | 星期三 | 06-FEB-23 |
02-FEB-23 | 星期四 | 06-FEB-23 |
03-FEB-23 | 星期五 | 06-FEB-23 |
04-FEB-23 | 星期六 | 06-FEB-23 |
05-FEB-23 | 周日 | 06-FEB-23 |
06-FEB-23 | 星期一 | 06-FEB-23 |
07-FEB-23 | 星期二 | 13-FEB-23 |
08-FEB-23 | 星期三 | 13-FEB-23 |
09-FEB-23 | 星期四 | 13-FEB-23 |
我已经将可选的第三个参数包含在
to_date()
中,因此即使会话日期语言是其他语言,月份缩写也会被识别。如果您从实际日期而不是字符串开始,则无需担心另一件事。
您可以将生成器简化为:
WITH data (dt) AS (
SELECT DATE '2023-01-29' + LEVEL - 1
FROM DUAL
CONNECT BY DATE '2023-01-29' + LEVEL - 1 <= DATE '2023-02-09'
)
SELECT dt,
TO_CHAR(dt,'fmDAY') dt_day,
CASE
WHEN dt = TRUNC(dt, 'IW')
THEN dt
ELSE TRUNC(dt, 'IW') + 7
END new_dt
FROM data
ORDER BY dt;
哪些输出:
DT | DT_DAY | NEW_DT |
---|---|---|
2023-01-29 00:00:00 | 周日 | 2023-01-30 00:00:00 |
2023-01-30 00:00:00 | 星期一 | 2023-01-30 00:00:00 |
2023-01-31 00:00:00 | 星期二 | 2023-02-06 00:00:00 |
2023-02-01 00:00:00 | 星期三 | 2023-02-06 00:00:00 |
2023-02-02 00:00:00 | 星期四 | 2023-02-06 00:00:00 |
2023-02-03 00:00:00 | 星期五 | 2023-02-06 00:00:00 |
2023-02-04 00:00:00 | 星期六 | 2023-02-06 00:00:00 |
2023-02-05 00:00:00 | 周日 | 2023-02-06 00:00:00 |
2023-02-06 00:00:00 | 星期一 | 2023-02-06 00:00:00 |
2023-02-07 00:00:00 | 星期二 | 2023-02-13 00:00:00 |
2023-02-08 00:00:00 | 星期三 | 2023-02-13 00:00:00 |
2023-02-09 00:00:00 | 星期四 | 2023-02-13 00:00:00 |
我在这里做错了什么?
TO_DATE(date_value, 'DAY')
输出一个固定长度的字符串,它是您的语言中最长日期名称的长度。对于英语是 'WEDNESDAY'
和其他字符串将用空格填充,例如 'MONDAY '
.
如果你不想填充字符串,那么使用
TO_DATE(date_value, 'fmDAY')
然后你的代码将工作(如果你拼写 TUESDAY
正确并且隐式日期转换成功)。
oracle 有一个简单的版本来获取日期
select date'2023-01-29' + level - 1 dt
from dual
connect by level <= (
date'2023-02-15' - date'2023-01-29' + 1
);
DT |
---|
29-JAN-23 |
23 年 1 月 30 日 |
31-JAN-23 |
01-FEB-23 |
02-FEB-23 |
03-FEB-23 |
04-FEB-23 |
05-FEB-23 |
06-FEB-23 |
07-FEB-23 |
08-FEB-23 |
09-FEB-23 |
10-FEB-23 |
11-FEB-23 |
12-FEB-23 |
13-FEB-23 |
14-FEB-23 |
15-FEB-23 |