简体   繁体   中英

Compare data and return unmatched column name with data

Technology: SQL Server.

I have two tables similar to following construct. Column name are same in both the tables.

I need to compare both tables and get the output (table) as shown in results. Preform this operation dynamically, Table name and column name will be provided on the fly.

Table1:

Key column1 column2 column3
1 4 5 6
2 2 5 8
3 4 5 10
4 4 6 10

Table2:

Key column1 column2 column3
1 6 5 6
2 2 5 8
3 4 5 10
4 4 8 10

Output:

TableName keyColumnName KeyColumnValue ColumnName Table1ColumnValue Table2ColumnValue
Table1 key 1 column1 4 6
Table1 key 4 column2 6 8

For each pair of rows (from TABLE1 and TABLE2 with the same KEY) this query only detect de first columns mismatch (when two or three columns mismatch only return de values from the first columns mismatch).

TableName ever refer to table1, I have to put a subquery to get it (ORDER BY key, t), without this subquery it returns table1 or table2 arbitrarily but with the Table1ColumnValue and Table2ColumnValue correctly set (changed).

I try it in Postgresql, you have to change some functions to try it in sqlServer like ARRAY_LENGTH() (in the second query) OR ARRAY_AGG().

SELECT      
    t[1] AS TableName,
    keyColumnName,
    k AS KeyColumnValue,
    CASE WHEN a1[1] != a1[2] THEN ColumnNames[1]
        WHEN a2[1] != a2[2] THEN ColumnNames[2] 
        WHEN a3[1] != a3[2] THEN ColumnNames[3] 
    END AS ColumnName,
    CASE WHEN a1[1] != a1[2] THEN a1[1]
        WHEN a2[1] != a2[2] THEN a2[1] 
        WHEN a3[1] != a3[2] THEN a3[1] 
    END AS Table1ColumnValue,
    CASE WHEN a1[1] != a1[2] THEN a1[2]
        WHEN a2[1] != a2[2] THEN a2[2] 
        WHEN a3[1] != a3[2] THEN a3[2] 
    END AS Table2ColumnValue

FROM
    (SELECT key, array_agg(column1) AS a1, array_agg(column2) AS a2, array_agg(column3) AS a3, keyColumnName, ColumnNames, array_agg(t) AS t
    FROM
        (SELECT *
        FROM
            (SELECT *, 'key' AS keyColumnName, ARRAY['column1', 'column2', 'column3'] AS ColumnNames, 'table1' AS t
            FROM t1

            UNION 

            SELECT *,  'key' AS keyColumnName, ARRAY['column1', 'column2', 'column3'] AS ColumnNames, 'table2' AS t
            FROM t2) AS u
        ORDER BY key, t
        ) AS aux1

    GROUP BY key, keyColumnName, ColumnNames
    ORDER BY key, t
) AS aux2

WHERE a1[1] != a1[2] OR a2[1] != a2[2] OR a3[1] != a3[2]

If you have rows with a key in a table that not exist in the other table, you can use this other query (it is similar than the above with a few modifications).

SELECT      
    t[1] AS TableName,
    keyColumnName,
    k AS KeyColumnValue,
    CASE WHEN a1[1] != a1[2] OR ARRAY_LENGTH(a1, 1) = 1 THEN ColumnNames[1]
        WHEN a2[1] != a2[2] THEN ColumnNames[2] 
        WHEN a3[1] != a3[2] THEN ColumnNames[3] 
    END AS ColumnName,
    CASE WHEN a1[1] != a1[2] OR ARRAY_LENGTH(a1, 1) = 1 THEN a1[1]
        WHEN a2[1] != a2[2] THEN a2[1] 
        WHEN a3[1] != a3[2] THEN a3[1] 
    END AS Table1ColumnValue,
    CASE WHEN a1[1] != a1[2] THEN a1[2]
        WHEN a2[1] != a2[2] THEN a2[2] 
        WHEN a3[1] != a3[2] THEN a3[2] 
    END AS Table2ColumnValue

FROM
    (SELECT key, array_agg(column1) AS a1, array_agg(column2) AS a2, array_agg(column3) AS a3, keyColumnName, ColumnNames, array_agg(t) AS t
    FROM
        (SELECT *
        FROM
            (SELECT *, 'key' AS keyColumnName, ARRAY['column1', 'column2', 'column3'] AS ColumnNames, 'table1' AS t
            FROM t1

            UNION 

            SELECT *,  'key' AS keyColumnName, ARRAY['column1', 'column2', 'column3'] AS ColumnNames, 'table2' AS t
            FROM t2) AS u
        ORDER BY key, t
        ) AS aux1

    GROUP BY key, keyColumnName, ColumnNames
    ORDER BY key, t
) AS aux2

WHERE a1[1] != a1[2] OR a2[1] != a2[2] OR a3[1] != a3[2] OR ARRAY_LENGTH(a1, 1) = 1 

You can unpivot the data and then use join :

with t1 as (
      select t1.key, v.*
      from table1 t1
           (values ('column1', t1.column1), ('column2', t1.column2), ('column3', t1.column3)
           ) v(name, val)
     ),
     t2 as (
      select t2.key, v.*
      from table1 t2
           (values ('column1', t2.column1), ('column2', t2.column2), ('column3', t2.column3)
           ) v(name, val)
     )
select t1.*, t2.*
from t1 join
     t2
     on t1.key = t2.key and
        t1.name = t2.name
where t1.val <> t2.val;

Some important notes:

  • This assumes that all columns have the same or compatible types.
  • This only compares keys that are in both tables.
  • This does not take NULL values into account.

All three of these are consistent with the sample data you have presented -- and they are reasonable assumptions. All can be handled by modifying the query, but that makes the query more complicated -- and tends to hide the important idea behind the query (unpivot --> join --> filter).

If you need a more general query, I would request that you ask a new question, with more explicit standard data and desired results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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