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)
Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top