У меня есть таблица, содержащая 4 столбца: Project, ScenarioStart, ScenarioEnd и TimePeriods.
Поначалу поле TimePeriods всегда пусто, так как именно здесь мне нужно заполнить строку всех применимых периодов времени для проекта (включая начало и конец).
Все периоды времени имеют формат ггггМм.
например
Project ScenarioStart ScenarioEnd TimePeriods
1 2024M9 2025M3
2 2024M6 2024M10
Теперь мне нужно обновить таблицу, добавив в нее текстовую строку, содержащую все применимые периоды для каждой строки. (Я использую разделение труб)
Таким образом, строка 1 будет выглядеть так: «2024M9|2024M10|2024M11|2024M12|2025M1|2025M2|2025M3».
Строка 2 должна быть «2024M6|2024M7|2024M8|2024M9|2024M10».
У меня уже есть рабочий код для генерации строки, но у него есть две проблемы:
Код для генерации строки приведен ниже. Может кто-нибудь помочь мне преобразовать его в обновление для каждой строки таблицы?
-- Create the variables to hold Start- and End-Period, and the blank Period List --
-- Currently only works if hard-coded, but should read the table cells)
declare @sScenarioStart nvarchar(10) = '2024M1',
@sScenarioEnd nvarchar(10) = '2026M3',
@SplitPeriods nvarchar(max) = ''
----------------------------------------------------------------
-- Create a list of valid time references for this submission --
----------------------------------------------------------------
Declare @sCounterTime nvarchar(10) = @sScenarioStart,
@sCounterYear nvarchar(4) = substring(@sScenarioStart,1,4),
@sCounterMonth nvarchar(4) = substring(@sScenarioStart,6,len(@sScenarioStart)-5)
print 'Counter Time: ' + @sCounterTime + 'Counter Year: ' + @sCounterYear + 'Counter Month: ' + @sCounterMonth
-- Generate the comma-sparated list of time periods --
While @sCounterTime <> @sScenarioEnd
BEGIN
-- Add the initial Time Period to the output string
set @SplitPeriods = @SplitPeriods + @sCounterTime + '|'
-- If Current month is December then +1 the year and reset the month back to 1
set @sCounterYear = case @sCounterMonth
when 12 then @sCounterYear + 1
else @sCounterYear
end
set @sCounterMonth = case @sCounterMonth
when 12 then 1
else @sCounterMonth + 1
end
-- Move the Time Period forward
set @sCounterTime = @sCounterYear + 'M' + @sCounterMonth
END -- End of BEGIN statement
-- Add the final Time Period to the string
set @SplitPeriods = @SplitPeriods + @sCounterTime
-- TESTING: Output the final string
print 'Final Period String: ' + @SplitPeriods
Вывод этого скрипта:
Время счетчика: 2024M1Год счетчика: 2024Месяц счетчика: 1
Строка конечного периода: 2024M1|2024M2|2024M3|2024M4|2024M5|2024M6|2024M7|2024M8|2024M9|2024M10|2024M11|2024M12|2025M1|2025M2|2025M3|2025M4|2 025М5|2025М6|2025М7|2025М8|2025М9|2025М10|2025М11| 2025М12|2026М1|2026М2|2026М3
Оба этих вывода верны, но второй необходимо обновить в столбце TimePeriods для каждой строки.
Спасибо
Этот ответ на вопрос может помочь stackoverflow.com/questions/39782311/…
Решение было бы проще, если бы ScenarioStart и ScenarioEnd были преобразованы в даты.
Не храните строки с разделителями в реляционной базе данных. datatechnologytoday.wordpress.com/2010/07/13/… или github.com/boralp/…
Я поместил ваши записи в таблицу примеров. Затем я преобразовал ScenarioStart и ScenarioEnd в даты. Затем я смог использовать рекурсивный CTE для генерации MonthDates. Я объединил MonthDates, используя String_Agg. Затем я обновил поле TimePeriods в таблице примера.
CREATE TABLE Example
(
Project VARCHAR(512),
ScenarioStart VARCHAR(512),
ScenarioEnd VARCHAR(512),
TimePeriods VARCHAR(512)
);
INSERT INTO Example (Project, ScenarioStart, ScenarioEnd) VALUES
('1', '2024M9', '2025M3'),
('2', '2024M6', '2024M10');
SELECT * FROM Example;
-- Recursive CTE to generate a list of months within the date range:
WITH Months AS (
SELECT Example.Project, CONVERT(DATE, DATEADD(D, -(DAY(CONVERT(Date, Substring(ScenarioStart,1,4) + '-' + Substring(ScenarioStart,6,2) + '-1'))) + 1, CONVERT(Date, Substring(ScenarioStart,1,4) + '-' + Substring(ScenarioStart,6,2) + '-1'))) [MonthDate]
FROM Example
UNION ALL
SELECT Example.Project, DATEADD(M, 1, MonthDate)
FROM Months
INNER JOIN Example on Example.Project=Months.Project
WHERE MonthDate <= DATEADD(M, -1, CONVERT(Date,Substring(Example.ScenarioEnd,1,4) + '-' + Substring(Example.ScenarioEnd,6,2) + '-1'))
)
UPDATE Example
SET Example.TimePeriods=t.TimePeriods
FROM Example E
INNER JOIN
(
--Combine MonthDates using STRING_AGG to make TimePeriods
SELECT Project, STRING_AGG(CAST(Year(MONTHDATE) as Varchar(4))+'M'+Cast(Month(Monthdate) as varchar(2)), '|') as TimePeriods
FROM Months
GROUP BY Project
) t
ON E.Project=t.Project
SELECT * FROM Example;
См. пример:
project_lng
в подзапросе t1).with t1 as(
select ProjectId,y1,m1 --,*
,(y2-y1)*12+m2-m1 project_lng
from (
select ProjectId --,*
,cast(substring(ScenarioStart,1,4) as int) y1
,cast(substring(ScenarioStart,6,2) as int) m1
,cast(substring(ScenarioEnd,1,4) as int) y2
,cast(substring(ScenarioEnd,6,2) as int) m2
from projects
) t
)
,expanded_periods as(
select ProjectId --,*
,y1+(m1+n-1)/12 yy
,(13-(12-(m1+n-1)%12)) mm
from t1
cross apply (select value n from generate_series(0,t1.project_lng,1) s1)nn
)
update projects
set TimePeriods=TimePeriodsNew
from (
select ProjectId --,ScenarioStart,ScenarioEnd
,string_agg(concat(yy,'M',mm),'|')within group (order by yy,mm) TimePeriodsNew
from expanded_periods
group by ProjectId --,ScenarioStart,ScenarioEnd
)src
where src.ProjectId=projects.ProjectId
Ваша колонка Project
называется ProjectId
.
Выход:
Возможно, эта рабочий пример поможет нам совместно найти решение dbfiddle.uk/GCfMqWTp