问题:如何在oracle
中仅选择两个日期之间的星期五日期 SELECT dates,TO_CHAR(dates,'day-mon-yyyy')
FROM
(SELECT to_date('01-jan-12','dd-mon-yy')+rownum -1 AS dates
FROM addresses
WHERE rownum <= to_date('31-jan-12','dd-mon-yy')- to_date('01-jan-12','dd-mon-yy')+1)
WHERE upper(regexp_substr(TO_CHAR(dates,'day-mon-yy'),'([[:alpha:]])+'))=upper('FRIDAY');
我需要输出如:
06-JAN-12 FRIDAY 13-JAN-12 FRIDAY 20-JAN-12 FRIDAY 27-JAN-12 FRIDAY 03-FEB-12 FRIDAY 10-FEB-12 FRIDAY 17-FEB-12 FRIDAY 24-FEB-12 FRIDAY 02-MAR-12 FRIDAY 09-MAR-12 FRIDAY 16-MAR-12 FRIDAY 23-MAR-12 FRIDAY 30-MAR-12 FRIDAY
访问更多sql查询:SQL Query Interview Questions
答案 0 :(得分:3)
您发布的内容会出错,因为您用作to_char()
的第一个参数的值不会用单引号括起来:
select to_date(01-jan-12,'dd-mon-yy') from dual;
ORA-00904: "JAN": invalid identifier
由于没有引号,jan
被解释为标识符,并且(可能)地址表中没有列调用JAN
。使用两位数的年份也是不好的做法,而且你必须(从真正的旧数据)使用RR
而不是YY
。月份名称也受NLS设置的影响,因此使用月份数字比使用名称更安全;如果你真的想要名字,to_char()
函数有第三个参数来控制语言。
您正在以非常复杂的方式执行此操作,并且您依赖于具有足够行的地址表。通过day
而不是DAY
指定您想要小写的日期名称,然后将其设置为大写,然后剥离字符串的位 - 您首先指定的位! - 只获取日期名称,然后根据假设(再次)比较NLS设置将为您提供英文日名称,这是......不必要的复杂。正如调用upper()
对固定的字符串文字一样,你可以(并且)以大写形式提供。
而不是
WHERE upper(regexp_substr(TO_CHAR(dates,'day-mon-yy'),'([[:alpha:]])+'))=upper('FRIDAY');
你可以做以下任何一种或其他变种:
WHERE regexp_substr(TO_CHAR(dates,'DAY-mon-
y'),'([[:alpha:]])+')=upper('FRIDAY');
WHERE TO_CHAR(dates,'DAY')='FRIDAY ';
WHERE TRIM(TO_CHAR(dates,'DAY'))=upper('FRIDAY');
WHERE TO_CHAR(dates,'FMDAY','NLS_DATE_LANGUAGE=ENGLISH')='FRIDAY';
您可以避免使用分层查询依赖于地址表,而不是dual
表:
SELECT next_day(date '2012-01-01' - 1, 'FRIDAY') + (7 * (level - 1))
FROM dual
CONNECT BY next_day(date '2012-01-01' - 1, 'FRIDAY') + (7 * (level - 1))
<= date '2012-03-31';
使用next_day
也依赖于NLS设置,因此除非您始终可以控制会话日期语言,否则获取所有日期然后在NLS中过滤它们可能更安全(如果效率稍低) - 独立的方式:
SELECT dates, to_char(dates, 'FMDAY')
FROM (
SELECT date '2012-01-01' + level - 1 AS dates
FROM dual
CONNECT BY level <= date '2012-03-31' - date '2012-01-01'
)
WHERE to_char(dates, 'FMDAY', 'NLS_DATE_LANGUAGE=ENGLISH') = 'FRIDAY';
DATES TO_CHAR(DATES,'FMDAY')
--------- ------------------------------------
06-JAN-12 FRIDAY
13-JAN-12 FRIDAY
20-JAN-12 FRIDAY
27-JAN-12 FRIDAY
03-FEB-12 FRIDAY
10-FEB-12 FRIDAY
17-FEB-12 FRIDAY
24-FEB-12 FRIDAY
02-MAR-12 FRIDAY
09-MAR-12 FRIDAY
16-MAR-12 FRIDAY
23-MAR-12 FRIDAY
30-MAR-12 FRIDAY
13 rows selected.
正如@mathguy在评论中指出的那样,尽管next_day()
对NLS敏感,但您可以使用表达式作为第二个参数,因此您可以执行以下操作来代替硬编码日期名称:
next_day(date '2012-01-01' - 1, to_char(date '1999-12-31', 'FMDAY'))
1999-12-31可以是任何已知星期五的日期;如果你不介意选择列表中的表达式和连接条款不同(实际上,你 - 我 - 不应该!)你可以通过以下方式降低该检查的计算成本: / p>
SELECT dates, to_char(dates, 'FMDAY', 'NLS_DATE_LANGUAGE=ENGLISH')
FROM (
SELECT next_day(date '2012-01-01' - 1,
to_char(date '1999-12-31', 'FMDAY')) + (7 * (level - 1)) AS dates
FROM dual
CONNECT BY level <= 1 + (date '2012-03-31' - next_day(date '2012-01-01' - 1,
to_char(date '1999-12-31', 'FMDAY')))/7
);
无论会话的日期语言如何,都会获得与上述相同的13行。如果您希望输出也是会话语言,只需删除覆盖的第三个参数to_char()
。