【问题标题】:Unpivot Multiple Columns with SQL使用 SQL 取消透视多列
【发布时间】:2018-03-12 09:58:21
【问题描述】:

需要取消透视列出期权和期权价格的多个列。可以使用以下代码创建示例起始数据集:

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。

标签: sql sql-server pivot unpivot


【解决方案1】:

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

【讨论】:

  • 这工作谢谢!这里唯一困难的是我有 100 多个选项,但我没有看到更好的方法。
  • @SanomaJean,如果您有 100 多个选项,那么您可以采用 Yogesh 的方法并动态创建它。请参阅下面的解决方案。
【解决方案2】:
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
身份证 |选项名称 |期权成本 -: | :----------- | :--------- 1 |红漆 | 11.98 2 |蓝漆 | 13.48 3 |黑漆 | 10.00 4 |红漆 | 11.98 1 |皮革饰边 | 20.00 2 |绒面革饰边 | 16.00 3 |皮革饰边 | 20.00 4 |没有修剪 | 0.00 1 |哑光 | 5.66 2 |光泽饰面 | 3.82 3 |哑光 | 5.66 4 |哑光 | 5.66

dbfiddle here

【讨论】:

  • 这很好,唯一的问题是有 100 多个选项,所以我认为这对于大规模来说是不可行的 :(
【解决方案3】:

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

DEMO

注意 主要假设是您在测试表中的列称为“Option” 和“选项成本”以正确创建分组

    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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-04
    相关资源
    最近更新 更多