web-dev-qa-db-fra.com

Comment optimiser une mise à jour SQL qui s'exécute sur une table Oracle avec 700 millions de lignes

UPDATE [TABLE] SET [FIELD]=0 WHERE [FIELD] IS NULL

[TABLE] est une table de base de données Oracle contenant plus de 700 millions de lignes. J'ai annulé l'exécution SQL après 6 heures d'exécution.

Existe-t-il un indice SQL susceptible d’améliorer les performances? Ou toute autre solution pour accélérer cela?

EDIT: Cette requête sera exécutée une fois, puis plus jamais.

10
b.roth

Tout d’abord, s’agit-il d’une requête unique ou d’une requête récurrente? Si vous ne devez le faire qu'une fois, vous pouvez envisager de lancer la requête en mode parallèle. Quoi qu'il en soit, vous devrez analyser toutes les lignes. Vous pouvez soit diviser la charge de travail vous-même avec des plages de ROWID (parallélisme à faire soi-même), soit utiliser les fonctions intégrées d'Oracle.

En supposant que vous souhaitiez l'exécuter fréquemment et optimiser cette requête, le nombre de lignes avec la colonne field comme NULL sera éventuellement faible par rapport au nombre total de lignes. Dans ce cas, un index pourrait accélérer les choses. Oracle n'indexe pas les lignes contenant toutes les colonnes indexées sous la forme NULL. Par conséquent, votre index n'utilisera pas d'index field (car vous souhaitez rechercher toutes les lignes où field est NULL).

Non plus:

  • créer un index sur (FIELD, 0), le 0 agira comme une pseudo-colonne non NULL et toutes les lignes seront indexées sur la table.
  • créer un index basé sur une fonction sur (CASE WHEN field IS NULL THEN 1 END), cela indexera uniquement les lignes qui sont des valeurs NULL (l'index serait donc très compact). Dans ce cas, vous devrez réécrire votre requête:

    UPDATE [TABLE] SET [FIELD]=0 WHERE (CASE WHEN field IS NULL THEN 1 END)=1

Modifier:

Comme il s'agit d'un scénario ponctuel, vous pouvez utiliser l'indicateur PARALLEL:

SQL> EXPLAIN PLAN FOR
  2  UPDATE /*+ PARALLEL(test_table 4)*/ test_table
  3     SET field=0
  4   WHERE field IS NULL;

Explained

SQL> select * from table( dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 4026746538
--------------------------------------------------------------------------------
| Id  | Operation             | Name       | Rows  | Bytes | Cost (%CPU)| Time
--------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT      |            | 22793 |   289K|    12   (9)| 00:00:
|   1 |  UPDATE               | TEST_TABLE |       |       |            |
|   2 |   PX COORDINATOR      |            |       |       |            |
|   3 |    PX SEND QC (RANDOM)| :TQ10000   | 22793 |   289K|    12   (9)| 00:00:
|   4 |     PX BLOCK ITERATOR |            | 22793 |   289K|    12   (9)| 00:00:
|*  5 |      TABLE ACCESS FULL| TEST_TABLE | 22793 |   289K|    12   (9)| 00:00:
--------------------------------------------------------------------------------
10
Vincent Malgrat

Est-ce que d'autres utilisateurs mettent à jour les mêmes lignes de la table en même temps?

Si tel est le cas, vous pouvez rencontrer de nombreux problèmes de concurrence (attente de verrous) et il peut être intéressant de le diviser en petites transactions.

DECLARE
  v_cnt number := 1;
BEGIN
 WHILE v_cnt > 0 LOOP
   UPDATE [TABLE] SET [FIELD]=0 WHERE [FIELD] IS NULL AND ROWNUM < 50000;
   v_cnt := SQL%ROWCOUNT;
   COMMIT;
 END LOOP;
END;
/

Plus la limite ROWNUM est petite, moins vous rencontrerez de problèmes de concurrence/verrouillage, mais plus vous passerez de temps à analyser des tables.

5
Gary Myers

Vincent a déjà répondu parfaitement à votre question, mais je suis curieux de savoir pourquoi cette action est derrière. Pourquoi mettez-vous à jour tous les NULL à 0?

Cordialement, Rob.

3
Rob van Wijk

Quelques suggestions:

  1. Supprimez tous les index contenant FIELD avant d'exécuter votre instruction UPDATE, puis rajoutez-les ultérieurement.

  2. Pour ce faire, écrivez une procédure PL/SQL qui est validée toutes les 1000 ou 10 000 lignes.

J'espère que cela t'aides.

1
Bob Jarvis

Vous pouvez obtenir le même résultat sans effectuer de mise à jour en utilisant une table ALTER pour définir la valeur "DEFAULT" des colonnes sur 0.

0
James Anderson