SQL Server Pivot/Unpivot an entire table (100+ fields)

时间:2021-05-13 10:18:12

Unfortunately, I'm stuck with this project the way it is. What I need to do is take a table with about 100 fields and compare two records, determining which fields don't match.

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

The best way I could think of was to pivot them, so this:

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

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

becomes this:

成为这个:

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

And then I can run a query against that like:

然后我可以运行一个查询,如:

SELECT *
FROM MyPivot
WHERE KeyID_42 <> KeyID_65

and I should be left with this:

我应该留下这个:

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

However, with a table with 100 or so fields, listing every single one of them in a PIVOT statement isn't gonna be pretty. And I may have to do this with another table later.

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

Is there an easy way to achieve this? One that doesn't require listing each field separately?

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

EDIT:

I'm at this point, but getting many errors like this: Invalid column name 'UpdateTime'.

我就是这样,但是遇到了很多这样的错误:列名“UpdateTime”无效。

The code I'm using is this:

我正在使用的代码是这样的:

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 个解决方案

#1


0  

Unpivot only once, and then write as follows:

只旋转一次,然后写如下:

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

#2


0  

This should do the trick, as it is dynamic you do not need to worry about the number of the fields:

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

Assumption: The column names will always be in the format Field

假设:列名称将始终采用字段格式

Sample data:

样本数据:

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

Query:

查询:

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

Result:

结果:

SQL Server Pivot/Unpivot an entire table (100+ fields)

In response to the comment "This returns error: Invalid length parameter passed to the LEFT or SUBSTRING function :)":

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

SQL Server Pivot/Unpivot an entire table (100+ fields)

#1


0  

Unpivot only once, and then write as follows:

只旋转一次,然后写如下:

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

#2


0  

This should do the trick, as it is dynamic you do not need to worry about the number of the fields:

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

Assumption: The column names will always be in the format Field

假设:列名称将始终采用字段格式

Sample data:

样本数据:

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

Query:

查询:

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

Result:

结果:

SQL Server Pivot/Unpivot an entire table (100+ fields)

In response to the comment "This returns error: Invalid length parameter passed to the LEFT or SUBSTRING function :)":

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

SQL Server Pivot/Unpivot an entire table (100+ fields)