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.
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:
(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
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:
--------------------------------------------------------------------------------
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.
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.
Quelques suggestions:
Supprimez tous les index contenant FIELD avant d'exécuter votre instruction UPDATE, puis rajoutez-les ultérieurement.
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.
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.