我正在尝试根据某些日期将一行分成几部分。我试图在游标中创建一个过程,但是失败了。似乎我没有声明两个变量,但是应该如何解决这个问题?
我有一个原始表(称为Table1),如下所示:
ID DOB Entry_date Termination_date
1 2000-6-1 2010-9-1 2012-7-1
2 2004-12-1 2011-11-20 2013-2-1
假设这些是每位加入和离开学校的学生的记录,带有生日日期。
我需要将记录分为几部分,以便计算他们每年,每个年龄和每个年级的上学天数。将此表称为Table2,我已经设法使用光标生成了该表:
ID Year Start_date End_date
1 2010 2010-1-1 2010-6-1
1 2010 2010-6-1 2010-9-1
1 2010 2010-9-1 2010-12-31
1 2011 2011-1-1 2011-6-1
1 2011 2011-6-1 2011-9-1
1 2011 2011-9-1 2011-12-31
1 2012 2012-1-1 2012-6-1
1 2012 2011-6-1 2011-7-1
2 2011 2011-1-1 2011-11-20
2 2011 2011-11-20 2011-12-1
2 2011 2011-12-1 2011-12-31
2 2012 2012-1-1 2012-11-20
2 2012 2012-11-20 2012-12-1
2 2012 2012-12-1 2012-12-31
2 2013 2013-1-1 2013-2-1
但是,现在从输入日期起还有3个月的观察期,将其称为Table3。以下是Table3的外观:
ID Year Start_date End_date
1 2010 2010-1-1 2010-6-1
1 2010 2010-6-1 2010-9-1
1 2010 2010-9-1 2010-12-1
1 2010 2010-12-1 2010-12-31
1 2011 2011-1-1 2011-6-1
1 2011 2011-6-1 2011-9-1
1 2011 2011-9-1 2011-12-31
1 2012 2012-1-1 2012-6-1
1 2012 2011-6-1 2011-7-1
2 2011 2011-1-1 2011-11-20
2 2011 2011-11-20 2011-12-1
2 2011 2011-12-1 2011-12-31
2 2012 2012-1-1 2012-2-20
2 2012 2012-2-20 2012-11-20
2 2012 2012-11-20 2012-12-1
2 2012 2012-12-1 2012-12-31
2 2013 2013-1-1 2013-2-1
我正在考虑添加一个程序来复制我所做的事情,但是在@birthday> = @ anniversary之前创建一个程序来判断观察日期是否在表2的每个间隔内(但我希望直接生成表3来自Table1,而不产生多余的Table2)。如果没有,只需像以前一样插入记录;否则插入记录两次,一个从开始日期开始,但在观察日期结束;下一个从观察日期开始,但在结束日期结束。
我尝试了如下代码,但失败了。
create table [dbo].[table3] (ID int, Year int, Start_date date, End_date date)
declare @ID int,
@DOB Date,
@Entry_date date,
@Termination_date date,
@startyr int,
@endyr int,
@birthday date,
@anniversary date,
@date1 date,
@date2 date
Declare cur1 cursor
For
Select ID, DOB, Entry_date, Termination_date
From [dbo].[Table1];
Open cur1;
fetch next from cur1 into @ID, @DOB, @Entry_date, @Termination_date;
while @@fetch_status=0
begin
set @startyr=year(@Entry_date);
set @endyr = year(@Termination_date);
set @obsdt=dateadd(day,90,@Entry_date);
while @startYr<=@EndYr
begin
set @birthday=datefromparts(@startyr,month(DOB),day(DOB));
set @anniversay= datefromparts(@startyr,month(Entry_date),day(Entry_date));
set @date1= datefromparts(@startYr,1,1);
set @date2 = case when datefromparts(@startYr,12,31)>@Termination_date then @Termination_date else datefromparts(@startYr,12,31) end;
create procedure dbo.test(@begindt date, @stopdt date)
as
if (@begindt>@ obsdt) and (@stopdt<@obsdt)
begin
insert into [dbo].[table3] (ID, Year, Start_date, End_date) values (@ID, @startyr, @ begindt, @obsdt);
insert into [dbo].[table3] (ID, Year, Start_date, End_date) values (@ID, @startyr, @ obsdt, @stopdt);
end
else
insert into [dbo].[table3] (ID, Year, Start_date, End_date) values (@ID, @startyr, @ begindt, @ stopdt);
if @birthday>=@anniversary
begin
exec [dbo].[test] @date1, @ anniversary
exec [dbo].[test] @anniversary, @birthday
exec [dbo].[test] @birthday, @date2
end
else
begin
exec [dbo].[test] @date1, @birthday
exec [dbo].[test] @birthday, @anniversary
exec [dbo].[test] @anniversary, @date2
end
set @startYr=@startYr+1
end
fetch next from cur1 into @ID, @DOB, @Entry_date, @Termination_date;
end
close cur1
go
错误消息如下:
关键字“ procedure”附近的语法不正确。
必须声明标量变量“ @begindt”。
必须声明标量变量“ @stopdt”。
答案 0 :(得分:0)
编辑-了解了您实际上试图实现的目标之后,我更新了我编写的查询。
以下是没有创建过程且没有游标的情况下重新创建的脚本。通常,如果您认为需要在sql server中使用游标,则99.9%的时间可能是错误的。
以下查询遍历多个CTE,这样做是为了使其易于理解,并且无疑会进一步简化。
您将看到,我首先将表格每年扩展为1行,然后为每行设置周年纪念日。
然后我将其扩展为每个日期一行,以便我可以对它们进行简单地排序,然后丢弃超出入学范围或该年不需要的任何日期,最后将它们配对为多个范围。
;With daterange as (
Select year(min(Entry_date)) as y1, year(max(Termination_date)) as y2 from table1
),
-- Expand the rows into 1 per year
years as
(
Select y1 as y from daterange
union all
Select y + 1
from years
where y <= (select y2 from daterange)
)
-- Now work our all the birthdays, anniversaries etc for each source row and each applicable year
, alldates as
(
Select table1.*,
dateadd(day,90,Entry_date) obsdt,
datefromparts(y,month(DOB),day(DOB)) as birthday,
datefromparts(y,month(Entry_date),day(Entry_date)) as anniversary,
datefromparts(y,1,1) date1,
datefromparts(y,12,31) date2,
y
From table1
join years on y between year(Entry_Date) and year(Termination_date)
)
-- There are 7 possible dates - year start, birthday, anniversay, obsdt, year end, entry, termination
, dates as
(
select d from (values(1),(2),(3),(4),(5),(6),(7)) as starts(d)
)
-- Split it into multiple rows so we can sort the dates
, expanded as
(
select ID, y, entry_date, termination_date,
case d
when 1 then date1
when 2 then birthday
when 3 then anniversary
when 4 then obsdt
when 5 then date2
when 6 then entry_date
when 7 then termination_date
end as dt
from alldates
cross join dates
)
-- Exclude rows that are not from the year - entry, termination, obsdt, or are outside the entry/termination dates
, validrows as
(
select distinct ID, y, dt
from expanded
where year(dt)=y
and dt>=entry_date and dt<=termination_date
)
-- Pair each ro with its next row
, pairs as
(
select *, lead(dt,1) over(partition by ID,Y order by dt) as dt2
from validrows
)
-- The final insert
insert into [dbo].[table3] (ID, Year, Start_date, End_date)
select ID, y, dt, dt2
from pairs where dt2 is not null
order by ID, y, dt
其结果是:
ID y dt dt2
1 2010 2010-09-01 2010-11-30
1 2010 2010-11-30 2010-12-31
1 2011 2011-01-01 2011-06-01
1 2011 2011-06-01 2011-09-01
1 2011 2011-09-01 2011-12-31
1 2012 2012-01-01 2012-06-01
1 2012 2012-06-01 2012-07-01
2 2011 2011-11-20 2011-12-01
2 2011 2011-12-01 2011-12-31
2 2012 2012-01-01 2012-02-18
2 2012 2012-02-18 2012-11-20
2 2012 2012-11-20 2012-12-01
2 2012 2012-12-01 2012-12-31
2 2013 2013-01-01 2013-02-01