sql查询只获取两个日期之间的星期五日期

时间:2017-03-06 19:04:06

标签: sql oracle

问题:如何在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

1 个答案:

答案 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()