我正在寻找一些SQL来确定当前周是否是包含该月第N天的那一周。
举例说明。我想知道我是否在包含本月将发生的第三个星期五的那周。或者一个月中的哪个星期包含第三个星期五。周应该被定义为从周日开始,到周六结束。因此,从周六开始的一个月直到第四周才会看到周五,而从周日到周五开始的月份将看到该月第三周的第三个星期五。
不确定这是否需要PL/SQL。
发布于 2012-08-22 13:45:15
您可以简单地生成包含每月第N天的周的日期范围,例如每个月的第三个星期五,使用如下查询:
select d - day_of_week AS sunday
,d + (7 - day_of_week) AS saturday
from (select trunc(sysdate,'YY')+rownum-1 AS d
,to_number(to_char(trunc(sysdate,'YY')+rownum-1,'D'))
AS day_of_week
from dual connect by level <= 366)
where to_char(d,'W') = 3
and to_char(d,'DY') = 'FRI';
SUNDAY SATURDAY
========== ==========
14/01/2012 21/01/2012
11/02/2012 18/02/2012
10/03/2012 17/03/2012
14/04/2012 21/04/2012
12/05/2012 19/05/2012
09/06/2012 16/06/2012
14/07/2012 21/07/2012
11/08/2012 18/08/2012
15/09/2012 22/09/2012
13/10/2012 20/10/2012
10/11/2012 17/11/2012
15/12/2012 22/12/2012编辑:您可以创建一个简单的函数来检查单个日期,例如:
CREATE FUNCTION date_in_week_of_nth_day
(in_date IN DATE
,in_week IN NUMBER
,in_day IN VARCHAR2
) RETURN CHAR IS
ret CHAR(1);
BEGIN
select 'Y' into ret
from (select trunc(in_date,'MM')+rownum-1 AS d
,to_number(to_char(trunc(in_date,'MM')+rownum-1,'D'))
AS day_of_week
from dual connect by level <= 31)
where to_char(d,'W') = in_week
and to_char(d,'DY') = in_day
and :in_date between (d - day_of_week) and (d + (7 - day_of_week));
RETURN ret;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'N';
END;发布于 2012-08-22 07:02:32
所有现代SQL平台都有丰富的日期/时间函数来进行日期运算。但是当我看到构建在它们之上的SQL时,尤其是对于像您这样的需求,我的眼睛会变得呆滞。
我使用的是精心制作的日历表格。关于使用它,有两件事我很喜欢。
下面是针对我的日程表的查询,如果您试图回答这个问题,“当前一周是否包含当月的第三个星期五?”(我的日历表使用ISO周数字,而不是严格的日历周。下面的两个解决方法。)
select cal_date
from calendar
where year_of_date = 2012
and iso_week = (select iso_week
from calendar
where cal_date = current_date)
and day_of_week = 'Fri'
and day_of_week_ordinal = 3;它不返回任何行;当前一周( 2012 -08-19到2012-08-25)不包含2012年8月的第三个星期五。(它包含第四个星期五。)
根本不需要更改我的表,我就可以用这个查询回答您的第一个问题。它将您对一周的定义包装在一个公用表表达式中。如果我必须在生产中使用这样的东西,我可能会创建一个视图,而不是CTE。
with current_week as (
select *
from calendar
where cal_date between (select max(cal_date)
from calendar
where day_of_week = 'Sun'
and cal_date <= current_date)
and (select min(cal_date)
from calendar
where day_of_week = 'Sat'
and cal_date >= current_date)
)
select cal_date
from current_week
where day_of_week = 'Fri'
and day_of_week_ordinal = 3;同样,它不返回任何行;原因相同。
第二种选择是定义您自己的week_number列来替换我的iso_week列。然后你就可以像上面的第一个一样表达你的查询了。
PostgreSQL中日历表的DDL。DDL是标准SQL;但是我不确定iso_year和iso_week上的CHECK约束是否是标准SQL。添加适合您的环境的索引。
包括一个用于填充表的函数。应该很容易移植到Oracle。
create table calendar (
cal_date date primary key,
year_of_date integer not null
check (year_of_date = extract(year from cal_date)),
month_of_year integer not null
check (month_of_year = extract(month from cal_date)),
day_of_month integer not null
check (day_of_month = extract(day from cal_date)),
day_of_week char(3) not null
check (day_of_week =
case when extract(dow from cal_date) = 0 then 'Sun'
when extract(dow from cal_date) = 1 then 'Mon'
when extract(dow from cal_date) = 2 then 'Tue'
when extract(dow from cal_date) = 3 then 'Wed'
when extract(dow from cal_date) = 4 then 'Thu'
when extract(dow from cal_date) = 5 then 'Fri'
when extract(dow from cal_date) = 6 then 'Sat'
end),
day_of_week_ordinal integer not null
check (day_of_week_ordinal =
case
when day_of_month >= 1 and day_of_month <= 7 then 1
when day_of_month >= 8 and day_of_month <= 14 then 2
when day_of_month >= 15 and day_of_month <= 21 then 3
when day_of_month >= 22 and day_of_month <= 28 then 4
else 5
end),
iso_year integer not null
check (iso_year = extract(isoyear from cal_date)),
iso_week integer not null
check (iso_week = extract(week from cal_date))
);
CREATE OR REPLACE FUNCTION insert_range_into_calendar(from_date date, to_date date)
RETURNS void AS
$BODY$
DECLARE
this_date date := from_date;
BEGIN
while (this_date <= to_date) LOOP
INSERT INTO calendar (cal_date, year_of_date, month_of_year, day_of_month, day_of_week, day_of_week_ordinal, iso_year, iso_week)
VALUES (this_date, extract(year from this_date), extract(month from this_date), extract(day from this_date),
case when extract(dow from this_date) = 0 then 'Sun'
when extract(dow from this_date) = 1 then 'Mon'
when extract(dow from this_date) = 2 then 'Tue'
when extract(dow from this_date) = 3 then 'Wed'
when extract(dow from this_date) = 4 then 'Thu'
when extract(dow from this_date) = 5 then 'Fri'
when extract(dow from this_date) = 6 then 'Sat'
end,
case when extract(day from this_date) between 1 and 7 then 1
when extract(day from this_date) between 8 and 14 then 2
when extract(day from this_date) between 15 and 21 then 3
when extract(day from this_date) between 22 and 28 then 4
when extract(day from this_date) > 28 then 5
end,
cast(extract(isoyear from this_date) as integer),
cast(extract(week from this_date) as integer));
this_date = this_date + interval '1 day';
end loop;
END;
$BODY$
LANGUAGE plpgsql 发布于 2012-08-22 10:20:30
可能有更好的方法,考虑到你没有任何类型的calendar表,如果第四个星期五在当前周,下面的查询将返回'Y‘-
Query week启动Sunday并结束Saturday
SELECT 'Y' FROM
(
SELECT max(to_date(month||' '||fri,'MON YYYY DD')) DT FROM (
SELECT LPAD( MONTH, 20-(20-LENGTH(MONTH))/2 ) MONTH,Sun, Mon, Tue,
Wed, Thu, Fri, Sat
FROM (SELECT TO_CHAR(dt,'fmMonthfm YYYY') MONTH,TO_CHAR(dt+1,'iw') week,
MAX(DECODE(TO_CHAR(dt,'d'),'1',LPAD(TO_CHAR(dt,'fmdd'),2))) Sun,
MAX(DECODE(TO_CHAR(dt,'d'),'2',LPAD(TO_CHAR(dt,'fmdd'),2))) Mon,
MAX(DECODE(TO_CHAR(dt,'d'),'3',LPAD(TO_CHAR(dt,'fmdd'),2))) Tue,
MAX(DECODE(TO_CHAR(dt,'d'),'4',LPAD(TO_CHAR(dt,'fmdd'),2))) Wed,
MAX(DECODE(TO_CHAR(dt,'d'),'5',LPAD(TO_CHAR(dt,'fmdd'),2))) Thu,
MAX(DECODE(TO_CHAR(dt,'d'),'6',LPAD(TO_CHAR(dt,'fmdd'),2))) Fri,
MAX(DECODE(TO_CHAR(dt,'d'),'7',LPAD(TO_CHAR(dt,'fmdd'),2))) Sat
FROM ( SELECT TRUNC(SYSDATE,'y')-1+ROWNUM dt
FROM all_objects
WHERE ROWNUM <= ADD_MONTHS(TRUNC(SYSDATE,'y'),12) - TRUNC(SYSDATE,'y'))
GROUP BY TO_CHAR(dt,'fmMonthfm YYYY'), TO_CHAR( dt+1, 'iw' ))
ORDER BY TO_DATE( MONTH, 'Month YYYY' ), TO_NUMBER(week))
where to_char(to_date(month,'MON YYYY'),'MON YYYY') = to_char(sysdate,'MON YYYY')
and fri is not null
and rownum <= 4) a
where a.dt BETWEEN sysdate+(8 - to_char(sysdate,'d'))-7
AND sysdate+(7 - to_char(sysdate,'d'));要检查第N天-只需替换rownum <= N并将and fri is not null子句更改为and <your_day> is not null,将SELECT to_date(month||' '||fri,'MON YYYY DD') DT FROM ..更改为SELECT to_date(month||' '||<your_day>,'MON YYYY DD') DT FROM ..。
因此,只使用day和N值的动态查询就可以得到所需的结果。喜欢
SELECT 'Y' FROM
(
SELECT max(to_date(month||' '||:day,'MON YYYY DD')) DT FROM (
SELECT LPAD( MONTH, 20-(20-LENGTH(MONTH))/2 ) MONTH,Sun, Mon, Tue,
Wed, Thu, Fri, Sat
FROM (SELECT TO_CHAR(dt,'fmMonthfm YYYY') MONTH,TO_CHAR(dt+1,'iw') week,
MAX(DECODE(TO_CHAR(dt,'d'),'1',LPAD(TO_CHAR(dt,'fmdd'),2))) Sun,
MAX(DECODE(TO_CHAR(dt,'d'),'2',LPAD(TO_CHAR(dt,'fmdd'),2))) Mon,
MAX(DECODE(TO_CHAR(dt,'d'),'3',LPAD(TO_CHAR(dt,'fmdd'),2))) Tue,
MAX(DECODE(TO_CHAR(dt,'d'),'4',LPAD(TO_CHAR(dt,'fmdd'),2))) Wed,
MAX(DECODE(TO_CHAR(dt,'d'),'5',LPAD(TO_CHAR(dt,'fmdd'),2))) Thu,
MAX(DECODE(TO_CHAR(dt,'d'),'6',LPAD(TO_CHAR(dt,'fmdd'),2))) Fri,
MAX(DECODE(TO_CHAR(dt,'d'),'7',LPAD(TO_CHAR(dt,'fmdd'),2))) Sat
FROM ( SELECT TRUNC(SYSDATE,'y')-1+ROWNUM dt
FROM all_objects
WHERE ROWNUM <= ADD_MONTHS(TRUNC(SYSDATE,'y'),12) - TRUNC(SYSDATE,'y'))
GROUP BY TO_CHAR(dt,'fmMonthfm YYYY'), TO_CHAR( dt+1, 'iw' ))
ORDER BY TO_DATE( MONTH, 'Month YYYY' ), TO_NUMBER(week))
where to_char(to_date(month,'MON YYYY'),'MON YYYY') = to_char(sysdate,'MON YYYY')
and :day is not null
and rownum <= :num) a
where a.dt BETWEEN sysdate+(8 - to_char(sysdate,'d'))-7
AND sysdate+(7 - to_char(sysdate,'d'));如果您仔细查看,您会发现子查询中有一个完整的日历。这是-
SELECT LPAD( MONTH, 20-(20-LENGTH(MONTH))/2 ) MONTH,Sun, Mon, Tue,
Wed, Thu, Fri, Sat
FROM (SELECT TO_CHAR(dt,'fmMonthfm YYYY') MONTH,TO_CHAR(dt+1,'iw') week,
MAX(DECODE(TO_CHAR(dt,'d'),'1',LPAD(TO_CHAR(dt,'fmdd'),2))) Sun,
MAX(DECODE(TO_CHAR(dt,'d'),'2',LPAD(TO_CHAR(dt,'fmdd'),2))) Mon,
MAX(DECODE(TO_CHAR(dt,'d'),'3',LPAD(TO_CHAR(dt,'fmdd'),2))) Tue,
MAX(DECODE(TO_CHAR(dt,'d'),'4',LPAD(TO_CHAR(dt,'fmdd'),2))) Wed,
MAX(DECODE(TO_CHAR(dt,'d'),'5',LPAD(TO_CHAR(dt,'fmdd'),2))) Thu,
MAX(DECODE(TO_CHAR(dt,'d'),'6',LPAD(TO_CHAR(dt,'fmdd'),2))) Fri,
MAX(DECODE(TO_CHAR(dt,'d'),'7',LPAD(TO_CHAR(dt,'fmdd'),2))) Sat
FROM ( SELECT TRUNC(SYSDATE,'y')-1+ROWNUM dt
FROM all_objects
WHERE ROWNUM <= ADD_MONTHS(TRUNC(SYSDATE,'y'),12) - TRUNC(SYSDATE,'y'))
GROUP BY TO_CHAR(dt,'fmMonthfm YYYY'), TO_CHAR( dt+1, 'iw' ))
ORDER BY TO_DATE( MONTH, 'Month YYYY' ), TO_NUMBER(week);https://stackoverflow.com/questions/12063680
复制相似问题