Aggiornamento di una tabella con milioni di record, sono stati 4 giorni


12

Attualmente sto aggiornando una tabella con milioni di record, sono passati 4 giorni e la query è ancora in esecuzione.

Ho controllato che il monitor delle attività mostra che la query è in esecuzione.

Nel registro eventi non ci sono errori.

Per quanto riguarda le prestazioni:

  • Tempdb nel disco A (850 GB di spazio libero)
  • file di database nel disco B (750 GB di spazio libero)
  • Ram da 16 GB

Per favore, suggeriscimi cosa devo fare?

La domanda

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 

Risposte:


3

C'è un dettaglio interessante in questa query che all'inizio non ho individuato. Grazie alla risposta di Fabricio Araujo ora lo vedo: stai accedendo a due tavoli. Non ho mai visto questo tipo di utilizzo della dichiarazione di aggiornamento prima e non consiglio di usarla. Vi consiglio di usare la sintassi di join più intuitiva per la risposta di Fabricio.

La causa probabile è che l'unione tra le due tabelle produce un numero estremo di righe. Ciò potrebbe accadere se l' LEFT(col, 3)espressione produce valori duplicati. Se produce 10 duplicati, il risultato del risultato sarà 100000x100000 = 10000000000 righe.

Non credo che l'indicizzazione abbia un ruolo qui. SQL Server può risolvere bene questo join non indicizzato con un hash o un merge join. Non richiede 4 giorni.

L'altra causa probabilmente sarebbe una sottovalutazione della cardinalità degli input o degli output del join. SQL Server potrebbe aver scelto un join loop.

Poiché questa è ancora una speculazione, ti consiglio di pubblicare il piano di query che farà luce su questo problema.


8

Questa query richiede di scansionare ogni riga della tabella perché

  • Immagino che procodet o ProviderCode non siano indicizzati
  • Anche se fossero indicizzati, hai una SINISTRA che è una funzione su un predicato WHERE
  • E hai anche COLLATE che è effettivamente una funzione su un predicato WHERE

"una funzione su un predicato WHERE" significa che gli indici non verranno utilizzati

Se lo lotti (diciamo UPDATE TOP (10000) ... AND costPercentage IS NULL) allora hai bisogno di un indice su costPercentage e questo presuppone che lo stai impostando.

Le uniche soluzioni che vedo sono

  • popolare una nuova tabella in batch, basata, ad esempio, sulla chiave primaria
  • creare colonne indicizzate e calcolate per nascondere le espressioni LEFT e COLLATE, quindi eseguire l'aggiornamento

@ gbn .. grazie è un'ottima idea .. ma dato che i dati sono in milioni, questo processo richiederà tempo .... stavo pensando che potrebbe esserci un modo per scoprire l'avanzamento della query?
Lucky

1
Perché occorrerebbero 4 giorni per scansionare "milioni" di righe? Non importa quanto grandi e fortemente indicizzate possano essere le righe, ciò non dovrebbe richiedere 4 giorni. La radice del problema è ancora sconosciuta.
usr

1
Se gestisci regolarmente dati di grandi dimensioni, che ne pensi di ottenere un server adeguato per quello? Metti i dati su un SSD ecc.
TomTom

1
@Lucky sicuro. Stavo affrontando la risposta. C'è qualcosa di sbagliato che non abbiamo ancora trovato. Non è la query da sola o l'hardware. Ciò non sarebbe mai pari a 4 giorni di durata.
usr

3
Dato che la query unisce una porzione di 3 caratteri di una colonna a una porzione di 3 caratteri di un'altra colonna, il risultato con ogni probabilità conterrà duplicati. Questo è molto peggio del semplice aggiornamento di milioni di righe. Scommetto che sta scansionando un tavolo da lavoro tra miliardi.
datagod

4

Prima di tutto, modifica la query in:

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 

Come indicato dal primo post di Jeff Moden in quella discussione , la tua domanda è molto simile a quella che ha messo in guardia sull'effetto Halloween.

Successivamente, tali espressioni LEFT devono essere indicizzate. La risposta di gbn ti dà le indicazioni su come farlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.