SQL Duplicate Excluir Consulta ao longo de milhões de linhas de Desempenho
-
03-07-2019 - |
Pergunta
Esta tem sido uma aventura. Comecei com a consulta duplicado looping localizado na minha anterior pergunta , mas cada loop iria passar por cima de todos os 17 milhões de discos , o que significa que levaria semanas (apenas correr *select count * from MyTable*
leva meu servidor 4:30 minutos usando MSSQL, 2005). Eu brilhava informações deste site e neste pós .
E chegaram à consulta abaixo. A questão é, é este o tipo correto de consulta para executar em 17 milhões de registros para qualquer tipo de desempenho? Se não for, o que é?
SQL consulta:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM tl_acxiomimport.dbo.tblacxiomlistings
EXCEPT
SELECT RecordID
FROM (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al WHERE Rank = 1)
Solução
Vendo o QueryPlan ajudaria.
É este viável?
SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m
inner join (SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
) al on (al.RecordID = m.RecordID and al.Rank = 1)
truncate table tl_acxiomimport.dbo.tblacxiomlistings
insert into tl_acxiomimport.dbo.tblacxiomlistings
select * from #temp
Outras dicas
Algo se passa com o seu DB, servidor, armazenamento ou alguma combinação destes. 04:30 para uma contagem select * parece muito elevado.
Executar uma DBCC_SHOWCONTIG para ver como fragmentado sua mesa é, isso poderia causar um grande impacto na performance sobre uma mesa desse tamanho.
Além disso, para adicionar ao comentário por RyanKeeter, execute o plano de show e se existem quaisquer varreduras de tabela criar um índice para o campo PK nessa tabela.
Não seria mais simples de fazer:
DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in
(SELECT RecordID
FROM (
SELECT RecordID,
Rank() over (Partition BY BusinessName,
latitude,
longitude,
Phone
ORDER BY webaddress DESC,
caption1 DESC,
caption2 DESC) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
)
WHERE Rank > 1
)
Executar este no analisador de consulta:
SET SHOWPLAN_TEXT ON
Em seguida, perguntar Query Analyzer para executar sua consulta. Em vez de executar a consulta, o SQL Server irá gerar um plano de consulta e colocá-lo no conjunto de resultados.
Mostra-nos o plano de consulta.
17 milhões de discos não é nada. Se for preciso 04:30 apenas para fazer um SELECT COUNT (*), então não é um problema sério, provavelmente relacionada a qualquer falta de memória no servidor ou um processador muito antigo.
Para o desempenho, consertar a máquina. Bombeá-lo até 2GB. RAM é tão barato estes dias que o seu custo é muito menos do que o seu tempo.
é o processador ou disco surra quando essa consulta está indo? Se não, então algo está bloqueando as chamadas. Nesse caso, você pode considerar colocar o banco de dados no modo de usuário único para a quantidade de tempo que leva para executar a limpeza.
Então, você está excluindo todos os registros que não são classificados em primeiro lugar? Pode valer a pena comparar a juntar-se contra uma sub consulta top 1 contra (que também pode funcionar em 2000, como classificação é 2005 e superior)
Você precisa remover todas as duplicatas em uma única operação? Eu suponho que você está pré-formando algum tipo de tarefa de limpeza, você pode ser capaz de fazê-lo peça-wise.
Basicamente criar um cursor que circula todos os registros (leia sujo) e remove dupes para cada um. Vai ser muito mais lento em geral, mas cada operação será relativamente mínimo. Em seguida, o seu serviço de limpeza se torna uma tarefa constante de fundo em vez de um lote noturno.
A sugestão acima para selecionar em uma tabela temporária primeiro é a sua melhor aposta. Você também pode usar algo como:
set rowcount 1000
antes de executar a sua exclusão. Ele vai parar de correr depois que exclui as linhas 1000. Em seguida, executá-lo novamente e novamente até que você obter 0 registros excluídos.
se eu entendi corretamente, você consulta é o mesmo que
DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
Eu acho que deve correr mais rápido, eu tendem a evitar o uso de cláusula "IN" em favor de associações, sempre que possível.
Você pode realmente testar a velocidade e os resultados com segurança simplesmente chamando SELECT *
ou SELECT COUNT(*)
na parte FROM, como por exemplo.
SELECT *
FROM
tl_acxiomimport.dbo.tblacxiomlistings allRecords
LEFT JOIN (
SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
FROM tl_acxiomimport.dbo.tblacxiomlistings
WHERE Rank = 1) myExceptions
ON allRecords.RecordID = myExceptions.RecordID
WHERE
myExceptions.RecordID IS NULL
Esta é outra razão pela qual eu preferiria a abordagem Cadastre Espero que ajude
Isso parece bom, mas você pode considerar a seleção de seus dados em uma tabela temporária e usar isso em sua declaração de exclusão. Tenho notado enormes ganhos de desempenho de fazer isso em vez de fazer tudo em que consulta um.
Lembre-se quando se faz um grande exclusão que é melhor ter um bom backup em primeiro lugar. (E eu também costumam copiar os registros excluídos para outra mesa apenas no caso, eu preciso recuperá-los de imediato.)
Além de usar truncar como sugerido, eu tive a melhor sorte usando este modelo para excluir lotes de linhas de uma tabela. Não me lembro off mão, mas eu acho que usando a transação ajudou a manter o arquivo de log de crescer - pode ter sido outra razão embora - não tenho certeza. E eu costumo mudar o método de log de transações até simples antes de fazer algo parecido com isto:
SET ROWCOUNT 5000 WHILE 1 = 1 BEGIN begin tran DELETE FROM ??? WHERE ??? IF @@rowcount = 0 BEGIN COMMIT BREAK END COMMIT END SET ROWCOUNT 0