Home > front end >  Generate multiples rows of new column based on one value of another column
Generate multiples rows of new column based on one value of another column

Time:10-22

I have a table like below:

ID Date
1 2022-01-01
2 2022-03-21

I want to add a new column based on the date and it should look like this

ID Date NewCol
1 2022-01-01 2022-02-01
1 2022-01-01 2022-03-01
1 2022-01-01 2022-04-01
1 2022-01-01 2022-05-01
2 2022-03-21 2022-04-21
2 2022-03-21 2022-05-21

Let's say that there is a @EndDate = 2022-05-31 (that's where it should stop)

I'm having a hard time trying to figure out how to do it in SSMS. Would appreciate any insights! Thanks :)

CodePudding user response:

In the following solutions we leverage string_split with combination with replicate to generate new records.

select    ID
         ,Date
         ,dateadd(month, row_number() over(partition by ID  order by (select null)), Date) as NewCol
from     (
         select   *
         from     t
         outer apply string_split(replicate(',',datediff(month, Date, '2022-05-31')-1),',')
         ) t
ID Date NewCol
1 2022-01-01 2022-02-01
1 2022-01-01 2022-03-01
1 2022-01-01 2022-04-01
1 2022-01-01 2022-05-01
2 2022-03-21 2022-04-21
2 2022-03-21 2022-05-21

Fiddle

For SQL in Azure and SQL Server 2022 we have a cleaner solution based on [ordinal][4].

"The enable_ordinal argument and ordinal output column are currently supported in Azure SQL Database, Azure SQL Managed Instance, and Azure Synapse Analytics (serverless SQL pool only). Beginning with SQL Server 2022 (16.x) Preview, the argument and output column are available in SQL Server."

select    ID
         ,Date
         ,dateadd(month, ordinal, Date) as NewCol
from     (
         select   *
         from     t
         outer apply string_split(replicate(',',datediff(month, Date, '2022-05-31')-1),',',1)
         ) t

CodePudding user response:

There are many ways to "explode" a row into a set, the simplest in my opinion is a recursive CTE:

DECLARE @endpoint date = '20220531';

DECLARE @prev date = DATEADD(MONTH, -1, @endpoint);

WITH x AS
(
  SELECT ID, date, NewCol = DATEADD(MONTH, 1, date) FROM #d
  UNION ALL
  SELECT ID, date, DATEADD(MONTH, 1, NewCol) FROM x
    WHERE NewCol < @prev
)
SELECT * FROM x
  ORDER BY ID, NewCol;

Working example in this fiddle.

Keep in mind that if you could have > 100 months you'll need to add OPTION (MAXRECURSION) (or just consider using a different solution at scale).

  • Related