根据记录数在SQL Server中将单列拆分为多列

时间:2019-06-20 13:50:09

标签: python sql-server tsql sql-server-2014 sql-server-2017

我在同一列中大约有28条记录(是动态的,有时记录数是奇数,有时是偶数)。

想根据一个变量将它们分成多列。每列中应该有5或6条记录。

注意:这5或6条记录是变量,应更改。

示例输入表:

           Col1
           -----
            A
            B
            C
            D
            E
            F
            G
            H
            I
            J
            K
            L
            M
            N
            O
            P
            Q
            R
            S
            T
            U
            V
            W
            X
            Y
            Z

所需的输出表将是:

            Col1    col2    col3    col4    col5
            -------------------------------------
            A        F       K       P       U
            B        G       L       Q       V
            C        H       M       R       W
            D        I       N       S       X
            E        J       O       T       Y
                                             Z

这是我尝试过的,这两个不同的查询提供了不同的结果:

            select Col1
                from Table1 where  Col1 is not null
                order by [col1] asc

--This provides all records

            WITH CTE AS
            (
            SELECT [Col1],
                   (ROW_NUMBER() OVER (ORDER BY Col1) -1)%5 AS Col,
                   (ROW_NUMBER() OVER (ORDER BY Col1) -1)/5 AS Row
            FROM Table1  where  Col1 is not null     
            )
            SELECT [0], [1], [2], [3], [4]
            FROM CTE 
            PIVOT (MAX([COL1]) FOR Col IN ([0], [1], [2], [3], [4])) AS Pvt
            ORDER BY Row

-在这种情况下,缺少记录(当记录数很高时,因为记录数可以动态增加)

            WITH CTE AS
            (
            SELECT [Col1],
                   (ROW_NUMBER() OVER (ORDER BY Col1) -1)%5 AS Col,
                   (ROW_NUMBER() OVER (ORDER BY Col1) -1)/5 AS Row
            FROM Table1  where  Col1 is not null    
            )
            SELECT [0], [1]
            FROM CTE 
            PIVOT (MAX([COL1]) FOR Col IN ([0], [1])) AS Pvt
            ORDER BY Row

我在做什么错?如何获得所需的输出?谢谢。

3 个答案:

答案 0 :(得分:3)

您需要首先为每条记录分配一列,这可以通过NTILE()完成:

WITH Table1 AS
(
    SELECT  TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
    FROM    sys.all_objects
)
SELECT [Col1],
        5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
FROM Table1 ;

给予:

Col1    Col
---------
Z       4
Y       4
X       4
W       4
V       4
U       4
T       3
....
Y       4
Z       4

请注意,要从最后一列开始填充,您必须以相反的顺序分配列(ORDER BY Col1 DESC),然后从总列数中扣除。

然后,您可以通过按列顺序进行排序:

WITH Table1 AS
(
    SELECT  TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
    FROM    sys.all_objects
)
SELECT  Col1, Col, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1) AS Row
FROM    (   SELECT [Col1],
                    5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
            FROM Table1  
        ) c;

给予:

Col1    Col     Row
--------------------
A       0       1
B       0       2
C       0       3
D       0       4
E       0       5
F       1       1
G       1       2

然后您可以应用数据透视:

WITH Table1 AS
(
    SELECT  TOP 26 Col1 = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
    FROM    sys.all_objects
), CTE AS
(
    SELECT  Col1, COL, ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1) AS Row
    FROM    (   SELECT [Col1],
                        5 - NTILE(5) OVER(ORDER BY Col1 DESC) AS Col
                FROM Table1  
            ) c
)
SELECT [0], [1], [2], [3], [4]
FROM CTE 
PIVOT (MAX([COL1]) FOR Col IN ([0], [1], [2], [3], [4])) AS Pvt
ORDER BY Row;

给予:

0       1       2       3       4
-------------------------------------
A       F       K       P       U
B       G       L       Q       V
C       H       M       R       W
D       I       N       S       X
E       J       O       T       Y
NULL    NULL    NULL    NULL    Z

添加

我已经建立了一个示例表,并使用了带有动态SQL的过程来简化重用,以演示该解决方案,对于我来说,它似乎可以正常工作。

设置

-- SET UP TABLE AND INSERT RANDOM VALUES
IF OBJECT_ID(N'tempdb..#Table1', 'U') IS NOT NULL
    DROP TABLE #Table1;

CREATE TABLE #Table1 (Col1 CHAR(2));
INSERT #Table1 (Col1)
SELECT  CONCAT(Letter, Number)
FROM    (SELECT  TOP 26 Letter = CHAR(64 + ROW_NUMBER() OVER(ORDER BY object_id))
        FROM  sys.all_objects) l
        CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9)) n (Number);

