Pergunta

Uma consulta que é usada para percorrer 17 milhões de registros para remover duplicatas tem funcionado agora por cerca de 16 horas e eu queria saber se a consulta for interrompido direita agora, se ele vai finalizar as instruções DELETE ou se tiver sido apagar durante a execução dessa consulta? Na verdade, se eu parar com isso, não é finalizar as exclusões ou rolos de volta?

Eu descobri que quando eu faço uma

 select count(*) from myTable

Que as linhas que ele retorna (ao fazer esta consulta) é de cerca de 5 a menos do que a contagem de linhas de partida foi. Obviamente, os recursos do servidor são extremamente pobres, então isso significa que este processo tem levado 16 horas para encontrar 5 duplicatas (quando na verdade existem milhares), e isso pode estar sendo executado por dias?

Esta consulta levou 6 segundos em 2000 linhas de dados de teste, e ele funciona muito bem nesse conjunto de dados, por isso achei que levaria 15 horas para o conjunto completo.

Todas as idéias?

Abaixo está a consulta:

--Declare the looping variable
DECLARE @LoopVar char(10)


    DECLARE
     --Set private variables that will be used throughout
      @long DECIMAL,
      @lat DECIMAL,
      @phoneNumber char(10),
      @businessname varchar(64),
      @winner char(10)

    SET @LoopVar = (SELECT MIN(RecordID) FROM MyTable)

    WHILE @LoopVar is not null
    BEGIN

      --initialize the private variables (essentially this is a .ctor)
      SELECT 
        @long = null,
        @lat = null,
        @businessname = null,
        @phoneNumber = null,
        @winner = null

      -- load data from the row declared when setting @LoopVar  
      SELECT
        @long = longitude,
        @lat = latitude,
        @businessname = BusinessName,
        @phoneNumber = Phone
      FROM MyTable
      WHERE RecordID = @LoopVar

      --find the winning row with that data. The winning row means 
      SELECT top 1 @Winner = RecordID
      FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
      ORDER BY
        CASE WHEN webAddress is not null THEN 1 ELSE 2 END,
        CASE WHEN caption1 is not null THEN 1 ELSE 2 END,
        CASE WHEN caption2 is not null THEN 1 ELSE 2 END,
        RecordID

      --delete any losers.
      DELETE FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
        AND @winner != RecordID

      -- prep the next loop value to go ahead and perform the next duplicate query.
      SET @LoopVar = (SELECT MIN(RecordID) 
    FROM MyTable
    WHERE @LoopVar < RecordID)
    END
Foi útil?

Solução

não, sql server não irá reverter as exclusões já realizados, se você parar consulta execução. Oracle requer um compromisso explícito de consultas ação ou os dados são revertidas, mas não mssql.

com o servidor SQL que não irá reverter a menos que você estiver executando especificamente no contexto de uma transação e você reverter essa transacção, ou os fecha a conexão sem a transação ter sido cometido. mas eu não vejo um contexto de transação em sua consulta acima.

Você também pode tentar re-estruturação sua consulta para fazer o apaga um pouco mais eficiente, mas, essencialmente, se as especificações de sua caixa não são lá essas coisas, então você pode ser preso espera-lo.

daqui para frente, você deve criar um índice exclusivo na tabela para manter-se de ter que passar por isso novamente.

Outras dicas

A sua consulta não está envolvido em uma transação, por isso não irá reverter as alterações já introduzidas pelas instruções DELETE.

Eu especificamente testado isso mesmo em meu próprio SQL Server usando a seguinte consulta e a tabela de ApplicationLog estava vazio, embora eu cancelado a consulta:

declare @count int
select @count = 5
WHILE @count > 0
BEGIN
  print @count
  delete from applicationlog;
  waitfor time '20:00';
  select @count = @count -1
END

