是否可以根据列值复制行并更改另一个列值?

时间:2019-05-28 18:04:55

标签: sql-server

我有一个表,其中包含以秒为单位的INT时间列,具有该记录类型的另一列以及一个外部ID。

我想选择同一行,只要它的秒行大于X,并且在第一行中将X保留为类型1的X秒,在第二行中则显示剩余的秒(秒-X,直至X)的类型2。与Y相同。最多3行。因此,第1行最多x,第2行最多Y,第3行是Y + 1及以上

例如:

X为5。Y为9。 我想要这个:

| id | type | seconds |
|----|------|---------|
|  1 |  10  |   19    |
|  2 |  10  |   12    |
|  3 |  10  |    7    |

成为这个:

| 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    |

有可能吗?

我已经看到专门针对oracle的解决方案(语法-)。 但是如何在SQL Server中做到这一点?

编辑:仅对此类型的id(在示例中为10),其他参数保持不变。

2 个答案:

答案 0 :(得分:1)

这不是很漂亮,但是可以解决问题。要复制行,我们使用计数表。在这种情况下,我使用了只有3行的硬编码代码。然后基于此进行计算。可能有一种更简单的编码方式,但是我今天还没尽力。

--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;

答案 1 :(得分:0)

对于这种类型的问题,我喜欢使用recursive CTE。从@Luis Cazares借用了额外的测试用例。还添加了您在注释中提到的类型15的测试用例。在下面的解决方案中,您可以交换X和Y的值,它将相应地进行计算。

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;