Pertama Saya akan mengganti subquery canggih ini:
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')
dengan yang ini:
Select Rownum As seq_number From Dual
Connect By Rownum <= (Select max( seq_number ) + 10 From TEMP_TABLE_NAME )
atau bahkan dengan konstanta sederhana:
Select Rownum As seq_number From Dual Connect By Rownum <= 1000000
Subquery Anda sejujurnya tidak berfungsi untuk kasus yang sangat mendasar:
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
Dan kasus kedua:
create table TEMP_TABLE_NAME(
seq_number NUMBER(15,0)
);
dalam hal ini subquery mencoba menghasilkan 999999999999999 baris, yang dengan cepat menyebabkan kesalahan kehabisan memori
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.
Kedua, kueri Anda tidak deterministik !!!
Ini sangat tergantung pada struktur tabel fisik dan tidak memaksakan urutan yang benar menggunakan ORDER BY
klausa.
Ingat ->Wikipedia - ORDER BY
Pertimbangkan kasus uji ini:
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
;
Jika indeks ada, maka hasilnya 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
Tetapi apa yang terjadi ketika suatu hari seseorang menghapus indeks, atau pengoptimal karena beberapa alasan memutuskan untuk tidak menggunakan indeks itu?
Menurut definisi:Tanpa ORDER BY, sistem basis data relasional dapat mengembalikan baris dalam format apa pun pesan. Saya mensimulasikan kasus-kasus ini menggunakan petunjuk:
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
Kueri di bawah ini memberlakukan urutan yang benar menggunakan ORDER BY
klausa dan memberikan hasil reproduktif terlepas dari indeks yang tepat ada atau tidak.
Saya menggunakan klausa ANSI SQL LEFT JOIN yang direkomendasikan sebagai gantinya WHERE .... (+)
usang sintaks.
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
Kinerja
Cara termudah untuk memeriksa kinerja adalah dengan melakukan tes - jalankan kueri 10- 100 kali dan ukur waktunya:
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 kali - 11,75 detik, jadi satu kueri membutuhkan 1,2 detik.
Dan versi berikutnya di mana batas di CONNECT BY
menggunakan subkueri:
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
Jauh lebih baik - hanya 100 milidetik.
Ini mengarah pada kesimpulan, bahwa CONNECT BY
bagian adalah yang paling mahal.
Upaya lain yang menggunakan tabel dengan urutan angka yang dibuat sebelumnya hingga 1 juta (semacam tampilan terwujud) alih-alih CONNECT BY
subquery yang menghasilkan angka setiap kali dengan cepat di memori:
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
Yang ini tercepat - hanya 40 md
Yang pertama 1200 md, yang terakhir 40 md - 30 kali lebih cepat (3000 %).