Re: Optimizer "enhancements" in 19.9?

From: Mikhail Velikikh <mvelikikh_at_gmail.com>
Date: Wed, 17 Feb 2021 17:56:21 +0000
Message-ID: <CALe4HpkSiOuRA3m07UJYE5JHQeH_TOLaZ9eW5GB==eL347G+9g_at_mail.gmail.com>



Hi Charles,

An over-simplified example to demonstrate the same issue which we discussed with Sayan:

create table t(id int, x date, y date);

I am running the queries below in both 19.4 and 19.9:

explain plan for
select *
  from t driver
 where x||y in (select --+ unnest

                       max(x||y)
                  from t
                 where id = driver.id);

select * from dbms_xplan.display();

*19.4: unnested*



| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time

    |



| 0 | SELECT STATEMENT | | 1 | 64 | 5 (20)|
00:00:01 |
|* 1 | HASH JOIN | | 1 | 64 | 5 (20)| 00:00:01 |
| 2 | TABLE ACCESS FULL | T | 1 | 31 | 2 (0)|
00:00:01 |
| 3 | VIEW | VW_SQ_1 | 1 | 33 | 3 (34)|
00:00:01 |
| 4 | HASH GROUP BY | | 1 | 31 | 3 (34)|
00:00:01 |
| 5 | TABLE ACCESS FULL| T | 1 | 31 | 2 (0)|
00:00:01 |

Predicate Information (identified by operation id):


   1 - access("MAX(X||Y)"=INTERNAL_FUNCTION("X")||INTERNAL_FUNCTION("Y")

               AND "ITEM_1"="DRIVER"."ID") *19.9: not unnested (the same as in your example)*



| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1 | 31 | 4 (0)| 00:00:01 |
|*  1 |  FILTER             |      |       |       |            |          |

| 2 | TABLE ACCESS FULL | T | 1 | 31 | 2 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 31 | | |
|* 4 | TABLE ACCESS FULL| T | 1 | 31 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------

Predicate Information (identified by operation id):


   1 - filter(INTERNAL_FUNCTION("X")||INTERNAL_FUNCTION("Y")= (SELECT

              /*+ UNNEST */
MAX(INTERNAL_FUNCTION("X")||INTERNAL_FUNCTION("Y")) FROM               "T" "T" WHERE "ID"=:B1))
   4 - filter("ID"=:B1)

Hint Report (identified by operation id / Query Block Name / Object Alias): Total hints for statement: 1 (U - Unused (1))


   3 - SEL$2
         U - unnest / Failed basic validity checks

*10053: SU: SU bypassed: More than 1 column in connect condition.*

*Next query: unnested in 19.4, not unnested in 19.9:*

explain plan for
select *
  from t driver
 where *nvl(x,y)* in (select --+ unnest

                       max(nvl(x,y))
                  from t
                 where id = driver.id);

select * from dbms_xplan.display();

*Single column: unnested in both 19.4/19.9*

explain plan for
select *
  from t driver
 where *(x)* in (select --+ unnest

                       max(x)
                  from t
                 where id = driver.id);

select * from dbms_xplan.display();

*Multi-column IN:*

explain plan for
select *
  from t driver
 where *(x,y)* in (select --+ unnest

                       max(x), max(y)
                  from t
                 where id = driver.id);

select * from dbms_xplan.display();

Has the same plan in both - there is no SU:



| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |


| 0 | SELECT STATEMENT | | 1 | 31 | 6 (0)| 00:00:01 |
|*  1 |  FILTER             |      |       |       |            |          |

| 2 | TABLE ACCESS FULL | T | 1 | 31 | 2 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 31 | | |
|* 4 | TABLE ACCESS FULL| T | 1 | 31 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------

Predicate Information (identified by operation id):


   1 - filter(("X","Y")= (SELECT /*+ UNNEST */ MAX("X"),MAX("Y") FROM

              "T" "T" WHERE "ID"=:B1))
   4 - filter("ID"=:B1)

Hint Report (identified by operation id / Query Block Name / Object Alias): Total hints for statement: 1 (U - Unused (1))


   3 - SEL$2
         U - unnest / Failed basic validity checks

*To conclude,* when more than one column is present in the IN condition, then SU is blocked with the following reason in 19.9: *10053: SU: SU bypassed: More than 1 column in connect condition.* 19.4 was less restricted. Both versions block unnesting with multi-column IN.

Best regards,
Mikhail Velikikh

On Wed, 17 Feb 2021 at 17:45, Sayan Malakshinov <xt.and.r_at_gmail.com> wrote:

