AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 12580
Accepted
ErikE
ErikE
Asked: 2012-02-10 11:51:19 +0800 CST2012-02-10 11:51:19 +0800 CST 2012-02-10 11:51:19 +0800 CST

Mostre facilmente linhas diferentes entre duas tabelas ou consultas

  • 772

Imagine que você tenha duas tabelas/consultas diferentes que deveriam ter/retornar dados idênticos. Você deseja verificar isso. Qual é uma maneira fácil de mostrar todas as linhas não correspondentes de cada tabela, como no exemplo abaixo, comparando todas as colunas? Suponha que haja 30 colunas nas tabelas, muitas das quais são NULLable.

Quando não há PK ou pode haver duplicatas por PK, juntar apenas colunas PK não é suficiente, e seria um desastre ter que fazer um FULL JOIN com 30 condições de junção que lidam adequadamente com NULLs, além de uma desagradável condição WHERE para excluir as linhas correspondentes.

Normalmente, é quando estou escrevendo uma nova consulta contra dados não apagados ou não totalmente compreendidos que o problema é pior e a probabilidade de um PK estar logicamente disponível é extremamente baixa. Eu crio duas maneiras diferentes de resolver o problema e depois comparo seus resultados, as diferenças destacando casos especiais nos dados que eu desconhecia.

O resultado precisa ficar assim:

Which   Col1   Col2   Col3   ... Col30
------  ------ ------ ------     ------
TableA  Cat    27     86               -- mismatch
TableB  Cat    27     105              -- mismatch
TableB  Cat    27     87               -- mismatch 2
TableA  Cat    128    92               -- no corresponding row
TableB  Lizard 83     NULL             -- no corresponding row

Se [Col1, Col2]acontecer de ser uma chave composta e ordenarmos por elas em nosso resultado final, podemos ver facilmente que A e B têm uma linha diferente que deve ser a mesma, e cada uma tem uma linha que não está na outra.

No exemplo acima, não é desejável ver a primeira linha duas vezes.

Aqui está DDL e DML para configurar tabelas e dados de amostra:

CREATE TABLE dbo.TableA (
   Col1 varchar(10),
   Col2 int,
   Col3 int,
   Col4 varchar(10),
   Col5 varchar(10),
   Col6 varchar(10),
   Col7 varchar(10),
   Col8 varchar(10),
   Col9 varchar(10),
   Col10 varchar(10),
   Col11 varchar(10),
   Col12 varchar(10),
   Col13 varchar(10),
   Col14 varchar(10),
   Col15 varchar(10),
   Col16 varchar(10),
   Col17 varchar(10),
   Col18 varchar(10),
   Col19 varchar(10),
   Col20 varchar(10),
   Col21 varchar(10),
   Col22 varchar(10),
   Col23 varchar(10),
   Col24 varchar(10),
   Col25 varchar(10),
   Col26 varchar(10),
   Col27 varchar(10),
   Col28 varchar(10),
   Col29 varchar(10),
   Col30 varchar(10)
);

CREATE TABLE dbo.TableB (
   Col1 varchar(10),
   Col2 int,
   Col3 int,
   Col4 varchar(10),
   Col5 varchar(10),
   Col6 varchar(10),
   Col7 varchar(10),
   Col8 varchar(10),
   Col9 varchar(10),
   Col10 varchar(10),
   Col11 varchar(10),
   Col12 varchar(10),
   Col13 varchar(10),
   Col14 varchar(10),
   Col15 varchar(10),
   Col16 varchar(10),
   Col17 varchar(10),
   Col18 varchar(10),
   Col19 varchar(10),
   Col20 varchar(10),
   Col21 varchar(10),
   Col22 varchar(10),
   Col23 varchar(10),
   Col24 varchar(10),
   Col25 varchar(10),
   Col26 varchar(10),
   Col27 varchar(10),
   Col28 varchar(10),
   Col29 varchar(10),
   Col30 varchar(10)
);

INSERT dbo.TableA (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30)
VALUES
   ('Cat', 27, 86, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Cat', 128, 92, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Porcupine', NULL, 42, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Tapir', NULL, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0')
;

