2010-01-13 7 views
1

Nous utilisons une table PL/SQL (appelée pTable) pour collecter un certain nombre d'identifiants à mettre à jour.Mise à jour lente lors de l'utilisation d'Oracle PL/SQL Table

Cependant, la déclaration

UPDATE aTable 
SET aColumn = 1 
WHERE id IN (SELECT COLUMN_VALUE 
        FROM TABLE (pTable)); 

prend beaucoup de temps à exécuter.

Il semble que l'optimiseur propose un plan d'exécution très mauvais, au lieu d'utiliser l'index défini sur id (en tant que clé primaire), il décide d'utiliser un scan de table complet sur aTable. pTable contient généralement très peu de valeurs (dans la plupart des cas un seul).

Que pouvons-nous faire pour accélérer les choses? Le meilleur que nous ayons trouvé est de gérer des pTable.Count bas (1 et 2) comme des cas spéciaux, mais ce n'est certainement pas très élégant.

Merci pour toutes les excellentes suggestions. J'ai écrit sur ce sujet dans mon blog au http://smartercoding.blogspot.com/2010/01/performance-issues-using-plsql-tables.html.

+2

vous avez oublié quelque chose ici je pense où est la déclaration –

+0

Oui désolé, j'ai appuyé sur le retour et que déjà posté la question. – Thorsten

+0

Combien de lignes y a-t-il dans unTable? Y a-t-il d'autres index sur unTable? – Timothy

Répondre

5

Vous pouvez essayer l'astuce de cardinalité. C'est bien si vous connaissez (grosso modo) le nombre de lignes dans la collection.

UPDATE aTable 
SET aColumn = 1 
WHERE id IN (SELECT /*+ cardinality(pt 10) */ 
        COLUMN_VALUE 
       FROM TABLE (pTable) pt); 
+0

+1 pour l'indice de cardinalité. Cela aidera à coup sûr. – Guru

+0

+1 cela a aidé à créer le "bon" plan d'exécution – Thorsten

+0

J'accepte cette réponse car c'est probablement la solution la plus propre. – Thorsten

2

Le mauvais plan d'exécution est probablement inévitable (malheureusement). Il n'y a pas d'informations de statistiques pour la table PL/SQL, donc l'optimiseur n'a aucun moyen de savoir qu'il y a peu de lignes dedans. Est-il possible d'utiliser des astuces dans un UPDATE? Si c'est le cas, vous pourriez forcer l'utilisation de l'index de cette façon.

+0

Merci Nils, j'ai appliqué l'indice pour utiliser l'index .. voir ma solution. – Thorsten

0

Je me demande si l'indicateur MATERIALIZE dans la sous-sélection de la table PL/SQL forcerait une instanciation de table temporaire et aiderait l'optimiseur?

UPDATE aTable 
SET aColumn = 1 
WHERE id IN (SELECT /*+ MATERIALIZE */ COLUMN_VALUE 
        FROM TABLE (pTable)); 
1

Il a contribué à indiquer à l'optimiseur d'utiliser l'indice « correct » au lieu d'aller sur une analyse sauvage table complète:

UPDATE /*+ INDEX(aTable PK_aTable) */aTable 
SET aColumn = 1 
WHERE id IN (SELECT COLUMN_VALUE 
        FROM TABLE (CAST (pdarllist AS list_of_keys))); 

Je ne pouvais pas appliquer cette solution à des scénarios plus complexes, mais trouvé d'autres solutions de contournement pour ceux-ci.

3

Voici une autre approche. Créer une table temporaire:

create global temporary table pTempTable (id int primary key) 
    on commit delete rows; 

Pour effectuer la mise à jour, remplir pTempTable avec le contenu de pTable et exécuter:

update 
(
    select aColumn 
    from aTable aa join pTempTable pp on aa.id = pp.id 
) 
set aColumn = 1; 

Le doit effectuer raisonnablement bien sans avoir recours à des conseils optimiseur.

+0

Bonne idée, je vais essayer. – Thorsten

1

Vous pourriez essayer d'ajouter une clause ROWNUM < .... Dans ce test, un ROWNUM < 30 modifie le plan d'utilisation d'un index. Bien sûr, cela dépend de votre ensemble de valeurs ayant une taille maximale raisonnable.

create table atable (acolumn number, id number); 
insert into atable select rownum, rownum from dual connect by level < 150000; 
alter table atable add constraint atab_pk primary key (id); 

exec dbms_stats.gather_table_stats(ownname => user, tabname => 'ATABLE'); 

create type type_coll is table of number(4); 
/

declare 
    v_coll type_coll; 
begin 
    v_coll := type_coll(1,2,3,4); 
    UPDATE aTable 
    SET aColumn = 1 
    WHERE id IN (SELECT COLUMN_VALUE 
        FROM TABLE (v_coll)); 
end; 
/

PLAN_TABLE_OUTPUT 
----------------------------------------------------------------------------------------------- 
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1)) 
---------------------------------------------------------------------------------------------- 
| Id | Operation       | Name | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------- 
| 0 | UPDATE STATEMENT     |  |  |  | 142 (100)|   | 
| 1 | UPDATE        | ATABLE |  |  |   |   | 
|* 2 | HASH JOIN RIGHT SEMI    |  |  1 | 11 | 142 (8)| 00:00:02 | 
| 3 | COLLECTION ITERATOR PICKLER FETCH|  |  |  |   |   | 
| 4 | TABLE ACCESS FULL    | ATABLE | 150K| 1325K| 108 (6)| 00:00:02 | 
---------------------------------------------------------------------------------------------- 

declare 
    v_coll type_coll; 
begin 
    v_coll := type_coll(1,2,3,4); 
    UPDATE aTable 
    SET aColumn = 1 
    WHERE id IN (SELECT COLUMN_VALUE 
        FROM TABLE (v_coll) 
          where rownum < 30); 
end; 
/


PLAN_TABLE_OUTPUT 
------------------------------------------------------------------------------------------------------ 
UPDATE ATABLE SET ACOLUMN = 1 WHERE ID IN (SELECT COLUMN_VALUE FROM TABLE (:B1) WHERE 
ROWNUM < 30) 

--------------------------------------------------------------------------------------------------- 
| Id | Operation        | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
--------------------------------------------------------------------------------------------------- 
| 0 | UPDATE STATEMENT      |   |  |  | 31 (100)|   | 
| 1 | UPDATE        | ATABLE |  |  |   |   | 
| 2 | NESTED LOOPS       |   |  1 | 22 | 31 (4)| 00:00:01 | 
| 3 | VIEW        | VW_NSO_1 | 29 | 377 | 29 (0)| 00:00:01 | 
| 4 |  SORT UNIQUE      |   |  1 | 58 |   |   | 
|* 5 |  COUNT STOPKEY      |   |  |  |   |   | 
| 6 |  COLLECTION ITERATOR PICKLER FETCH|   |  |  |   |   | 
|* 7 | INDEX UNIQUE SCAN     | ATAB_PK |  1 |  9 |  0 (0)|   | 
--------------------------------------------------------------------------------------------------- 
+0

+1 grand exemple .. Je vais tenter le coup! – Thorsten

Questions connexes