sql >> Database teknologi >  >> RDS >> Oracle

Få de første 10 ubrugte manual_sequence-numre

Først Jeg ville erstatte denne sofistikerede underforespørgsel:

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')
 

med denne:

Select Rownum As seq_number From Dual 
Connect By Rownum <= (Select max( seq_number ) + 10 From TEMP_TABLE_NAME ) 
 

eller endda med en simpel konstant:

Select Rownum As seq_number From Dual Connect By Rownum <= 1000000
 

Din underforespørgsel virker ærlig talt ikke for et meget grundlæggende tilfælde:

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
 

Og et andet tilfælde:

create table TEMP_TABLE_NAME(
  seq_number NUMBER(15,0)
);
 

i dette tilfælde forsøger underforespørgslen at generere 9999999999999999 rækker, hvilket hurtigt fører til fejl i hukommelsen

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.
 

For det andet er din forespørgsel ikke deterministisk !!!
Det afhænger stærkt af en fysisk tabelstruktur og pålægger ikke den korrekte rækkefølge ved hjælp af ORDER BY klausul.
Husk ->Wikipedia - BESTIL AF

Overvej denne testcase:

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
  ;
 

Hvis indekset eksisterer, så er resultatet 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
 

Men hvad sker der, når nogen en dag sletter indekset, eller optimeringsværktøjet af en eller anden grund beslutter sig for ikke at bruge det indeks?
Ifølge definitionen:Uden ORDER BY kan relationsdatabasesystemet returnere rækkerne i enhver bestille. Jeg simulerer disse tilfælde ved at bruge et tip:

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
 

Nedenstående forespørgsel håndhæver en korrekt rækkefølge ved hjælp af ORDER BY klausul og giver reproductibe-resultater uanset om det korrekte indeks eksisterer eller ej.
Jeg bruger den anbefalede ANSI SQL LEFT JOIN-klausul i stedet for forældet WHERE .... (+) syntaks.

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

Ydeevne
Den nemmeste måde at kontrollere ydeevnen på er at lave en test - kør forespørgslen 10-100 gange og mål tiden:

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 gange - 11,75 sek., så én forespørgsel tager 1,2 sek.

Og en næste version med en grænse i CONNECT BY bruger en underforespørgsel:

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
 

Meget bedre - kun 100 millisekunder.
Dette førte til den konklusion, at CONNECT BY del er den dyreste.

Endnu et forsøg, der bruger en tabel med forudgenereret talsekvens op til 1 mln (en slags materialiseret visning) i stedet for CONNECT BY underforespørgsel, der genererer tal hver gang i farten i hukommelsen:

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
 

Denne er den hurtigste - kun 40 ms

Den første 1200 ms, den sidste 40 ms - 30 gange hurtigere (3000 %).




  1. Vedvarende UUID i PostgreSQL ved hjælp af JPA

  2. Sådan starter parallelle planer op – del 1

  3. Kan jeg affyre en hændelse på connect-databasen i Entity Framework Core?

  4. Søgeordssøgning ved hjælp af PHP MySql?