Isso já foi solicitado no Stack Overflow , mas apenas para o MySQL. Estou usando o PostgreSQL. Infelizmente (e surpreendentemente) o PostgreSQL não parece ter algo como CHECKSUM table
.
Uma solução PostgreSQL seria boa, mas uma genérica seria melhor. Encontrei http://www.besttechtools.com/articles/article/sql-query-to-check-two-tables-have-identical-data , mas não entendo a lógica usada.
Background: Eu reescrevi algum código gerador de banco de dados, então preciso verificar se o código antigo e o novo produzem resultados idênticos.
Você pode usar o
EXCEPT
operador. Por exemplo, se as tabelas tiverem estrutura idêntica, o seguinte retornará todas as linhas que estão em uma tabela, mas não na outra (portanto, 0 linhas se as tabelas tiverem dados idênticos):Ou com
EXISTS
para retornar apenas um valor booleano ou uma string com um dos 2 resultados possíveis:Testado no SQLfiddle
Também não é o que
EXCEPT
remove duplicatas (isso não deve ser uma preocupação se suas tabelas tiverem algumaPRIMARY KEY
restriçãoUNIQUE
, mas pode ser se você estiver comparando resultados de consultas arbitrárias que podem produzir linhas duplicadas).Outra coisa que a palavra-
EXCEPT
chave faz é tratarNULL
os valores como idênticos, portanto, se a tabelaA
tiver uma linha com(1,2,NULL)
e a tabelaB
tiver uma linha com(1,2,NULL)
, a primeira consulta não mostrará essas linhas e a segunda consulta retornará'same'
se as duas tabelas não tiverem outra linha.Se você quiser contar essas linhas como diferentes, você pode usar uma variação na
FULL JOIN
resposta do gsiems, para obter todas as linhas (diferentes):e para obter uma resposta sim/não:
Se todas as colunas das duas tabelas não forem anuláveis, as duas abordagens darão respostas idênticas.
Uma opção é usar um FULL OUTER JOIN entre as duas tabelas no seguinte formato:
Por exemplo:
Retornará uma contagem de 2, enquanto:
retorna a contagem esperada de 0.
A coisa que eu gosto sobre este método é que ele só precisa ler cada tabela uma vez versus ler cada tabela duas vezes ao usar EXISTS. Além disso, isso deve funcionar para qualquer banco de dados que suporte junções externas completas (não apenas Postgresql).
Eu geralmente desencorajo o uso da cláusula USING, mas aqui está uma situação em que acredito ser a melhor abordagem.
Adendo 2019-05-03:
Se houver um problema com possíveis dados nulos (ou seja, a coluna id não é anulável, mas o val é), você pode tentar o seguinte:
Você precisa da cláusula Except Algo como
Isso retorna todas as linhas da primeira tabela que não estão na segunda tabela
Usar
except
é a chave para encontrar o que está na tabela1 e não na tabela2 e vice-versa e unir os resultados. Se forem idênticos, a contagem é zero.Se você não se importa com uma tabela ser menor que a outra, mas se importa apenas com as diferenças nos atributos:
Olhando para o código vinculado que você não entende:
O molho secreto está usando
union
em oposição aunion all
. O primeiro retém apenas linhas distintas, enquanto o último mantém duplicatas ( referência ). Em outras palavras, as consultas aninhadas dizem "me dê todas as linhas e colunas de EmpDtl1 e, além disso, aquelas de EmpDtl2 que ainda não estão em EmpDtl1". A contagem desta subconsulta será igual à contagem de EmpDtl1 se e somente se EmpDtl2 não contribuir com nenhuma linha para o resultado, ou seja, as duas tabelas são idênticas.Como alternativa, despeje as tabelas na sequência de teclas em dois arquivos de texto e use a ferramenta de comparação de sua escolha.
Gambiarra!
Esta é uma solução alternativa para uma verificação rápida e não uma solução profissional. Ele está trabalhando apenas com colunas numéricas como identificadores, caso contrário, converta colunas de string em numéricas (conte as letras, converta em bytes ou similares) se necessário, e NULLs não são problema.
Primeira solução não profissional: amostras
No final, como as consultas demoravam muito em uma tabela de 1 Mio para serem comparadas com outras às vezes com valores NULL nos campos para cuidar também, peguei algumas amostras de números de linha escolhidos para testar a diferença de dois mesas aproximadamente. Eu não encontrei nenhuma diferença, pensando que eles são idênticos de qualquer maneira.
Segunda solução um pouco mais profissional: sum() de colunas
Embora essa amostra parecesse suficiente, eu queria ter certeza e, resumindo todas as colunas numéricas, encontrei diferenças!
A saída é como:
Essa soma de colunas ou de toda a tabela é a verificação rápida, e normalmente já deve responder a pergunta principal e revela mais se você agrupar a tabela por atributos interessantes.
Soma de verificação
É um pouco como a solução CHECKSUM no MySQL .
Mente: MySQL!! , não Postgresql:
Você pode encontrar maneiras melhores em Como posso obter um hash de uma tabela inteira no postgresql? .
A questão aqui é que é apenas um Hash, informações como no meu caso, quando eu pude ver pela soma da col1 que deve haver valores 0 ou NULL, se perde.
Próximo passo: encontrar diferenças
O próximo passo para encontrar as diferenças é então ordenar por col1 em ambas as tabelas, ou agrupar por - count() em col1 se não for único.
Você também já deve saber quais atributos podem ser o problema e verificar que, no meu caso, sei que algum col1 pode ser 0. A soma de 0 não faz sentido, contando aqui, e a soma da outra coluna prova a diferença:
Uma contra-consulta:
e depois de algum tempo, você encontra os candidatos com alguma sorte ou depois de um grupo por visão geral que você pode verificar também subtraindo um do outro,
ORDER BY difference DESC
.Talvez execute consultas de longa duração agora
Mas mesmo que você conheça scripts pesados agora para encontrar as diferenças reais em uma verificação completa da mesa, você sabe pelo menos que esperar uma hora ou mais faz sentido. :)
Pequena dica: selecione em uma nova tabela
Pequena dica: se você executar uma consulta tão duradoura, considere a indexação primeiro. Além disso, verifique se você pode criar diretamente uma tabela dos resultados do sql usando
Para que você tenha os resultados não apenas na saída.