Oracle:WITH语句执行缓慢

时间:2012-04-05 20:53:54

标签: sql performance oracle with-statement

最近,我一直在尝试将更多WITH语句包含在我的Oracle SQL中,以便创建更清晰,更高效的代码。然而,我仍然觉得它实际上效率较低,但只是在某些条件下,这是令人沮丧的。

一个例子是创建一个用于对电话号码进行排名的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,而且可能不存在其中一个,我使用两个子查询来拉取每个子查询。

我的计划"很简单:根据主要指标和电话代码为每个学生/个人提取最佳记录。因此,如果只有PR或MA记录,我们仍然会得到一个结果,因为最好的将被拉。但是,有时候不存在Phone MA或PR#,但我们仍然需要学生信息,因此必须限制主查询中的查询并不理想(但第二个WITH语句应该能够处理)。 / p>

最终,我想将结果应用于Address表,类似于SPRTELE。拉取电话号码的子查询很好,但使用单独的子查询来拉动街道,城市,州和拉链并不理想。而且,我们必须同时提取PR和MA地址,以防其中一个或另一个不存在。

在我看来,地址和电话表都设置得很差,因为可以有任何电话/地址类型(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')。

(
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

此外,这些是用于拉出一个地址的子查询。同样,我必须使用两个来获取MA和PR地址类型,以防缺少一个或另一个。

--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. 任何其他建议。

1 个答案:

答案 0 :(得分:1)

在此查询中,硬(昂贵)操作是获取rank - row_number函数 - 这需要(有时在查询中不明确)进行排序。

  1. 可能这是因为当你不写RANK_NO = 1时,你不使用RANK_NO列elswere,因此Oracle不需要计算它。

  2. 您可以订购内部查询,并且在外部查询中使用rownum,但您不能拥有分区。你有一个分区,一个sigle 1,一个2,依此类推。

  3. 如果您可以从SPRTELE表中过滤掉您所获得的行,您将获得更快的速度。几行 - >更好的速度。
  4. 你应该看到整个解释计划,以改善一些事情。 发布查询的解释计划。