繁体   English   中英

使用 SQL 反转多列

[英]Unpivot Multiple Columns with SQL

需要对列出选项和选项价格的多个列进行逆透视。 可以使用以下代码创建示例起始数据集:

CREATE TABLE testtable (
ID int,
OptionA nvarchar(25),
OptionACost decimal(16,4), 
OptionB nvarchar(25),
OptionBCost decimal(16,4), 
OptionC nvarchar(25),
OptionCCost decimal(16,4) )

INSERT INTO testtable (ID, OptionA, OptionACost, OptionB, OptionBCost, 
OptionC, OptionCCost) 

VALUES
    ('1', 'Red Paint', '11.98', 'Leather Trim', '20.00', 'Matte Finish', '5.66'),
    ('2', 'Blue Paint', '13.48', 'Suede Trim', '16.00', 'Gloss Finish', '3.82'),
    ('3', 'Black Paint', '10.00', 'Leather Trim', '20.00', 'Matte Finish', '5.66'),
    ('4', 'Red Paint', '11.98', 'No Trim', '0.00', 'Matte Finish', '5.66');

我的理想结果样本数据集是使用以下代码创建的:

CREATE TABLE testtableresult (
ID int,
OptionName nvarchar(25),
OptionCost decimal(16,4))

INSERT INTO testtable2 (ID, OptionName, OptionCost)
VALUES
    ('1', 'Red Paint', '11.98'),
    ('2', 'Blue Paint', '13.48'),
    ('3', 'Black Paint', '10.00'),
    ('4', 'Red Paint', '11.98')
    ('1', 'Leather Trim', '20.00'),
    ('2', 'Suede Trim', '16.00'),
    ('3', 'Leather Trim', '20.00'),
    ('4', 'No Trim', '0.00')
    ('1', 'Matte Finish', '5.66'),
    ('2', 'Suede Trim', '3.88'),
    ('3', 'Matte Finish', '5.66'),
    ('4', 'Matte Finish', '5.66');

SQL Server 具有APPLY运算符(即CROSS APPLY ),它可以执行您想要的类似于UNPIVOT

SELECT a.* FROM #testtable t
CROSS APPLY (
    VALUES (ID, OptionA, OptionACost, 1), (ID,OptionB, OptionBCost, 2),
           (ID, OptionC, OptionCCost, 3)
)a(Id, Name, Cost, ids)
ORDER BY a.ids, a.id
SELECT ID, OptionA AS OptionName, CAST(OptionACost as decimal(10,2)) AS OptionCost FROM testtable UNION ALL SELECT ID, OptionB AS OptionName, CAST(OptionBCost as decimal(10,2)) AS OptionCost FROM testtable UNION ALL SELECT ID, OptionC AS OptionName, CAST(OptionCCost as decimal(10,2)) AS OptionCost FROM testtable GO
\n身份证 | 选项名称 | 期权成本\n -: |  :----------- |  :---------\n  1 | 红漆|  11.98     \n  2 | 蓝色油漆 |  13.48     \n  3 | 黑漆 |  10.00     \n  4 | 红漆|  11.98     \n  1 | 皮革饰边 |  20.00     \n  2 | 麂皮饰边 |  16.00     \n  3 | 皮革饰边 |  20.00     \n  4 | 无修剪 |  0.00      \n  1 | 哑光饰面 |  5.66      \n  2 | 光泽饰面 |  3.82      \n  3 | 哑光饰面 |  5.66      \n  4 | 哑光饰面 |  5.66      \n

dbfiddle 在这里

除了Yogesh的解决方案,这里是动态创建多个CROSS APPLY的代码

演示

注意主要假设是测试表中的列称为“Option<>”和“Option<>Cost”,以便正确创建分组

    SET NOCOUNT ON
    IF OBJECT_ID ('tempdb..#Cols') IS NOT NULL DROP TABLE #Cols
    ;WITH Cols as
    (
        SELECT COLUMN_NAME , FIRST_VALUE(COLUMN_NAME) OVER (ORDER BY ORDINAL_POSITION ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MainCol
        FROM INFORMATION_SCHEMA.COLUMNS 
        WHERE TABLE_NAME = 'testtable'
    )
    SELECT *
    INTO #Cols
    FROM Cols
    WHERE COLUMN_NAME !=MainCol


    DECLARE @ColsOptions NVARCHAR(MAX)= ''
    DECLARE @Sql NVARCHAR(MAX)= ''
    ;WITH Pvt as 
        (
        SELECT *,
        DENSE_RANK() OVER (PARTITION BY MainCol ORDER BY SUBSTRING(COLUMN_NAME,LEN('Option')+1, 1)) Grp
        FROM #Cols
        )
    , InternalPvt AS 
        (
        SELECT * , ROW_NUMBER () OVER (PARTITION BY Grp ORDER BY Grp) InternalGrp
        FROM Pvt
        )
    , CrossApply as 
    (
     SELECT MAX(MainCol) MainCol,
            MAX(CASE WHEN  InternalGrp = 1 THEN COLUMN_NAME END) [Option],
            MAX(CASE WHEN  InternalGrp = 2 THEN COLUMN_NAME END) OptionCost,
            Grp
     FROM InternalPvt   
     GROUP BY Grp
     )

     SELECT @ColsOptions += '('+MainCol+','+[Option]+','+OptionCost+','+CONVERT(NVARCHAR(10),Grp) +'),'+CHAR(10) 
     FROM CrossApply
     SET @ColsOptions = SUBSTRING(@ColsOptions,0,LEN(@ColsOptions) - 1 )

     SET @sql = 
    'SELECT a.*
     FROM testtable t
     CROSS APPLY (
                VALUES '+@ColsOptions+'
                ) a(Id, Name, Cost, ids)
     ORDER BY a.ids, a.id'

    exec sp_executesql @Sql

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM