Разделить одну запись на несколько строк на основе дат.Процедура в Курсоре отправлена ​​ошибка - PullRequest
0 голосов
/ 19 июня 2019

Я пытаюсь разбить один ряд на несколько, в зависимости от некоторых дат. Я пытался создать процедуру внутри курсора, но не удалось. Кажется, я не объявляю две переменные, но как мне решить проблему?

У меня есть оригинальная таблица (называемая 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. Вот как должна выглядеть таблица 3:

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> = @iversary, чтобы судить, находится ли дата наблюдения в пределах каждого интервала в таблице2 (но я хочу создать Таблицу напрямую из таблицы 1, без создания избыточной таблицы 2). Если нет, просто вставьте запись, как раньше; иначе вставьте запись дважды, один начинается с даты начала, но заканчивается датой наблюдения; следующий начинается с даты наблюдения, но заканчивается в дату окончания.

Я попробовал код следующим образом, но не смог.

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

Сообщение об ошибке выглядит следующим образом:

Неверный синтаксис рядом с ключевым словом «процедура».
Необходимо объявить скалярную переменную "@begindt".
Необходимо объявить скалярную переменную "@stopdt".

1 Ответ

0 голосов
/ 19 июня 2019

РЕДАКТИРОВАТЬ - После понимания того, что вы на самом деле пытались достичь, я обновил запрос, который я написал.

Ниже приведен сценарий, воссозданный без процедуры создания и без курсора. Как правило, если вам когда-нибудь кажется, что вам нужен курсор на сервере sql, вы, вероятно, ошибаетесь в 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...