0

最近,我一直在尝试将更多 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 语句应该能够处理这个问题)。

最终,我想将结果应用于与 SPRTELE 类似的地址表。提取电话号码的子查询很好,但使用单独的子查询提取街道、城市、州和邮政编码并不理想。而且,我们必须同时提取 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')。

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

1 回答 1

1

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

  1. 可能这是因为当你不写RANK_NO = 1时,你没有使用其他的RANK_NO列,所以Oracle不需要计算它。

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

  3. 如果您可以过滤从 SPRTELE 表中获取的行,您将获得更快的速度。几行->更快的速度。
  4. 您应该查看整个解释计划以改进某些内容。发布查询的解释计划。
于 2012-04-06T06:18:17.680 回答