Premier Je remplacerais cette sous-requête sophistiquée :
Select Rownum seq_number From Dual Connect By Rownum <=
(Select LPAD(9,(UTC.DATA_PRECISION - UTC.DATA_SCALE),9)
From User_Tab_Columns UTC
where UTC.Table_Name = 'Table_Name' And UTC.Column_Name = 'seq_number')
avec celui-ci :
Select Rownum As seq_number From Dual
Connect By Rownum <= (Select max( seq_number ) + 10 From TEMP_TABLE_NAME )
ou même avec une simple constante :
Select Rownum As seq_number From Dual Connect By Rownum <= 1000000
Votre sous-requête ne fonctionne franchement pas pour un cas très basique :
create table TEMP_TABLE_NAME(
seq_number NUMBER
);
SELECT LPAD (9,(UTC.DATA_PRECISION - UTC.DATA_SCALE),9) as x ,
UTC.DATA_PRECISION, UTC.DATA_SCALE, UTC.COLUMN_NAME
FROM User_Tab_Columns UTC
WHERE UTC.Table_Name = 'TEMP_TABLE_NAME'
AND UTC.Column_Name = 'SEQ_NUMBER'
;
X DATA_PRECISION DATA_SCALE COLUMN_NAME
-------- -------------- ---------- -----------
(null) (null) (null) SEQ_NUMBER
Et un deuxième cas :
create table TEMP_TABLE_NAME(
seq_number NUMBER(15,0)
);
dans ce cas, la sous-requête essaie de générer 999999999999999 lignes, ce qui conduit rapidement à une erreur de mémoire insuffisante
SELECT count(*) FROM (
SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <=
(SELECT LPAD (9,(UTC.DATA_PRECISION - UTC.DATA_SCALE),9)
FROM User_Tab_Columns UTC
WHERE UTC.Table_Name = 'TEMP_TABLE_NAME'
AND UTC.Column_Name = 'SEQ_NUMBER')
);
ORA-30009: Not enough memory for CONNECT BY operation
30009. 0000 - "Not enough memory for %s operation"
*Cause: The memory size was not sufficient to process all the levels of the
hierarchy specified by the query.
*Action: In WORKAREA_SIZE_POLICY=AUTO mode, set PGA_AGGREGATE_TARGET to
a reasonably larger value.
Or, in WORKAREA_SIZE_POLICY=MANUAL mode, set SORT_AREA_SIZE to a
reasonably larger value.
Deuxièmement votre requête n'est pas déterministe !!!
Cela dépend fortement d'une structure de table physique et n'impose pas le bon ordre en utilisant ORDER BY
clause.
N'oubliez pas ->Wikipedia - ORDER BY
Considérez ce cas de test :
create table TEMP_TABLE_NAME
as SELECT * FROM (
select rownum as seq_number , t.*
from ALL_OBJECTS t
cross join ( select * from dual connect by level <= 10)
where rownum <= 100000
)
ORDER BY DBMS_RANDOM.Value;
create unique index TEMP_TABLE_NAME_IDX on TEMP_TABLE_NAME(seq_Number);
select count(*) from TEMP_TABLE_NAME;
COUNT(*)
----------
100000
DELETE FROM TEMP_TABLE_NAME
WHERE seq_number between 10000 and 10002
OR seq_number between 20000 and 20002
OR seq_number between 30000 and 30002
OR seq_number between 40000 and 40002
OR seq_number between 50000 and 50002
OR seq_number between 60000 and 60002
;
Si l'index existe, alors le résultat est OK :
SELECT T1.*
FROM ( SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <= 1000000
) T1,
TEMP_TABLE_NAME T2
WHERE T1.seq_number = T2.seq_number(+)
AND T2.ROWID IS NULL
AND ROWNUM <= 10
;
SEQ_NUMBER
----------
10000
10001
10002
20000
20001
20002
30000
30001
30002
40000
Mais que se passe-t-il lorsqu'un jour quelqu'un supprime l'index, ou que l'optimiseur, pour certaines raisons, décide de ne pas utiliser cet index ?
Selon la définition :Sans ORDER BY, le système de base de données relationnelle peut renvoyer les lignes dans n'importe quel commande. Je simule ces cas en utilisant un indice :
SELECT /*+ NO_INDEX(T2) */ T1.*
FROM ( SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <= 1000000
) T1,
TEMP_TABLE_NAME T2
WHERE T1.seq_number = T2.seq_number(+)
AND T2.ROWID IS NULL
AND ROWNUM <= 10
;
SEQ_NUMBER
----------
213856
910281
668862
412743
295487
214762
788486
346216
777734
806457
La requête ci-dessous applique une commande appropriée en utilisant ORDER BY
clause et donne des résultats reproductibles, que l'index approprié existe ou non.
J'utilise la clause ANSI SQL LEFT JOIN recommandée à la place de l'obsolète WHERE .... (+)
syntaxe.
SELECT * FROM (
SELECT /*+ NO_INDEX(T2) */ T1.*
FROM ( SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <= 1000000
) T1
LEFT JOIN TEMP_TABLE_NAME T2
ON T1.seq_number = T2.seq_number
WHERE T2.ROWID IS NULL
ORDER BY T1.seq_number
)
WHERE ROWNUM <= 10
Performances
Le moyen le plus simple de vérifier les performances consiste à effectuer un test :exécutez la requête 10 à 100 fois et mesurez le temps :
SET TIMING ON;
DECLARE
x NUMBER;
BEGIN
FOR i IN 1..10 LOOP
SELECT sum( seq_number ) INTO x
FROM (
SELECT * FROM (
SELECT T1.*
FROM ( SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <= 1000000
) T1
LEFT JOIN TEMP_TABLE_NAME T2
ON T1.seq_number = T2.seq_number
WHERE T2.ROWID IS NULL
ORDER BY T1.seq_number
)
WHERE ROWNUM <= 10
);
END LOOP;
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:11.750
10 fois - 11,75 s, donc une requête prend 1,2 s.
Et une prochaine version où une limite dans CONNECT BY
utilise une sous-requête :
SET TIMING ON;
DECLARE
x NUMBER;
BEGIN
FOR i IN 1..10 LOOP
SELECT sum( seq_number ) INTO x
FROM (
SELECT * FROM (
SELECT T1.*
FROM ( SELECT ROWNUM seq_number
FROM DUAL
CONNECT BY ROWNUM <= (Select max( seq_number ) + 10 From TEMP_TABLE_NAME )
) T1
LEFT JOIN TEMP_TABLE_NAME T2
ON T1.seq_number = T2.seq_number
WHERE T2.ROWID IS NULL
ORDER BY T1.seq_number
)
WHERE ROWNUM <= 10
);
END LOOP;
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.986
Bien mieux - seulement 100 millisecondes.
Cela a conduit à la conclusion que le CONNECT BY
partie est la plus coûteuse.
Une autre tentative qui utilise une table avec une séquence pré-générée de nombres jusqu'à 1 million (sorte de vue matérialisée) au lieu de la CONNECT BY
sous-requête qui génère des nombres à chaque fois à la volée en mémoire :
create table seq(
seq_number int primary key
)
ORGANIZATION INDEX ;
INSERT INTO seq
SELECT level FROM dual
CONNECT BY LEVEL <= 1000000;
SET TIMING ON;
DECLARE
x NUMBER;
BEGIN
FOR i IN 1..10 LOOP
SELECT sum( seq_number ) INTO x
FROM (
SELECT * FROM (
SELECT T1.*
FROM seq T1
LEFT JOIN TEMP_TABLE_NAME T2
ON T1.seq_number = T2.seq_number
WHERE T2.ROWID IS NULL
ORDER BY T1.seq_number
)
WHERE ROWNUM <= 10
);
END LOOP;
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.398
Celui-ci est le plus rapide - seulement 40 ms
Le premier 1200 ms, le dernier 40 ms - 30 fois plus rapide (3000 %).