Как обновить таблицу SQL, используя сгенерированную переменную в каждой строке?

У меня есть таблица, содержащая 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».

У меня уже есть рабочий код для генерации строки, но у него есть две проблемы:

  1. Он работает только с фиксированными значениями начала и конца, а не считывает каждую строку данных и использует начало и конец существующих столбцов.
  2. В настоящее время он просто создает строку как переменную, а не обновляет таблицу со значением.

Код для генерации строки приведен ниже. Может кто-нибудь помочь мне преобразовать его в обновление для каждой строки таблицы?

-- 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 для каждой строки.

Спасибо

Возможно, эта рабочий пример поможет нам совместно найти решение dbfiddle.uk/GCfMqWTp

Bart McEndree 15.04.2024 20:43

Этот ответ на вопрос может помочь stackoverflow.com/questions/39782311/…

Bart McEndree 15.04.2024 20:46

Решение было бы проще, если бы ScenarioStart и ScenarioEnd были преобразованы в даты.

Bart McEndree 15.04.2024 22:08

Не храните строки с разделителями в реляционной базе данных. datatechnologytoday.wordpress.com/2010/07/13/… или github.com/boralp/…

MatBailie 16.04.2024 00:17
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
4
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я поместил ваши записи в таблицу примеров. Затем я преобразовал 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;

рабочий пример

Проект Начало сценария Конец сценария Периоды времени 1 2024М9 2025М3 2024М9│2024М10│2024М11│2024М12│2025М1│2025М2│2025М3 2 2024М6 2024М10 2024М6│2024М7│2024М8│2024М9│2024М10

См. пример:

  1. Рассчитать год и месяц для ScenarioStart
    и продолжительность периода в месяцах (project_lng в подзапросе t1).
  2. Расширить период черезgenerate_series: 1 месяц -> 1 строка.
  3. Агрегируйте назад, объединяя месяцы.
  4. Обновить целевую таблицу.
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.

Выход:

Ид проекта Начало сценария Конец сценария Периоды времени 1 2024М9 2025М3 2024М9|2024М10|2024М11|2024М12|2025М1|2025М2|2025М3 2 2024М6 2024М10 2024М6|2024М7|2024М8|2024М9|2024М10 3 2024М6 2025М10 2024М6|2024М7|2024М8|2024М9|2024М10|2024М11|2024М12| 2025М1|2025М2|2025М3|2025М4|2025М5|2025М6|2025М7| 2025М8|2025М9|2025М10

Скрипка

Другие вопросы по теме