Atualizando uma tabela com milhões de registros, já faz 4 dias

12

Atualmente, estou atualizando uma tabela com milhões de registros, já faz 4 dias e a consulta ainda está em execução.

Eu verifiquei o monitor de atividade mostra que a consulta está em execução.

No log de eventos, não há erros.

Desempenho:

  • Tempdb no disco A (850 gb de espaço livre)
  • arquivo de banco de dados no disco B (espaço livre de 750 gb)
  • 16 GB de RAM

Por favor, sugira-me o que devo fazer?

A pergunta

UPDATE
    dbo.table1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
    dbo.table2 t2
WHERE
    LEFT(dbo.test1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 
Por sorte
fonte

Respostas:

3

Há um detalhe interessante nessa consulta que não identifiquei no início. Graças à resposta de Fabricio Araujo, agora a vejo: você está acessando duas tabelas. Eu nunca vi esse tipo de uso da declaração de atualização antes e não aconselho usá-la. Eu recomendo que você use a sintaxe de junção mais intuitiva de acordo com a resposta do Fabricio.

A causa provável é que a junção entre as duas tabelas produz um número extremo de linhas. Isso pode acontecer se a LEFT(col, 3)expressão produzir valores duplicados. Se produzir 10 duplicatas, isso resultará em 100000x100000 = 10000000000 linhas no resultado da junção.

Não acho que a indexação tenha um papel aqui. O SQL Server pode resolver essa junção não indexada muito bem com uma junção de hash ou mesclagem. Não leva 4 dias.

A outra causa provavelmente seria uma subestimação da cardinalidade das entradas ou saídas da junção. O SQL Server pode ter escolhido uma associação de loop.

Como ainda é especulação, recomendo que você publique o plano de consulta que esclarecerá esse problema.

usr
fonte
8

Esta consulta requer que você verifique todas as linhas da tabela porque

  • Eu acho que procodet ou ProviderCode não estão indexados
  • Mesmo se eles foram indexados, você tem uma esquerda que é uma função em um predicado WHERE
  • E você também tem COLLATE, que é efetivamente uma função em um predicado WHERE

"uma função em um predicado WHERE" significa que índices não serão usados

Se você o fizer em lote (digamos em UPDATE TOP (10000) ... AND costPercentage IS NULL), será necessário um índice em costPercentage e isso pressupõe que você esteja configurando-o.

As únicas soluções que vejo são

  • preencher uma nova tabela em lotes, com base, digamos, na chave primária
  • crie colunas computadas indexadas para ocultar as expressões LEFT e COLLATE e execute a atualização
gbn
fonte
@ gbn .. obrigado, é uma ótima idéia .. mas, como os dados estão em milhões, esse processo levará tempo .... eu estava pensando que talvez haja uma maneira de descobrir o andamento da consulta?
Sorte
1
Por que levaria 4 dias para digitalizar "milhões" de linhas? Não importa quão grandes e altamente indexadas as linhas possam ser, isso não deve levar quatro dias. A raiz do problema ainda é desconhecida.
usr
1
Se você lida regularmente com grandes dados, e se você conseguir um servidor adequado para isso? Coloque os dados em um SSD etc.
TomTom
1
@ Sorte certeza. Eu estava abordando a resposta. Há algo errado que ainda não encontramos. Não é a consulta por si só ou pelo hardware. Isso nunca equivaleria a 4 dias de duração.
usr
3
Dado que a consulta está unindo uma parte de 3 caracteres de uma coluna a uma parte de 3 caracteres de outra coluna, o resultado provavelmente conterá duplicatas. Isso é muito pior do que apenas atualizar milhões de linhas. Aposto que está varrendo bilhões de dólares em uma mesa de trabalho.
datagod 25/05
4

Primeiro, altere a consulta para:

UPDATE t1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
  dbo.table1 t1
  inner join dbo.table2 t2
    on LEFT(t1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 

Conforme indicado pelo primeiro post de Jeff Moden nessa discussão , sua consulta é muito semelhante à que ele alertou sobre o "efeito Halloween".

Depois disso, essas expressões LEFT devem ser indexadas. A resposta do gbn fornece as dicas de como fazer isso.

Fabricio Araujo
fonte