[英]Remove Nulls from multiple rows; create [Start] and [End] columns
I'm trying to create a table that has start and end columns by week that dont overlap month transitions. 我正在尝试创建一个表,该表具有按周开始和结束的列,并且不与月份转换重叠。 Using January 2016 as an example, I want the results to look like: 以2016年1月为例,我希望结果看起来像:
Start End
1/1/2016 1/2/2016
1/3/2016 1/9/2016
1/10/2016 1/16/2016
1/17/2016 1/23/2016
1/24/2016 1/30/2016
1/31/2016 1/31/2016
What I'm currently getting with the query is (I want the records in the 2nd and 3rd columns to line up accordingly): 我现在通过查询得到的是(我希望第二列和第三列中的记录相应地对齐):
DATES Wk_START_END MONTH_START_END
1/1/2016 1/1/2016
1/2/2016 1/2/2016
1/3/2016 1/3/2016
1/4/2016
1/5/2016
1/6/2016
1/7/2016
1/8/2016
1/9/2016 1/9/2016
1/10/2016 1/10/2016
1/11/2016
1/12/2016
1/13/2016
1/14/2016
1/15/2016
1/16/2016 1/16/2016
1/17/2016 1/17/2016
1/18/2016
1/19/2016
1/20/2016
1/21/2016
1/22/2016
1/23/2016 1/23/2016
1/24/2016 1/24/2016
1/25/2016
1/26/2016
1/27/2016
1/28/2016
1/29/2016
1/30/2016 1/30/2016
1/31/2016 1/31/2016 1/31/2016
Here's the query at the moment: 此刻的查询:
SELECT trunc
(sysdate, 'YEAR')+rownum-1 DATES
--,to_char(trunc(sysdate,'YEAR') + rownum -1 ,'D') Day_Of_Wk
, CASE
WHEN to_char
(trunc
(sysdate, 'YEAR')+rownum-1, 'D') = '1' THEN trunc
(sysdate, 'YEAR')+rownum-1
WHEN to_char
(trunc
(sysdate, 'YEAR')+rownum-1, 'D') = '7' THEN trunc
(sysdate, 'YEAR')+rownum-1
ELSE NULL
END Wk_Start_End
, CASE
WHEN trunc
(sysdate, 'YEAR')+rownum-1 = TRUNC
(trunc
(sysdate, 'YEAR')+rownum-1, 'MONTH') THEN trunc
(sysdate, 'YEAR')+rownum-1
WHEN trunc
(sysdate, 'YEAR')+rownum-1 = Add_months
(TRUNC
(trunc
(sysdate, 'YEAR')+rownum-1, 'MONTH'), 1)-1 THEN trunc
(sysdate, 'YEAR')+rownum-1
END Month_Start_end
FROM all_objects
WHERE trunc
(sysdate, 'YEAR')+rownum <= Add_months
(trunc
(sysdate, 'YEAR'), 12)-1;
Any help is appreciated. 任何帮助表示赞赏。 Thanks! 谢谢!
Hmmm. 嗯 You seem to want odd-numbered rows with values, along with the next non-null value. 您似乎想要带值的奇数行,以及下一个非空值。 I'm thinking: 我在想:
select dte as start_date, next_dte and end_date
from (select ao.*, rownum as seqnum, lead(dte) over (order by dates) as next_dte
from ((select dates, Wk_START_END as dte from all_objects ao) union all
(select dates, MONTH_START_END as dte from all_objects ao)
) t
where dte is not null
) t
where mod(seqnum, 2) = 1;
Here is a query that will do it for all 2016 dates. 这是一个查询,它将针对所有2016年日期执行此操作。 You can extrapolate it for your purposes, I hope. 我希望您可以根据自己的目的进行推断。
The basic approach is to get all the dates in the range of interest (say, all of 2016), then group them by month/week. 基本方法是获取感兴趣范围内的所有日期(例如,2016年全年),然后按月/周分组。 Then get the min()
and max()
date in each group. 然后获取每个组中的min()
和max()
日期。
WITH dtes AS
(SELECT TO_DATE ('01-JAN-2016') + ROWNUM - 1 dte
FROM DUAL
CONNECT BY ROWNUM <= 365),
dtes_grouped_by_month_week AS
(SELECT dte,
DENSE_RANK () OVER (PARTITION BY NULL ORDER BY TO_CHAR (dte, 'IYYY-MM'), TO_CHAR (dte, 'IW')) month_week
FROM dtes)
SELECT MIN (dte) start_date,
MAX (dte) end_date
FROM dtes_grouped_by_month_week
GROUP BY month_week
ORDER BY month_week;
Looking at your example, you seem to want Sunday to be the 1st day of the week instead of Monday. 看您的示例,您似乎希望星期日是一周的第一天,而不是星期一。 That's easily adjusted for in the query -- just add +1 to the date in the TO_CHAR
functions in the DENSE_RANK
. 这在查询中很容易进行调整-只需在DENSE_RANK
的TO_CHAR
函数中的日期上加上+1。
The query below starts from scratch - it doesn't use any of your code (or its output). 下面的查询从头开始-它不使用任何代码(或其输出)。 The year and month are hard-coded in the first CTE (subfactored query in the WITH clause at the top); 年和月在第一个CTE中进行硬编码(顶部的WITH子句中的子查询); more likely in your application you will exclude the first CTE, named inputs
, and you will make y
and m
into bind variables in the definition of first_date
(also in the WITH clause). 在您的应用程序中,您更有可能会排除第一个CTE(名为inputs
,并且会在first_date
的定义中(也在WITH子句中)使y
和m
成为绑定变量。
I used your convention: the week starts on "day 1 of the week" (which in the US is Sunday) and ends on "day 7 of the week." 我使用了您的约定:一周从“一周的第一天”开始(在美国为星期日),到“一周的第七天”结束。 This can be adjusted through NLS parameters if needed. 如果需要,可以通过NLS参数进行调整。
with
inputs ( y, m ) as (
select 2016, 1 from dual
),
first_date ( f_dt ) as (
select to_date(to_char(y, '0009') || '-' || to_char(m, '09'), 'yyyy-mm')
from inputs
),
mth_dates ( dt ) as (
select f_dt + level - 1 from first_date
connect by level <= last_day(f_dt) - f_dt + 1
),
start_dates ( dt, rn ) as (
select dt, row_number() over (order by dt)
from ( select dt from mth_dates where to_char(dt, 'd') = '1'
union
select min(dt) from mth_dates )
),
end_dates ( dt, rn ) as (
select dt, row_number() over (order by dt)
from ( select dt from mth_dates where to_char(dt, 'd') = '7'
union
select max(dt) from mth_dates )
)
select s.rn as week_nbr, s.dt as start_date, e.dt as end_date
from start_dates s inner join end_dates e on s.rn = e.rn;
WEEK_NBR START_DATE END_DATE
---------- ---------- ----------
1 2016-01-01 2016-01-02
2 2016-01-03 2016-01-09
3 2016-01-10 2016-01-16
4 2016-01-17 2016-01-23
5 2016-01-24 2016-01-30
6 2016-01-31 2016-01-31
ADDED at OP's request: 添加在OP的要求:
To generate the start and end dates for the entire year one can use the query below. 要生成全年的开始日期和结束日期,可以使用以下查询。
with
inputs ( y ) as (
select 2016 from dual
),
first_date ( f_dt ) as (
select to_date(to_char(y, '0009') || '-01-01', 'yyyy-mm-dd')
from inputs
),
year_dates ( dt ) as (
select f_dt + level - 1 from first_date
connect by level <= add_months(f_dt, 12) - f_dt
),
start_dates ( dt, rn ) as (
select dt, row_number() over (order by dt)
from ( select dt from year_dates where to_char(dt, 'd') = '1'
or extract(day from dt) = 1 )
),
end_dates ( dt, rn ) as (
select dt, row_number() over (order by dt)
from ( select dt from year_dates where to_char(dt, 'd') = '7'
or extract(day from dt + 1) = 1 )
)
select s.dt as start_date, e.dt as end_date
from start_dates s inner join end_dates e on s.rn = e.rn;
Further comment : I actually like Matthew's answer better than mine. 进一步说明 :实际上,我比我更喜欢马修的回答。 His solution simply groups the days into the proper "set intersections" of months and weeks and uses max()
and min()
over those groups, avoiding the need for a join. 他的解决方案只是将日期分为几个月和几周的适当“集合交集”,并在这些组上使用max()
和min()
,从而避免了连接的需要。 It's a better solution than mine. 这是比我更好的解决方案。
For completeness, I reproduce Matthew's solution below, with a few minor changes. 为了完整起见,我在下面复制了Matthew的解决方案,但做了一些小的更改。
First, to match the requirement (and as Matthew suggested), I add 1 to dte
in forming the groups by week, so weeks begin on Sundays and end on Saturdays. 首先,为了满足要求(并如Matthew所建议),我在dte
中加1来按周形成组,因此,几周从星期日开始,到星期六结束。
Second, as I suggested in a comment to Matthew's Answer, I use "month" and "week" directly to form the groups; 第二,正如我在对《马修答案》的评论中所建议的那样,我直接使用“月”和“周”来组成小组。 there is no need for dense_rank()
. 不需要dense_rank()
。
Third, to conform with good coding practices, I added an explicit date format model to to_date()
in the first CTE. 第三,为了遵循良好的编码习惯,我在第一个CTE中向to_date()
添加了一个明确的日期格式模型。
Credit : @Matthew McPeak 图片来源 :@Matthew McPeak
with dtes ( dte ) as (
select to_date ('01-Jan-2016', 'dd-Mon-yyyy') + rownum - 1
from dual
connect by rownum <= 366 -- 2016 is a leap year
),
dtes_grouped_by_month_week ( dte, mth, wk ) as (
select dte, to_char(dte, 'mm'), to_char(dte+1, 'iw')
from dtes
)
select min(dte) start_date, max(dte) end_date
from dtes_grouped_by_month_week
group by mth, wk
order by start_date;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.