Zuerst Ich würde diese anspruchsvolle Unterabfrage ersetzen:
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')
mit diesem hier:
Select Rownum As seq_number From Dual
Connect By Rownum <= (Select max( seq_number ) + 10 From TEMP_TABLE_NAME )
oder sogar mit einer einfachen Konstante:
Select Rownum As seq_number From Dual Connect By Rownum <= 1000000
Ihre Unterabfrage funktioniert ehrlich gesagt nicht für einen sehr einfachen Fall:
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
Und ein zweiter Fall:
create table TEMP_TABLE_NAME(
seq_number NUMBER(15,0)
);
In diesem Fall versucht die Unterabfrage, 999999999999999 Zeilen zu generieren, was schnell zu einem Speichermangel führt
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.
Zweitens ist Ihre Abfrage nicht deterministisch !!!
Es hängt stark von einer physischen Tabellenstruktur ab und erzwingt nicht die richtige Reihenfolge mit ORDER BY
Klausel.
Denken Sie an ->Wikipedia - ORDER BY
Betrachten Sie diesen Testfall:
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
;
Wenn der Index existiert, ist das Ergebnis 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
Aber was passiert, wenn eines Tages jemand den Index löscht oder der Optimierer aus irgendwelchen Gründen beschließt, diesen Index nicht zu verwenden?
Gemäß der Definition:Ohne ORDER BY kann das relationale Datenbanksystem die Zeilen in jedem zurückgeben bestellen. Ich simuliere diese Fälle mit einem Hinweis:
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
Die folgende Abfrage erzwingt eine korrekte Reihenfolge mit ORDER BY
-Klausel und liefert reproduzierbare Ergebnisse, unabhängig davon, ob der richtige Index vorhanden ist oder nicht.
Ich verwende die empfohlene ANSI SQL LEFT JOIN-Klausel anstelle des veralteten WHERE .... (+)
Syntax.
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
Leistung
Der einfachste Weg, die Leistung zu überprüfen, ist ein Test - führen Sie die Abfrage 10-100 Mal aus und messen Sie die Zeit:
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 Mal - 11,75 Sek., also dauert eine Abfrage 1,2 Sek.
Und eine nächste Version mit einem Limit in CONNECT BY
verwendet eine Unterabfrage:
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
Viel besser - nur 100 Millisekunden.
Dies führte zu dem Schluss, dass der CONNECT BY
Teil ist am teuersten.
Ein weiterer Versuch, der anstelle des CONNECT BY
eine Tabelle mit vorgenerierter Zahlenfolge bis 1 Mio. verwendet (eine Art materialisierte Ansicht). Unterabfrage, die jedes Mal spontan Zahlen im Speicher generiert:
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
Dieser ist der schnellste - nur 40 ms
Der erste 1200 ms, der letzte 40 ms - 30 mal schneller (3000 %).