sql >> データベース >  >> RDS >> Oracle

最初の10個の未使用のmanual_sequence番号を取得する

    最初 この洗練されたサブクエリを置き換えます:

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

    これで:

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

    または単純な定数でも:

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

    率直に言って、サブクエリは非常に基本的なケースでは機能しません。

    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
    

    そして2番目のケース:

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

    この場合、サブクエリは999999999999999行を生成しようとしますが、これはすぐにメモリ不足エラーにつながります

    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.
    

    次に、クエリは決定論的ではありません!!!
    これは物理テーブル構造に強く依存し、ORDER BYを使用して正しい順序を強制しません 条項。
    覚えておいてください-> Wikipedia-ORDER BY

    このテストケースを考えてみましょう:

    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
      ;
    

    インデックスが存在する場合、結果は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
    

    しかし、いつか誰かがインデックスを削除したり、何らかの理由でオプティマイザがそのインデックスを使用しないことを決定した場合はどうなりますか?
    定義によると、 ORDER BYがないと、リレーショナルデータベースシステムは任意の行を返す可能性があります。注文します。 ヒントを使用してこれらのケースをシミュレートします:

    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
    

    以下のクエリは、ORDER BYを使用して適切な順序を適用します 句を指定し、適切なインデックスが存在するかどうかに関係なく、再現結果を提供します。
    代わりに、推奨されるANSI SQLLEFTJOIN句を使用しています。WHERE .... (+) 構文。

    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
    

    パフォーマンス
    パフォーマンスを確認する最も簡単な方法は、テストを実行することです。クエリを10〜100回実行し、時間を測定します。

    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回-11.75秒なので、1回のクエリには1.2秒かかります。

    そして、CONNECT BYの制限がある次のバージョン サブクエリを使用します:

    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
    

    はるかに優れています-わずか100ミリ秒です。
    これにより、CONNECT BYという結論に至ります。 一部が最もコストがかかります。

    CONNECT BYの代わりに、最大1 mln(マテリアライズドビューの一種)までの番号の事前生成されたシーケンスを持つテーブルを使用する別の試み メモリ内でその場で毎回数値を生成するサブクエリ:

    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
    

    これは最速です-わずか40ミリ秒

    最初のものは1200ミリ秒、最後のものは40ミリ秒-30倍速い(3000%)。




    1. SQL / MySQL:数量値を日付ごとに複数の行に分割

    2. mysqlを介してテーブルで最も人気のある単語を取得するにはどうすればよいですか?

    3. SELECT * .. LiMIT start、ORDER BY句なしでカウントを使用してMySQLのテーブルをスキャンするのは正しいですか?

    4. Oracleの「INSERTALL」は重複を無視します