> Hi Charles,
>
> OK, Mikhail Velikikh has found the new limitation blocking subquery
> unnesting, he will write in details soon, but I just want to highlight
> strange storing time in date column and so complex type conversions... I'd
> try to avoid such problems in future
>
> Best regards,
> Sayan Malakshinov
> Oracle performance tuning expert
> Oracle Database Developer Choice Award winner
> Oracle ACE Associate
> http://orasql.org
>
> ср, 17 февр. 2021 г., 20:41 Charles Schultz <sacrophyte_at_gmail.com>:
>
>> Sayan, to answer your question, both are DATE. Yes, I agree it is better
>> to rewrite the query; however, that is not the point of this email thread.
>> :)
>>
>> SQL > desc notes
>>  Name
>>  Null?    Type
>>  -----------------------------------------------------------------------
>> -------- ------------------------------------------------
>>  NOTEID
>>  NOT NULL VARCHAR2(14 CHAR)
>>  DATENOTE
>>         DATE
>>  TIMENOTE
>>         DATE
>>  NOTETOPIC
>>          VARCHAR2(80 CHAR)
>>  NOTETEXT
>>         CLOB
>>  USERID
>>         VARCHAR2(14 CHAR)
>>  ENTITYTYPEID
>>         NUMBER(38)
>>  ENTITYID
>>         VARCHAR2(14 CHAR)
>>  ENTITYSUBTYPEID
>>          NUMBER(38)
>>  ENTITYSUBID
>>          VARCHAR2(14 CHAR)
>>  NOTECATEGORY
>>         VARCHAR2(80 CHAR)
>>  CNVID
>>          VARCHAR2(20 CHAR)
>>  CNVID2
>>         VARCHAR2(20 CHAR)
>>  LASTMODDATE
>>          DATE
>>  LASTMODTIME
>>          DATE
>>  LASTMODUSERID
>>          VARCHAR2(14 CHAR)
>>  ISPRIORITYITEM
>>         NUMBER(38)
>>  EMAILMID
>>         VARCHAR2(14 CHAR)
>>  ISSUMMARYITEM
>>          NUMBER(38)
>>  SITEID
>>         VARCHAR2(4 CHAR)
>>  LFSWDDID
>>         VARCHAR2(14 CHAR)
>>
>> On Wed, Feb 17, 2021 at 11:30 AM Sayan Malakshinov <xt.and.r_at_gmail.com>
>> wrote:
>>
>>> Hello,
>>>
>>> What are datatypes of the columns LASTMODDATE and LASTMODTIME?
>>> To be honest it's much better to rewrite this query using analytic
>>> function dense_rank() over(...) = 1, ie without the need to scan table
>>> twice
>>>
>>>
>>> --
>>> Best regards,
>>> Sayan Malakshinov
>>> Oracle performance tuning expert
>>> Oracle Database Developer Choice Award winner
>>> Oracle ACE Associate
>>> http://orasql.org
>>>
>>> ср, 17 февр. 2021 г., 19:30 Jonathan Lewis <jlewisoracle_at_gmail.com>:
>>>
>>>>
>>>> Various possibilties.
>>>> a) Oracle Corp. has realised that there's a boundary condition with
>>>> this pattern that could produce wrong results and blocks the transformation
>>>> (e.g. are either of lastmoddate and lastmodtime declared not null - if not
>>>> the unnest ought to be invalid)
>>>>
>>>> b) Oracle Corp. has modified the optimizer code to produced more
>>>> efficient plans in almost all cases, but the change introduces certain
>>>> restrictiions that your SQL now meets (e.g. even if both lastmoddate and
>>>> lastmodetime the code may now assume that to_char() or to_date() could
>>>> produce a null from a non-null.
>>>>
>>>> c) Some relatively simple code change has introduced a bug
>>>>
>>>> Since the report suggests the blocking of the UNNEST is for a failed
>>>> validity test I'd suspect it may be deliberate.
>>>>
>>>> One quick and dirty test I'd try is to add to the subquery the predicate
>>>> where to_date(to_char(A.LASTMODDATE,'yyyymmdd') || to_char
>>>> (A.LASTMODTIME,'hh24miss'),'yyyymmddhh24miss')is not null
>>>>
>>>> and see if that allows the unnest.
>>>>
>>>> The other Q&D I would do is produce the 10053 trace file and look for
>>>> the final "Unparsed" query to see what transformation (if any) Oracle had
>>>> applied in case that gave you a clue.  (I'd resist looking at more detail
>>>> in the 10053 for as long as possible).
>>>>
>>>> Regards
>>>> Jonathan Lewis
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>
>> --
>> Charles Schultz
>>
>

--
http://www.freelists.org/webpage/oracle-l
Received on Wed Feb 17 2021 - 18:56:21 CET

Original text of this message