No entanto sua consulta é susceptível de levar muitos dias ou semanas, muito mais, em seguida, 15 horas. Sua estimativa de que você pode processar 2.000 registros a cada 6 segundos é errado, porque cada iteração em seu loop while irá demorar muito mais, com 17 milhões de linhas, então ele faz com 2000 linhas. Então, a menos que sua consulta leva significativamente menos de um segundo para 2000 linhas, vai demorar dias para todos os 17 milhões.

Você deve fazer uma nova pergunta sobre como você pode excluir linhas duplicadas de forma eficiente.

Se você não fazer nada explícito sobre transações em seguida, a conexão estará em modo de transações confirmação automática . Neste modo cada instrução SQL é considerada uma transação.

A questão é se isso significa que as instruções SQL individuais são transações e, portanto, a ser cometidos como você vai, ou se a contagem de loop while exteriores como uma transação.

Não parece haver nenhuma discussão sobre isso na descrição da construção, enquanto em MSDN . No entanto, desde uma indicação quando não pode modificar diretamente o banco de dados parece lógico que ele não iniciar uma transação de confirmação automática.

transações implícitas

Se há 'transacções implícitas' foi definido, então cada iteração em seu loop comprometido as alterações.

É possível para qualquer SQL Server para ser definido com 'transações implícita'. Esta é uma configuração de banco de dados (por padrão é OFF). Você também pode ter transações implícitas nas propriedades de um interior consulta particular de Management Studio (clique direito em consulta painel> Opções), por configurações padrão no cliente, ou uma declaração SET.

SET IMPLICIT_TRANSACTIONS ON;

De qualquer maneira, se este fosse o caso, você ainda precisa executar uma consolidação explícita / ROLLBACK independentemente da interrupção da execução da consulta.


referência transações implícita:

http://msdn.microsoft.com/en-us/library /ms188317.aspx

http://msdn.microsoft.com/en-us/library /ms190230.aspx

Eu herdei um sistema que tinha lógica algo como a sua implementados em SQL. No nosso caso, estávamos tentando linhas juntas link usando correspondência difusa que tinham nomes semelhantes / endereços, etc, e que a lógica foi feito puramente em SQL. Na época, eu herdou tivemos cerca de 300.000 linhas na tabela e de acordo com os horários, calculou-se que seria necessário um ano para coincidir com todos eles.

Como uma experiência para ver o quanto mais rápido eu poderia fazê-lo fora do SQL, eu escrevi um programa para despejar tabela db em arquivos simples, leia os arquivos simples em um programa C ++, criar meus próprios índices, e fazer a difusa lógica lá, em seguida, reimportar os arquivos simples no banco de dados. O que levou um ano no SQL levou cerca de 30 segundos no C ++ aplicativo.

Então, meu conselho é, nem sequer tentar o que você está fazendo em SQL. Export, processo, re-importação.

exclui que foram realizados até este ponto não será revertida.


Como o autor original do código em questão , e que emitiu a ressalva de que o desempenho será dependente de índices, gostaria de propor os seguintes itens para acelerar o processo.

RecordId melhor que seja PRIMARY KEY. Eu não quero dizer IDENTIDADE, quero dizer PRIMARY KEY. Confirmar esta usando sp_help

Alguns índice deve ser usado para avaliar essa consulta. Descobrir quais desses quatro colunas tem menos repetições e índice que ...

SELECT *
FROM MyTable
WHERE @long = longitude
  AND @lat = latitude
  AND @businessname = BusinessName
  AND @phoneNumber = Phone

Antes e depois de adicionar este índice, verifique o plano de consulta para ver se o índice de varredura foi adicionado.

Como um loop sua consulta terá dificuldade para escalar bem, mesmo com índices apropriados. A consulta deve ser reescrito para uma única declaração, de acordo com as sugestões na sua pergunta anterior sobre isso.

Se você não estiver executando explicitamente dentro de uma transação que só irá reverter a instrução em execução.

Eu acho que essa consulta seria muito mais eficiente se ele foi re-escrito usando um algoritmo de passagem única usando um cursor. Você iria pedir-lhe cursor tabela, longitude, latitude, BusinessName E @phoneNumber. Você iria percorrer a linhas de cada vez. Se uma linha tem a mesma longitude, latitude, businessname e phonenumber como a linha anterior, em seguida, excluí-lo.

