T-SQL: Splitting one row into many

Something for your SQL toolbox…

I was recently challenged with needing to split a row into multiple rows (a quantity across multiple dates).

We were using a CURSOR, but the performance was horrible. When I converted it to a recursive CTE it went from 6+ minutes to a few seconds!

Below was my “theorem proof” before I implemented the final solution…

*Note: PIVOT would be used to split a row into multiple columns rather than rows.

Links:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
DECLARE @RowsToSplit TABLE (
      Id INT IDENTITY(1, 1) NOT NULL
      -- how many rows are desired after the split?
      , MaxRows INT NOT NULL
      -- what value to split across the rows?
      , QuantityToSplit INT NOT NULL
);
 
INSERT INTO @RowsToSplit VALUES ( 20, 100 );
INSERT INTO @RowsToSplit VALUES ( 5, 15 );
 
-- Recursive Common Table Expression (CTE)
WITH SplitRowsCTE ( Id, MaxRows, QuantityToSplit, Quantity, RowIndex )
AS (
      -- "Anchor" Set: The root set of the recursive function.
      SELECT 
            Id -- row tracking
            , MaxRows -- remember the goal # of rows
            , QuantityToSplit -- remember the goal value to split
            -- split the first row's quantity
            , QuantityToSplit / MaxRows AS Quantity
            -- start at index 1
            , 1 AS RowIndex
      FROM @RowsToSplit -- anchor source
      -- Recursive Set: Iterate from the source until all rows have been generated.
      UNION ALL
      SELECT
            Id -- row tracking
            , MaxRows -- remember the goal # of rows
            , QuantityToSplit -- remember the goal value to split
            -- split the row's quantity
            , QuantityToSplit / MaxRows AS Quantity
            -- increment the row index
            , RowIndex + 1 AS RowIndex
      FROM SplitRowsCTE
      -- stop just before we get to the max rows (root has already been created)
      WHERE RowIndex < MaxRows
)
 
-- Call the recursive CTE function
-- Eliminate the source information from the result set
SELECT Id, RowIndex, Quantity
FROM SplitRowsCTE
ORDER BY Id, RowIndex;

Leave a Reply