Oracle: WITH statement performing slowly

Go To StackoverFlow.com

0

Recently I have been trying to incorperate more WITH statements into my Oracle SQL in order to create cleaner, more efficient code. However, I continue to feel like it is actually less efficient, but only under certain conditions, which is frustrating.

One example is a WITH statement created to rank phone numbers. I want it to retrieve on the best of all the valid/active phone numbers.

Here's an example of the WITH statement I'm using for ranking phone numbers:

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
)

PHONE_RANK runs fairly quickly if joined to the SPRIDEN table. However, when I try to limit it by the RANK_NO, it takes much longer to run.

  • With out any criteria on PHONE_RANK, it runs in less than 0.2 seconds.
  • With and PHONE_RANK.RANK_NO = 1 it takes about 3.5 seconds.
  • When using and PHONE_RANK.RANK_NO = 1 it is a part of more complex code, it tends to scale.

(Begin Edit)

I have used Subqueries for this process. The speed is great, and they are decent for phone numbers. However, since the most common numbers are PR and MA, and one or the other may not exist, I to use two subqueries to pull each one.

My "plan" is simple: Pull the best record for each student/person based on the Primary Indicator and Telephone Code. Thus, if there is only a PR or MA record, we will still get a result, since the best will be pulled. However, sometimes no Phone MA or PR # will exist, but we still want the student information, thus having to limit the query inside the main query is not ideal (but a second WITH statement should be able to take care of that).

Ultimately, I would like to apply the result to the Address table, which is similar to SPRTELE. A subquery to pull a phone number is fine, but using separate subqueries to pull the street, city, state and zip is not ideal. And, again, we have to pull both PR and MA address, in case one or the other does not exist.

Both the Address and Phone tables are set up poorly in my opinion, as one can have multiples of any Phone/ADDRESS Type (TELE_CODE, ATYP_CODE), and all/none of the records for each type may be active. Also, the data is not maintained well (and it would be a beast to maintain it).

For the SPRTELE table field (indexes are starred):

*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

If it is useful, here is the subquery I am use for phone numbers. I also include one for Permanent Phone numbers (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

Also, these are the Subqueries used to pull ONE address. Again, I have to use two to get both MA and PR address types, in case one or the other is missing.

--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,

(End Edit)

Any help with the following would be great:

  1. Why would selecting 'RANK_NO = 1' cause such a lack in performance? Is it the WITH statement, the Partition By, or something else?
  2. Is there a better method than using "Partition By" and then selecting RANK_NO = 1 to get the top phone number?
  3. Suggestions to improve the above code.
  4. Any other suggestions.
2012-04-05 20:53
by Phillip
What is the table structure? What are the indexes? Lastly, what is the full SQL statement as this wouldn't work... especially the indexes; no-one's magical and we can't help without all the informatio - Ben 2012-04-05 21:02
In addition to what Ben said: Have you tried "un"-refactoring the query in the WITH clause back into your main query (as subqueries, most likely) to see how it performs in that case - FrustratedWithFormsDesigner 2012-04-05 21:05
Use query analyzer to tell you exactly what Oracle's doing for each query. There's no need to have us guess - Josh 2012-04-05 22:28
My guess is that the 0.2 seconds, you measured only the response time of fetching the first few rows, using a tool like TOAD. What time does it take if you fetch all rows - Rob van Wijk 2012-04-06 12:07
A lot of information has been added between the "(Begin Edit)" and "(End Edit)" section. If anything additional is needed let me know - Phillip 2012-04-06 15:06
@Josh, I'm using a program called Argos, and am pretty much limited to SQL only statements. However, would I be able to use Query Analyzer despite this fact - Phillip 2012-04-06 15:06
@RobvanWijk It was pulled using a majority of the database. And yes, it was run in a tool called Argos (but I wasn't using the preview to time it). I basically had to use a stop watch and time each one from the time I hit "Execute" till when the data displayed - Phillip 2012-04-06 15:07


1

In this query, the hard(expensive) operation is to obtain the rank - row_number function - this requires (sometimes not explicit in the query) sorts.

  1. Probably this is because when you don't write RANK_NO = 1, you don't use the RANK_NO column elswere, so Oracle does not need to calculate it.

  2. you can order the inner query, and, in the outer queryes, use rownum, but you can't have partitions. You have one partion, a sigle 1, a single 2, and so on.

  3. If you can filter the rows you're getting from SPRTELE table, You'll get better speed. Few rows -> better speed.
  4. You should see the entire explain plan in order to improve something. Post the explain plan of the query.
2012-04-06 06:18
by Florin Ghita
Thank you for this information. It makes a lot of sense that the SQL only runs the Partition when the column is actually used. Which is brilliant. As for the Explain Plan, where would you like me to post it? And do you want all the columns or just certain ones - Phillip 2012-04-06 15:32
when I'll have some time, I'll read what you added to question - Florin Ghita 2012-04-07 10:21
Sounds good. Thank you for your willingness to help - Phillip 2012-04-10 14:06
oh, sorry I was too busy theese days... I visited stackoverflow just for one minute.. - Florin Ghita 2012-04-11 05:22
Hey, no worries, I am just thankful folks are willing to help. If it is something you can't get to anytime soon, that's just fine. It is my problem and it isn't urgent. The fact you are willing to help despite this is great - Phillip 2012-04-11 13:49
ok, I read what you added, but it is not useful to me in this moment. Your problem is that when you use phone_rank view, you get performance issues. I want to see a query that has this problem. The query that join it with SPRIDEN is probably reasonable to last 3.5 sec. But do you have another examples - Florin Ghita 2012-04-11 20:33
Ads