Eu acho que você precisa considerar seriamente a sua methodolology. Você precisa começar a pensar em conjuntos (embora para o desempenho que você pode precisar de processamento em lote, mas não linha por linha contra uma mesa ficha 17 milhões.)

Em primeiro lugar fazer todos os seus registros têm duplicatas? Suspeito que não, então a primeira coisa que você wan a fazer é limitar o seu processamento para somente os registros que têm duplicatas. Desde que esta é uma mesa grande e você pode precisar de fazer as exclusões em lotes ao longo do tempo, dependendo do que outro processamento está acontecendo, você primeiro puxar os registros que deseja lidar com em uma tabela própria que você, então índice. Você também pode usar uma tabela temporária se você estiver indo para ser capaz de fazer tudo isso ao mesmo tempo, sem nunca parar que outro sábio criar uma tabela no seu banco de dados e soltar no final.

Algo como (Nota eu não escrever a criar statments índice, eu acho que você pode olhar que até você mesmo):

SELECT min(m.RecordID), m.longitude, m.latitude, m.businessname, m.phone  
     into  #RecordsToKeep    
FROM MyTable   m
join 
(select longitude, latitude, businessname, phone
from MyTable
group by longitude, latitude, businessname, phone
having count(*) >1) a 
on a.longitude = m.longitude and a.latitude = m.latitude and
a.businessname = b.businessname and a.phone = b.phone 
group by  m.longitude, m.latitude, m.businessname, m.phone   
ORDER BY CASE WHEN m.webAddress is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption1 is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption2 is not null THEN 1 ELSE 2 END



while (select count(*) from #RecordsToKeep) > 0
begin
select top 1000 * 
into #Batch
from #RecordsToKeep

Delete m
from mytable m
join #Batch b 
        on b.longitude = m.longitude and b.latitude = m.latitude and
        b.businessname = b.businessname and b.phone = b.phone 
where r.recordid <> b.recordID

Delete r
from  #RecordsToKeep r
join #Batch b on r.recordid = b.recordid

end

Delete m
from mytable m
join #RecordsToKeep r 
        on r.longitude = m.longitude and r.latitude = m.latitude and
        r.businessname = b.businessname and r.phone = b.phone 
where r.recordid <> m.recordID

Além disso, tente pensar um outro método para remover linhas duplicadas:

delete t1 from table1 as t1 where exists (
    select * from table1 as t2 where
        t1.column1=t2.column1 and
        t1.column2=t2.column2 and
        t1.column3=t2.column3 and
        --add other colums if any
        t1.id>t2.id
)

Eu suponho que você tem uma coluna id inteiro em sua tabela.

Se a sua máquina não tem hardware muito avançado, então pode demorar sql server um tempo muito longo para completar o comando. Eu não sei ao certo como esta operação é realizada sob o capô, mas com base na minha experiência, isso poderia ser feito de forma mais eficiente, trazendo os registros do banco de dados e na memória para um programa que usa uma estrutura de árvore com uma regra duplicado remover para a inserção. Tente ler a totalidade da tabela em chuncks (dizem que 10.000 linhas de cada vez) em um programa C ++ usando ODBC. Uma vez no C ++ uso programa e std :: mapa onde chave é a chave única e struct é uma estrutura que contém o resto dos dados em variáveis. Loop sobre todos os registros e realizar a inserção no mapa. A função mapa inserto irá lidar com a remoção dos duplicados. Desde a pesquisa dentro de um mapa é lg (n) tempo muito menos tempo para encontrar duplicatas do que usar o loop while. Você pode então excluir a tabela inteira e adicionar as tuplas de volta para o banco de dados a partir do mapa através da formação de inserção consultas e executá-los via ODBC ou construindo um script arquivo de texto e executá-lo no estúdio de gestão.

Eu tenho certeza que é um negatory. Caso contrário, o que seria o ponto de transações ser?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top