GO
IF OBJECT_ID(N'tempdb..#GenerateMatrix', 'P') IS NOT NULL
    DROP PROCEDURE #GenerateMatrix;

GO
CREATE PROCEDURE #GenerateMatrix @Records INT, @Columns INT
AS
BEGIN
    -- GENERATE COLUMNS FOR PIVOT AND SELECT
    DECLARE @ColSQL NVARCHAR(MAX) = 
            STUFF((SELECT   TOP (@Columns) 
                            CONCAT(',', QUOTENAME(ROW_NUMBER() OVER(ORDER BY Col1) - 1))
                    FROM #Table1
                    FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)'), 1, 1, '');


    -- FOR @Cols = 5 Generates "[0],[1],[2],[3],[4]"

    DECLARE @SQL NVARCHAR(MAX) = 
        CONCAT('SELECT ', @ColSQL, '
                FROM (SELECT  Col1, Col, Row = ROW_NUMBER() OVER(PARTITION BY Col ORDER BY Col1)
                    FROM    (   SELECT [Col1], Col = @Columns - NTILE(@Columns) OVER(ORDER BY Col1 DESC)
                                FROM (SELECT TOP (@Records) Col1 FROM #Table1 Col1) t
                            ) c) c 
                PIVOT (MAX([COL1]) FOR Col IN (', @ColSQL, ')) AS Pvt
                ORDER BY Row;');

    EXECUTE sp_executesql @SQL, N'@Columns INT, @Records INT', @Columns = @Columns, @Records = @Records;

END
GO

测试1

EXECUTE #GenerateMatrix @Records = 26, @Columns = 5;

0       1       2       3       4
----------------------------------
A1      A6      B2      B7      C3
A2      A7      B3      B8      C4
A3      A8      B4      B9      C5
A4      A9      B5      C1      C6
A5      B1      B6      C2      C7
NULL    NULL    NULL    NULL    C8

测试2

EXECUTE #GenerateMatrix @Records = 8, @Columns = 4;

0       1       2       3
----------------------------
A1      A3      A5      A7
A2      A4      A6      A8

测试3

EXECUTE #GenerateMatrix @Records = 40, @Columns = 8;

0       1       2       3       4       5       6       7
-----------------------------------------------------------
A1      A6      B2      B7      C3      C8      D4      D9
A2      A7      B3      B8      C4      C9      D5      E1
A3      A8      B4      B9      C5      D1      D6      E2
A4      A9      B5      C1      C6      D2      D7      E3
A5      B1      B6      C2      C7      D3      D8      E4

测试4

EXECUTE #GenerateMatrix @Records = 50, @Columns = 6;

0       1       2       3       4       5
    ---------------------------------------
A1      A9      B8      C7      D6      E6
A2      B1      B9      C8      D7      E7
A3      B2      C1      C9      D8      E8
A4      B3      C2      D1      D9      E9
A5      B4      C3      D2      E1      F1
A6      B5      C4      D3      E2      F2
A7      B6      C5      D4      E3      F3
A8      B7      C6      D5      E4      F4
NULL    NULL    NULL    NULL    E5      F5

在任何测试中都没有记录丢失,也没有行只有零值的行。

Example on DB Fiddle

答案 1 :(得分:1)

凭经验,我能够提出以下数据透视查询:

WITH cte AS (
    SELECT Col1, ROW_NUMBER() OVER (ORDER BY Col1) rn
    FROM yourTable
)

SELECT
    MAX(CASE WHEN (rn-1) / 5 = 0 THEN Col1 END) AS col1,
    MAX(CASE WHEN (rn-1) / 5 = 1 THEN Col1 END) AS col2,
    MAX(CASE WHEN (rn-1) / 5 = 2 THEN Col1 END) AS col3,
    MAX(CASE WHEN (rn-1) / 5 = 3 THEN Col1 END) AS col4,
    MAX(CASE WHEN (rn-1) / 5 = 4 THEN Col1 END) AS co15
FROM cte
GROUP BY
    (rn-1) % 5;

enter image description here

Demo

这里的想法是,每个组(一行)由当前行号在模数5的哪一点确定。列或枢轴取决于当前行号的5的多少倍。

答案 2 :(得分:0)

您在做错什么了,在Col中获得了五个值,

(ROW_NUMBER() OVER (ORDER BY Col1) -1)%5 AS Col,

但其中只有2个是PIVOTing。

 PIVOT (MAX([COL1]) FOR Col IN ([0], [1])) AS Pvt

因此,您将忽略所有具有值2、3和4的Col