INSERT dbo.TableB (Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30)
VALUES
   ('Cat', 27, 105, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Cat', 27, 87, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Lizard', 83, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Porcupine', NULL, 42, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0'),
   ('Tapir', NULL, NULL, 'a', 'b', 'c', 'd', 'e', 'f', 'g',' h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0');
sql-server sql-server-2008
  • 4 4 respostas
  • 112502 Views

4 respostas

  • Voted
  1. hqrsie
    2012-02-10T11:58:00+08:002012-02-10T11:58:00+08:00

    Isso pode ser tratado usando EXCEPT e/ou INTERSECT. http://msdn.microsoft.com/en-us/library/ms188055.aspx

    Primeiro localize todos os registros que estão na tabela1 que não estão na tabela 2 e, em seguida, localize todos os registros que estão na tabela 2 que não estão na tabela um.

    SELECT * FROM table1
    EXCEPT
    SELECT * FROM table2
    
    UNION
    
    SELECT * FROM table2
    EXCEPT
    SELECT * FROM table1
    

    Há, sem dúvida, uma maneira mais eficiente de fazer isso, mas é a primeira solução "rápida e suja" que me vem à cabeça. Além disso, não recomendo usar um caractere curinga *, mas serve aqui por brevidade.

    Como alternativa, você pode usar um operador INTERSECT e excluir todos os resultados dele.

    • 23
  2. Best Answer
    Martin Smith
    2012-02-11T04:19:52+08:002012-02-11T04:19:52+08:00

    Você não precisa de 30 condições de junção para um FULL OUTER JOINaqui.

    Você pode apenas Full Outer Join no PK, preservar linhas com pelo menos uma diferença WHERE EXISTS (SELECT A.* EXCEPT SELECT B.*)e usar CROSS APPLY (SELECT A.* UNION ALL SELECT B.*)para desarticular os dois lados das JOINlinhas ed em linhas individuais.

    WITH TableA(Col1, Col2, Col3) 
         AS (SELECT 'Dog',1,1     UNION ALL 
             SELECT 'Cat',27,86   UNION ALL 
             SELECT 'Cat',128,92), 
         TableB(Col1, Col2, Col3) 
         AS (SELECT 'Dog',1,1     UNION ALL 
             SELECT 'Cat',27,105  UNION ALL 
             SELECT 'Lizard',83,NULL) 
    SELECT CA.*
    FROM   TableA A 
           FULL OUTER JOIN TableB B 
             ON A.Col1 = B.Col1 
                AND A.Col2 = B.Col2 
    /*Unpivot the joined rows*/
    CROSS APPLY (SELECT 'TableA' AS what, A.* UNION ALL
                 SELECT 'TableB' AS what, B.*) AS CA     
    /*Exclude identical rows*/
    WHERE  EXISTS (SELECT A.* 
                   EXCEPT 
                   SELECT B.*) 
    /*Discard NULL extended row*/
    AND CA.Col1 IS NOT NULL      
    ORDER BY CA.Col1, CA.Col2
    

    Dá

    what   Col1   Col2        Col3
    ------ ------ ----------- -----------
    TableA Cat    27          86
    TableB Cat    27          105
    TableA Cat    128         92
    TableB Lizard 83          NULL
    

    Ou uma versão lidando com os postes movidos.

    SELECT DISTINCT CA.*
    FROM   TableA A 
           FULL OUTER JOIN TableB B 
             ON EXISTS (SELECT A.*  INTERSECT  SELECT B.*) 
    CROSS APPLY (SELECT 'TableA' AS what, A.* UNION ALL
                 SELECT 'TableB' AS what, B.*) AS CA     
    WHERE NOT EXISTS (SELECT A.*  INTERSECT  SELECT B.*) 
    AND CA.Col1 IS NOT NULL
    ORDER BY CA.Col1, CA.Col2  
    

    Para tabelas com muitas colunas, ainda pode ser difícil identificar as colunas específicas que diferem. Para isso, você pode potencialmente usar o abaixo.

    (embora apenas em tabelas relativamente pequenas, caso contrário, esse método provavelmente não terá desempenho adequado)

    SELECT t1.primary_key,
           y1.c,
           y1.v,
           y2.v
    FROM   t1
           JOIN t2
             ON t1.primary_key = t2.primary_key
           CROSS APPLY (SELECT t1.*
                        FOR xml path('row'), elements xsinil, type) x1(x)
           CROSS APPLY (SELECT t2.*
                        FOR xml path('row'), elements xsinil, type) x2(x)
           CROSS APPLY (SELECT n.n.value('local-name(.)', 'sysname'),
                               n.n.value('.', 'nvarchar(max)')
                        FROM   x1.x.nodes('row/*') AS n(n)) y1(c, v)
           CROSS APPLY (SELECT n.n.value('local-name(.)', 'sysname'),
                               n.n.value('.', 'nvarchar(max)')
                        FROM   x2.x.nodes('row/*') AS n(n)) y2(c, v)
    WHERE  y1.c = y2.c
           AND EXISTS(SELECT y1.v
                      EXCEPT
                      SELECT y2.v) 
    
    • 17
  3. A-K
    2012-02-10T12:32:44+08:002012-02-10T12:32:44+08:00

    É fácil de realizar com uma ferramenta de terceiros, como o Data Compare, ou apenas no cliente. No contexto de procedimentos armazenados de teste de unidade, acabamos de escrever algum código C#.

    Aqui está o código C# que estamos usando, citado em um artigo antigo: Feche essas brechas - Testando procedimentos armazenados

       internal static class DataSetComparer
       {
          internal static bool Compare(DataSet one, DataSet two)
          {
             if(one.Tables.Count != two.Tables.Count)
                return false;
    
             for(int i = 0; i < one.Tables.Count; i++)
                if(!CompareTables(one.Tables[i], two.Tables[i]))
                   return false;
    
             return true;
            }
    
          private static bool CompareTables(DataTable one, DataTable two)
          {
             if(one.Rows.Count != two.Rows.Count)
                return false;
    
             for(int i = 0; i < one.Rows.Count; i++)
                if(!CompareRows(one.Rows[i], two.Rows[i]))
                   return false;
    
             return true;
          }
    
          private static bool CompareRows(DataRow one, DataRow two)
          {
             if(one.ItemArray.Length != two.ItemArray.Length)
                return false;
    
             for(int i = 0; i < one.ItemArray.Length; i++)
                if(!CompareItems(one.ItemArray[i], two.ItemArray[i]))
                   return false;
    
             return true;
          }
    
          private static bool CompareItems(object value1, object value2)
          {
             if(value1.GetType() != value2.GetType())
                return false;
    
             if(value1 is DBNull)
                return true;
    
             if(value1 is DateTime)
                return ((DateTime) value1).CompareTo((DateTime) value2)
                                                                  == 0;
    
             if(value1 is byte[])
             {
                if(((byte[]) value1).Length != ((byte[]) value2).Length)
                   return false;
    
                for(int i = 0; i < ((byte[]) value1).Length; i++)
                   if(((byte[]) value1)[i] != ((byte[]) value2)[i])
                      return false;
    
                return true;
             }
    
             return value1.ToString().Equals(value2.ToString());
          }
       }
    
    • 7
  4. ErikE
    2012-03-23T13:32:00+08:002012-03-23T13:32:00+08:00

    Aqui está uma maneira de mostrar o que foi solicitado:

    SELECT
       Which = 'TableA',
       *
    FROM (
       SELECT * FROM dbo.TableA
       EXCEPT
       SELECT * FROM dbo.TableB
    ) X
    UNION ALL
    SELECT
       'TableB',
       *
    FROM (
       SELECT * FROM dbo.TableB
       EXCEPT
       SELECT * FROM dbo.TableA
    ) X
    ORDER BY
       Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9, Col10, Col11, Col12, Col13, Col14, Col15, Col16, Col17, Col18, Col19, Col20, Col21, Col22, Col23, Col24, Col25, Col26, Col27, Col28, Col29, Col30
    ;
    
    • 4

relate perguntas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Quanto "Padding" coloco em meus índices?

  • Existe um processo do tipo "práticas recomendadas" para os desenvolvedores seguirem para alterações no banco de dados?

  • Como determinar se um Índice é necessário ou necessário

  • Downgrade do SQL Server 2008 para 2005

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como ver a lista de bancos de dados no Oracle?

    • 8 respostas
  • Marko Smith

    Quão grande deve ser o mysql innodb_buffer_pool_size?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    restaurar a tabela do arquivo .frm e .ibd?

    • 10 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve