根据日期将一条记录分成多行。游标中的程序发送错误

时间:2019-06-19 01:50:39

标签: sql sql-server

我正在尝试根据某些日期将一行分成几部分。我试图在游标中创建一个过程,但是失败了。似乎我没有声明两个变量,但是应该如何解决这个问题?

我有一个原始表(称为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”。

1 个答案:

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