SQL Server透视/取消透视整个表(100多个字段)

时间:2016-02-26 17:17:44

标签: sql sql-server

不幸的是,我一直坚持这个项目。我需要做的是拿一张包含大约100个字段的表并比较两个记录,确定哪些字段不匹配。

我能想到的最好的方法是转动它们,所以:

KeyID    Field1    Field2   Field3    Field4   Field5    Field6
42       Apples    Pears    Blue      Hills    Dog       iPhone
65       Apples    Plums    Red       Hills    Cat       iPhone   

成为这个:

FieldName    KeyID_42    KeyID_65
Field1       Apples      Apples
Field2       Pears       Plums
Field3       Blue        Red
Field4       Hills       Hills
Field5       Dog         Cat
Field6       iPhone      iPhone

然后我可以针对它运行查询:

SELECT *
FROM MyPivot
WHERE KeyID_42 <> KeyID_65

我应该留下这个:

FieldName    KeyID_42    KeyID_65
Field2       Pears       Plums
Field3       Blue        Red
Field5       Dog         Cat

然而,对于一个包含100个左右字段的表,在PIVOT语句中列出它们中的每一个字段都不会很漂亮。我可能不得不在以后的另一张桌子上这样做。

有没有简单的方法来实现这一目标?一个不需要单独列出每个字段?

编辑:

此时此刻,但收到许多错误:Invalid column name 'UpdateTime'.

我使用的代码是:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
   @query  AS NVARCHAR(MAX)

select @colsUnpivot 
  = stuff((select ','+quotename(C.name)
           FROM sys.columns c
           WHERE c.object_id = OBJECT_ID('dbo.tblSQLAdminInventory')
           --AND C.Name <> 'EffectiveDate' 
           for xml path('')), 1, 1, '')

set @query 
  = 'select KeyID, ID1, ID2
     from tblSQLAdminInventory
     unpivot
     (
        KeyID
        for ID1 in ('+ @colsunpivot +')
     ) u
     unpivot
     (
        KeyID
        for ID2 in ('+ @colsunpivot +')
     ) u     
     '

exec sp_executesql @query;

2 个答案:

答案 0 :(得分:0)

仅展开一次,然后按如下方式编写:

;WITH cte AS (
    -- the query with unpivot but only once 
    -- UNPIVOT(value FOR ID IN (...))
)
SELECT
    *
FROM
    cte AS c1
    INNER JOIN cte AS c2 ON
        c2.ID=c1.ID
WHERE
    c1.KeyId=42 AND
    c2.KeyId=65 AND
    c1.value<>c2.value;

答案 1 :(得分:0)

这应该可以解决问题,因为它是动态的,你不需要担心字段的数量:

假设:列名将始终采用Field

格式

示例数据:

CREATE TABLE temp2 (KeyID INT, Field1 VARCHAR(20), Field2 VARCHAR(20), Field3 VARCHAR(20), Field4 VARCHAR(20), Field5 VARCHAR(20), Field6 VARCHAR(20));

INSERT INTO temp2
VALUES
       (42, 'Apples', 'Pears', 'Blue', 'Hills', 'Dog', 'iPhone'),
       (65, 'Apples', 'Plums', 'Red', 'Hills', 'Cat', 'iPhone');

查询:

-- Where clause params
DECLARE @whereClauseParam VARCHAR(MAX) = '[42] <> [65]' --<-- User will need to determine what the condition will be

--Get the Fields required for the initial pivot
DECLARE @Fields VARCHAR(MAX)= '';

SELECT @Fields+=QUOTENAME(t.name)+', '
FROM sys.columns AS t
WHERE t.object_id = OBJECT_ID('temp2')
      AND t.name LIKE 'Field[0-9]%';

-- Get the KeyId's with alias added
DECLARE @keyIDs VARCHAR(MAX)= '';

SELECT @keyIDs+=QUOTENAME(t.KeyID)+' AS [KeyID_'+CAST(t.KeyID AS VARCHAR(10))+'], '
FROM temp2 AS t;

-- Get the KeyId's without alias
DECLARE @keyIDs1 VARCHAR(MAX)= '';

SELECT @keyIDs1+=QUOTENAME(t.KeyID)+', '
FROM temp2 AS t;

--Generate Dynamic SQL
DECLARE @SQL2 VARCHAR(MAX)= 'SELECT  Value AS FieldName,';

SELECT @SQL2+=SUBSTRING(@keyIDs, 1, LEN(@keyIDs)-1)+'
FROM
(SELECT KeyID , Value , FieldName
FROM 
   (SELECT KeyID,'+SUBSTRING(@Fields, 1, LEN(@Fields)-1)+'
   FROM temp2) p --<-- you will need to change the name to point to your table
UNPIVOT
   (FieldName  FOR value IN 
      ('+SUBSTRING(@Fields, 1, LEN(@Fields)-1)+')
)AS unpvt) AS SourceTable
PIVOT
(
MAX(FieldName)
FOR KeyID IN ('+SUBSTRING(@keyIDs1, 1, LEN(@keyIDs1)-1)+')
) AS PivotTable
WHERE '+@whereClauseParam

PRINT(@SQL2);
EXECUTE(@SQL2);

结果:

enter image description here

响应评论“这返回错误:传递给LEFT或SUBSTRING函数的长度参数无效:)”:

enter image description here