0

最近、よりクリーンで効率的なコードを作成するために、より多くの WITH ステートメントを Oracle SQL に取り込もうとしています。ただし、実際には効率が悪いと感じ続けていますが、それは特定の条件下でのみであり、イライラしています.

1 つの例は、電話番号をランク付けするために作成された WITH ステートメントです。すべての有効な/アクティブな電話番号の中で最高のものを取得したい.

電話番号のランク付けに使用している WITH ステートメントの例を次に示します。

Select * From (
WITH
PHONE_RANK as        
-- Description: Phone numbers with a RANK_NO generated based on importance.
(
    Select 
        ID as ID,
        PHONE_AREA || PHONE_NUMBER || PHONE_EXT as PHONE_NUMBER,
        TELE_CODE as TELE_CODE,
        PRIMARY_IND as PRIMARY_IND,
    --Generate a RANK_NO
        row_Number() over
        (
            Partition By ID
            Order By 
                ID,
                Decode(PRIMARY_IND,  'Y',1,  2),
                Decode(TELE_CODE,  'MA',1,  'PR',2,  'CA',3,  'CELL',4,  99)
        ) as RANK_NO
    From 
        SPRTELE
    Where 
        STATUS_IND is null
        and TELE_CODE in ('MA', 'PR')
        and Length(PHONE_AREA || PHONE_NUMBER || PHONE_EXT) >= 7
)

Select SPRIDEN.ID, PHONE_NUMBER
From SPRIDEN, PHONE_RANK  --SPRIDEN contains basic info (name, id, etc)
Where 
    SPRIDEN.CHANGE_IND is NULL
    SPRIDEN.ID = PHONE_RANK.ID
    and RANK_NO = 1
)

SPRIDEN テーブルに結合されている場合、PHONE_RANK はかなり高速に実行されます。ただし、RANK_NO で制限しようとすると、実行に時間がかかります。

  • PHONE_RANK の基準がない場合、0.2 秒未満で実行されます。
  • and PHONE_RANK.RANK_NO = 1約3.5秒かかります 。
  • それを使用and PHONE_RANK.RANK_NO = 1するのがより複雑なコードの一部である場合、スケーリングする傾向があります。

(編集開始)

このプロセスにはサブクエリを使用しました。速度は素晴らしく、電話番号としてはまともです。ただし、最も一般的な番号は PR と MA であり、どちらか一方が存在しない可能性があるため、2 つのサブクエリを使用してそれぞれをプルします。

私の「計画」は単純です。主な指標と電話番号に基づいて、各生徒/個人の最高の記録を引き出します。したがって、PR レコードまたは MA レコードのみがある場合でも、最良のものが引き出されるため、結果が得られます。ただし、Phone MA または PR # が存在しない場合もありますが、それでも学生の情報が必要なため、メイン クエリ内でクエリを制限する必要があるのは理想的ではありません (ただし、2 つ目の WITH ステートメントで対応できるはずです)。

最終的には、結果を SPRTELE に似た Address テーブルに適用したいと思います。電話番号をプルするサブクエリは問題ありませんが、別のサブクエリを使用して番地、都市、州、郵便番号をプルするのは理想的ではありません。また、どちらか一方が存在しない場合に備えて、PR と MA の両方のアドレスをプルする必要があります。

私の意見では、Address テーブルと Phone テーブルの設定が適切ではありません。任意の Phone/ADDRESS タイプ (TELE_CODE、ATYP_CODE) の倍数を持つことができ、各タイプのすべてのレコードがアクティブである/まったくない可能性があるためです。また、データは適切に維持されていません (そして、それを維持するのは獣です)。

SPRTELE テーブル フィールドの場合 (インデックスにはスターが付いています):

*ID (004000, 123456, etc)
*SEQNO (1, 2... n)
*TELE_CODE (MA, PR, etc)
ACTIVITY_DATE
COMMENT
CTRY_CODE_PHONE
DATA_ORIGIN
INTL_ACCESS
PHONE_AREA
PHONE_EXT
PHONE_NUMBER
PRIMARY_IND
ADDR_SEQNO
ATYP_CODE
STATUS_IND
UNLIST_IND
USER_ID

役に立つ場合は、電話番号に使用しているサブクエリを次に示します。また、固定電話番号 (TELE_CODE = 'PR') の 1 つも含めます。

(
Select PHONE_AREA || PHONE_NUMBER || PHONE_EXT
From SPRTELE
Where 
    ID = S1.ID
    and STATUS_IND is Null
    and TELE_CODE = 'MA'
    and SEQNO =
    (
        Select Max(SEQNO)
        From SPRTELE
        Where 
            ID = S1.ID
            and STATUS_IND is Null
            and TELE_CODE = 'MA'
    )
) as MA_Phone

また、これらは 1 つのアドレスを取得するために使用されるサブクエリです。繰り返しになりますが、MA と PR の両方のアドレス タイプを取得するには、どちらか一方が欠落している場合に備えて、2 つを使用する必要があります。

--MAILING STREET ADDRESS
(
    Select STREET_LINE1 || ' ' || STREET_LINE2 || ' '|| STREET_LINE3
    From SPRADDR S2
    Where 
        S2.ID = S1.ID
        and ATYP_CODE = 'MA'
        and STATUS_IND is NULL
        and SEQNO =
        (
            Select MAX(SEQNO)
            From SPRADDR S2
            Where 
                S2.ID = S1.ID
                and ATYP_CODE = 'MA'
                and STATUS_IND is NULL
        )
) as MA_Street,

--MAILING CITY STATE ZIP
(
    Select CITY || ', ' || STAT_CODE || ' ' || ZIP
    From SPRADDR S2
    Where 
        S2.ID = S1.ID
        and ATYP_CODE = 'MA'
        and STATUS_IND is NULL
        and SEQNO =
        (
            Select MAX(SEQNO)
            From SPRADDR S2
            Where 
                S2.ID = S1.ID
                and ATYP_CODE = 'MA'
                and STATUS_IND is NULL
        )
) as MA_Address,

(編集終了)

次のことについての助けは素晴らしいでしょう:

  1. 「RANK_NO = 1」を選択すると、パフォーマンスが低下するのはなぜですか? それは WITH ステートメントですか、Partition By ですか、それとも何か他のものですか?
  2. 「Partition By」を使用し、RANK_NO = 1 を選択して一番上の電話番号を取得するよりも良い方法はありますか?
  3. 上記のコードを改善するための提案。
  4. その他の提案。
4

1 に答える 1

1

このクエリでは、困難な (費用のかかる) 操作は、ランク (row_number 関数) を取得することです。これには、(クエリで明示的でない場合もあります) 並べ替えが必要です。

  1. おそらくこれは、RANK_NO = 1 と書かないと RANK_NO 列を使用しないため、Oracle が計算する必要がないためです。

  2. 内側のクエリを並べ替え、外側のクエリで rownum を使用できますが、パーティションを使用することはできません。パーティションが 1 つ、シングル 1、シングル 2 などがあります。

  3. SPRTELE テーブルから取得している行をフィルター処理できれば、速度が向上します。行数が少ない -> 速度が向上します。
  4. 何かを改善するには、Explain Plan 全体を確認する必要があります。クエリの説明計画を投稿します。
于 2012-04-06T06:18:17.680 に答える