Reputation: 59
I have a table with an INT column of time in seconds, another column with the type of that record, and a foreign ID.
I want to select the same row repeated everytime it's seconds row is greater than X, and in the first row it keeps X seconds with type 1, and in the second row it shows the seconds left (seconds - X, up to X) with type 2. The same with Y. No more than 3 rows. So row 1 is up to x, row 2 up to Y and row 3 is Y+1 and beyond
Eg:
X is 5. Y is 9. I want this:
| id | type | seconds |
|----|------|---------|
| 1 | 10 | 19 |
| 2 | 10 | 12 |
| 3 | 10 | 7 |
to become this:
| id | type | seconds |
|----|------|---------|
| 1 | 1 | 5 |
| 1 | 2 | 4 |
| 1 | 3 | 10 |
| 2 | 1 | 5 |
| 2 | 2 | 4 |
| 2 | 3 | 3 |
| 3 | 1 | 5 |
| 3 | 2 | 2 |
Is that possible?
I've seen solutions to this (syntax-) exclusively for oracle. But how do I do this in SQL Server?
Edit: Only for this type id (10 in the example), letting others unchanged.
Upvotes: 0
Views: 892
Reputation: 2507
For this type of problem I like to use a recursive CTE. Borrowed the extra test cases from @Luis Cazares. Also added test cases for type 15 you mentioned in comments. In the solution below you can swap out values of X and Y and it will calculate accordingly.
CREATE TABLE #TestData
(
id INT
, type INT
, seconds int
);
INSERT INTO #TestData VALUES
(1, 10, 19)
, (2, 10, 12)
, (3, 10, 7)
, (4, 10, 5)
, (5, 10, 3)
, (6, 15, 54)
, (7, 15, 8);
DECLARE
@X INT = 5
, @Y INT = 9;
SET @Y = @Y - @X;
WITH CTE AS
(
SELECT id, CASE WHEN type = 10 THEN 0 ELSE type END AS type, seconds, seconds AS ov
FROM #TestData
UNION ALL
SELECT
id
, type + 1
, CASE
WHEN type = 0 THEN seconds - @X
WHEN type = 1 THEN seconds - @Y
WHEN type = 2 THEN seconds
END
, ov
FROM CTE
WHERE seconds > 0
)
SELECT
id
, type
, CASE
WHEN type = 1 AND seconds > 0 THEN @X
WHEN type = 2 AND seconds > 0 THEN @Y
WHEN type = 2 AND seconds < 0 THEN ov - @X
WHEN type = 3 AND seconds > 0 THEN seconds
ELSE ov
END
FROM CTE
WHERE type <> 0 AND CTE.seconds IS NOT NULL
ORDER BY id, type
DROP TABLE #TestData;
Upvotes: 0
Reputation: 3585
This is not very pretty, but does the trick. To duplicate rows, we use tally tables. In this case I used a hard coded one with only 3 rows. Then made the calculations based on it. There might be a simpler way to code it, but I'm not on my best today.
--Creating sample data
CREATE TABLE SampleData(
id int,
[type] int,
seconds int
);
INSERT INTO SampleData
VALUES
( 1, 10, 19),
( 2, 10, 12),
( 3, 10, 7),
( 4, 10, 5),
( 5, 10, 3);
GO
--Actual solution
DECLARE @X int = 5;
WITH CTE AS(
SELECT id,
CASE WHEN [type] = 10 THEN n ELSE [type] END AS [type],
CASE WHEN [type] <> 10 THEN seconds
WHEN n = 1 AND seconds > @X THEN @X
WHEN n = 1 AND seconds <= @X THEN seconds
WHEN n = 2 AND seconds - @X >= @X THEN @X-1
WHEN n = 2 AND seconds - @X > 0 THEN seconds - @X
WHEN n = 3 AND seconds > @X*2-1 THEN seconds - (@X*2-1)
END AS seconds
FROM SampleData
CROSS JOIN( VALUES(1),(2),(3))AS x(n)
WHERE [type] = 10 OR n = 1
)
SELECT *
FROM CTE
WHERE seconds IS NOT NULL;
Upvotes: 1