Mise à jour d'une table avec des millions d'enregistrements, cela fait 4 jours

12

Je suis en train de mettre à jour une table avec des millions d'enregistrements, cela fait 4 jours et la requête est toujours en cours d'exécution.

J'ai vérifié que le moniteur d'activité montre que la requête est en cours d'exécution.

Dans le journal des événements, il n'y a aucune erreur.

En termes de performances:

  • Tempdb dans le disque A (850 Go d'espace libre)
  • fichier de base de données sur le disque B (750 Go d'espace libre)
  • 16 Go de RAM

Veuillez me suggérer que dois-je faire?

La requête

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 
Chanceux
la source

Réponses:

3

Il y a un détail intéressant à cette requête que je n'ai pas repéré au début. Grâce à la réponse de Fabricio Araujo, je le vois maintenant: vous accédez à deux tables. Je n'ai jamais vu ce genre d'utilisation de la déclaration de mise à jour auparavant et je ne conseille pas de l'utiliser. Je vous recommande d'utiliser la syntaxe de jointure plus intuitive selon la réponse de Fabricio.

La cause probable est que la jointure entre les deux tables produit un nombre extrême de lignes. Cela peut se produire si l' LEFT(col, 3)expression produit des valeurs en double. S'il produit 10 doublons, cela se traduira par 100000x100000 = 10000000000 lignes dans le résultat de la jointure.

Je ne pense pas que l'indexation joue un rôle ici. SQL Server peut résoudre cette jointure non indexée très bien avec un hachage ou une jointure de fusion. Ne prend pas 4 jours.

L'autre cause probable serait une sous-estimation de la cardinalité des entrées ou des sorties de jointure. SQL Server a peut-être choisi une jointure en boucle.

Comme il s'agit toujours de spéculations, je vous recommande de publier le plan de requête qui éclairera ce problème.

usr
la source
8

Cette requête vous oblige à analyser chaque ligne du tableau car

  • Je suppose que procodet ou ProviderCode ne sont pas indexés
  • Même s'ils ont été indexés, vous avez un LEFT qui est une fonction sur un prédicat WHERE
  • Et vous avez aussi COLLATE qui est effectivement une fonction sur un prédicat WHERE

"une fonction sur un prédicat WHERE" signifie que les index ne seront pas utilisés

Si vous le regroupez (par exemple sur UPDATE TOP (10000) ... ET costPercentage IS NULL), vous avez besoin d'un index sur costPercentage et cela suppose que vous le définissez.

Les seules solutions que je vois sont

  • remplir une nouvelle table par lots, en fonction, par exemple, de la clé primaire
  • créer des colonnes indexées et calculées pour masquer les expressions LEFT et COLLATE, puis exécuter la mise à jour
gbn
la source
@ gbn .. merci c'est une excellente idée .. mais comme les données sont en millions ce processus prendra du temps .... je pensais peut-être qu'il y avait un moyen de découvrir la progression de la requête?
Lucky
1
Pourquoi faudrait-il 4 jours pour analyser des "millions" de lignes? Quelle que soit la taille et l'indexation des lignes, cela ne devrait pas prendre 4 jours. La racine du problème est encore inconnue.
usr
1
Si vous traitez régulièrement des données volumineuses, qu'en est-il pour obtenir un serveur approprié? Mettez les données sur un SSD etc.
TomTom
1
@Lucky bien sûr. J'adressais la réponse. Il y a quelque chose de mal que nous n'avons pas encore trouvé. Ce n'est pas la requête en elle-même ou le matériel. Cela ne représenterait jamais 4 jours de durée.
usr
3
Étant donné que la requête joint une partie de 3 caractères d'une colonne à une partie de 3 caractères d'une autre colonne, le résultat contiendra très probablement des doublons. C'est bien pire que de simplement mettre à jour des millions de lignes. Je parie qu'il balaye une table de travail par milliards.
datagod
4

Tout d'abord, changez la requête en:

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 

Comme indiqué par le premier post de Jeff Moden dans cette discussion , votre requête est très similaire à celle qu'il a mise en garde contre "l'effet Halloween".

Après cela, ces expressions LEFT doivent être indexées. La réponse de gbn vous donne des indications sur la façon de procéder.

Fabricio Araujo
la source