Bug #117092 The same statement may produce different results with different execution plans.
Submitted: 2 Jan 3:34 Modified: 3 Jan 1:28
Reporter: yi liu Email Updates:
Status: Can't repeat Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S3 (Non-critical)
Version:8.0.18 OS:Any
Assigned to: CPU Architecture:Any

[2 Jan 3:34] yi liu
Description:
The same statement may produce different results with different execution plans.

How to repeat:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 16891
Server version: 8.0.18 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.18    |
+-----------+
1 row in set (0.00 sec)

mysql> create table test4(col1 varchar(40), col2 int);
Query OK, 0 rows affected (0.02 sec)

mysql> insert into test4
    -> values
    ->  ('3134'        , 88663422)
    -> ,('3135'        , 88663423)
    -> ,('3137'        , 88663425)
    -> ,('38866001'    , 88663425)
    -> ,('388660010001', 162023)
    -> ,('388660010002', 88664116)
    -> ,('38866002'    , 88663697)
    -> ,('388660020001', 88663697)
    -> ,('388660020002', 88664117)
    -> ,('38866003'    , 88663724)
    -> ,('388660030001', 88663724)
    -> ,('388660030002', 88664247)
    -> ,('38866004'    , 88663763)
    -> ,('388660040001', 88663763)
    -> ,('388660040002', 88664238)
    -> ,('38866005'    , 88663972)
    -> ,('388660050001', 88663972)
    -> ,('388660050002', 88664163)
    -> ,('388660050003', 88664222)
    -> ,('38866006'    , 88664031)
    -> ,('388660060001', 88664031)
    -> ,('388660060002', 88664183)
    -> ,('3138'        , 88663426)
    -> ,('3140'        , 88663428)
    -> ,('3145'        , 88663433)
    -> ,('3147'        , 88663435)
    -> ,('3148'        , 88663436)
    -> ,('3149'        , 88663437)
    -> ,('3150'        , 88663438)
    -> ,('3151'        , 88663439)
    -> ,('3154'        , 88663442)
    -> ,('3155'        , 88663443)
    -> ,('3158'        , 88663446)
    -> ,('3160'        , 88663448)
    -> ,('3165'        , 88663453)
    -> ,('3168'        , 88663456)
    -> ,('3170'        , 88663458)
    -> ,('3172'        , 88663460)
    -> ,('3183'        , 88663471)
    -> ,('3185'        , 88663473)
    -> ,('3187'        , 88663475)
    -> ,('3189'        , 88663477)
    -> ,('3193'        , 88663481)
    -> ,('3194'        , 88663482)
    -> ,('3198'        , 88663486)
    -> ,('3199'        , 88663487)
    -> ,('3202'        , 88663490)
    -> ,('3204'        , 88663492)
    -> ,('3207'        , 88663495)
    -> ,('3213'        , 88663501)
    -> ,('3216'        , 88663504)
    -> ,('3218'        , 88663506)
    -> ,('3220'        , 88663508)
    -> ,('3224'        , 88663512)
    -> ,('3227'        , 88663515)
    -> ,('3236'        , 88663524)
    -> ,('3238'        , 88663526)
    -> ,('3243'        , 88663531)
    -> ,('3245'        , 88663533)
    -> ,('3246'        , 88663534)
    -> ,('3247'        , 88663535)
    -> ,('3248'        , 88663536)
    -> ,('3249'        , 88663537)
    -> ,('3256'        , 88663544)
    -> ,('3257'        , 88663545)
    -> ,('3259'        , 88663547)
    -> ,('3263'        , 88663551)
    -> ,('3265'        , 88663553)
    -> ,('3267'        , 88663555)
    -> ,('3271'        , 88663559)
    -> ,('3275'        , 88663563)
    -> ,('3280'        , 88663568)
    -> ,('3284'        , 88664531)
    -> ,('3285'        , 88664532)
    -> ,('3286'        , 88664533)
    -> ,('3292'        , 88664849);
Query OK, 76 rows affected (0.01 sec)
Records: 76  Duplicates: 0  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> with temp_itemcode as (
    ->    select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
    -> ), temp_data as (
    ->    select b.col1 as rcol1, a.col2, a.col1
    ->      from test4 a
    ->      join temp_itemcode b
    ->        on a.col1 like concat(b.col1,'%')
    -> ), temp_data2 as (
    ->    select d.col1
    ->      from temp_data c
    ->      join temp_itemcode d
    ->        on c.col1 = d.col1 and c.rcol1=d.col1
    -> )
    -> select e.*, f.*
    ->   from temp_data2 e
    ->   join temp_data f
    ->     on f.rcol1 = e.col1
    ->  where not exists (select null
    ->         from temp_data g
    ->         where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);
+----------+----------+----------+--------------+
| col1     | rcol1    | col2     | col1         |
+----------+----------+----------+--------------+
| 38866001 | 38866001 | 88663425 | 38866001     |
| 38866001 | 38866001 |   162023 | 388660010001 |
| 38866001 | 38866001 | 88664116 | 388660010002 |
+----------+----------+----------+--------------+
3 rows in set (0.01 sec)

mysql> explain format=tree
    -> with temp_itemcode as (
    ->    select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
    -> ), temp_data as (
    ->    select b.col1 as rcol1, a.col2, a.col1
    ->      from test4 a
    ->      join temp_itemcode b
    ->        on a.col1 like concat(b.col1,'%')
    -> ), temp_data2 as (
    ->    select d.col1
    ->      from temp_data c
    ->      join temp_itemcode d
    ->        on c.col1 = d.col1 and c.rcol1=d.col1
    -> )
    -> select e.*, f.*
    ->   from temp_data2 e
    ->   join temp_data f
    ->     on f.rcol1 = e.col1
    ->  where not exists (select null
    ->         from temp_data g
    ->         where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1)\G
*************************** 1. row ***************************
EXPLAIN: -> Nested loop anti-join
    -> Filter: (a.col1 like concat(t.col1,'%'))
        -> Inner hash join
            -> Table scan on a  (cost=0.14 rows=76)
            -> Hash
                -> Nested loop inner join
                    -> Nested loop inner join
                        -> Filter: (a.col1 like concat(t.col1,'%'))
                            -> Inner hash join (a.col1 = t.col1)
                                -> Table scan on a  (cost=0.18 rows=76)
                                -> Hash
                                    -> Filter: (t.col1 is not null)
                                        -> Materialize table function
                        -> Materialize table function
                    -> Materialize table function
    -> Nested loop inner join
        -> Materialize table function
        -> Filter: ((a.col2 = a.col2) and (a.col1 like concat(a.col1,'%')) and (a.col1 <> a.col1) and (a.col1 like concat(t.col1,'%')))  (cost=46.55 rows=76)
            -> Table scan on a  (cost=46.55 rows=76)

1 row in set, 4 warnings (0.00 sec)

mysql> show warnings;
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message                                                             |
+-------+------+---------------------------------------------------------------------+
| Note  | 1276 | Field or reference 'f.rcol1' of SELECT #8 was resolved in SELECT #1 |
| Note  | 1276 | Field or reference 'f.col2' of SELECT #8 was resolved in SELECT #1  |
| Note  | 1276 | Field or reference 'f.col1' of SELECT #8 was resolved in SELECT #1  |
| Note  | 1276 | Field or reference 'f.col1' of SELECT #8 was resolved in SELECT #1  |
+-------+------+---------------------------------------------------------------------+
4 rows in set (0.00 sec)

mysql> create index ind_test4_01 on test4(col1);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index ind_test4_02 on test4(col2);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> with temp_itemcode as (
    ->    select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
    -> ), temp_data as (
    ->    select b.col1 as rcol1, a.col2, a.col1
    ->      from test4 a
    ->      join temp_itemcode b
    ->        on a.col1 like concat(b.col1,'%')
    -> ), temp_data2 as (
    ->    select d.col1
    ->      from temp_data c
    ->      join temp_itemcode d
    ->        on c.col1 = d.col1 and c.rcol1=d.col1
    -> )
    -> select e.*, f.*
    ->   from temp_data2 e
    ->   join temp_data f
    ->     on f.rcol1 = e.col1
    ->  where not exists (select null
    ->         from temp_data g
    ->         where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);
+----------+----------+----------+----------+
| col1     | rcol1    | col2     | col1     |
+----------+----------+----------+----------+
| 38866001 | 38866001 | 88663425 | 38866001 |
+----------+----------+----------+----------+
1 row in set (0.00 sec)

mysql> explain format=tree
    -> with temp_itemcode as (
    ->    select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
    -> ), temp_data as (
    ->    select b.col1 as rcol1, a.col2, a.col1
    ->      from test4 a
    ->      join temp_itemcode b
    ->        on a.col1 like concat(b.col1,'%')
    -> ), temp_data2 as (
    ->    select d.col1
    ->      from temp_data c
    ->      join temp_itemcode d
    ->        on c.col1 = d.col1 and c.rcol1=d.col1
    -> )
    -> select e.*, f.*
    ->   from temp_data2 e
    ->   join temp_data f
    ->     on f.rcol1 = e.col1
    ->  where not exists (select null
    ->         from temp_data g
    ->         where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1)\G
*************************** 1. row ***************************
EXPLAIN: -> Remove duplicate (t, a, a, t, t) rows using temporary table (weedout)
    -> Nested loop anti-join
        -> Inner hash join (t.col1 = t.col1)
            -> Materialize table function
            -> Hash
                -> Filter: (a.col1 like concat(t.col1,'%'))
                    -> Inner hash join (t.col1 = t.col1)
                        -> Materialize table function
                        -> Hash
                            -> Filter: (a.col1 like concat(t.col1,'%'))
                                -> Inner hash join
                                    -> Table scan on a  (cost=0.55 rows=76)
                                    -> Hash
                                        -> Nested loop inner join
                                            -> Filter: (t.col1 is not null)
                                                -> Materialize table function
                                            -> Index lookup on a using ind_test4_01 (col1=t.col1)  (cost=0.30 rows=1)
        -> Nested loop inner join
            -> Filter: ((a.col1 like concat(a.col1,'%')) and (a.col1 <> a.col1))  (cost=0.28 rows=1)
                -> Index lookup on a using ind_test4_02 (col2=a.col2)  (cost=0.28 rows=1)
            -> Filter: ((t.col1 = t.col1) and (a.col1 like concat(t.col1,'%')))
                -> Materialize table function

1 row in set, 4 warnings (0.00 sec)

mysql> show warnings;
+-------+------+---------------------------------------------------------------------+
| Level | Code | Message                                                             |
+-------+------+---------------------------------------------------------------------+
| Note  | 1276 | Field or reference 'f.rcol1' of SELECT #8 was resolved in SELECT #1 |
| Note  | 1276 | Field or reference 'f.col2' of SELECT #8 was resolved in SELECT #1  |
| Note  | 1276 | Field or reference 'f.col1' of SELECT #8 was resolved in SELECT #1  |
| Note  | 1276 | Field or reference 'f.col1' of SELECT #8 was resolved in SELECT #1  |
+-------+------+---------------------------------------------------------------------+
4 rows in set (0.00 sec)

Suggested fix:
#WA1: no_semijoin()
with temp_itemcode as (
   select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
), temp_data as (
   select b.col1 as rcol1, a.col2, a.col1
     from test4 a
     join temp_itemcode b
       on a.col1 like concat(b.col1,'%')
), temp_data2 as (
   select d.col1
     from temp_data c
     join temp_itemcode d
       on c.col1 = d.col1 and c.rcol1=d.col1
)
select e.*, f.*
  from temp_data2 e
  join temp_data f
    on f.rcol1 = e.col1
 where not exists (select/*+ no_semijoin()*/ null
        from temp_data g
        where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);

col1    |rcol1   |col2    |col1
--------+--------+--------+------------
38866001|38866001|88663425|38866001
38866001|38866001|  162023|388660010001
38866001|38866001|88664116|388660010002

-> Inner hash join (t.col1 = t.col1)
    -> Materialize table function
    -> Hash
        -> Filter: (a.col1 like concat(t.col1,'%'))
            -> Inner hash join (t.col1 = t.col1)
                -> Materialize table function
                -> Hash
                    -> Filter: ((a.col1 like concat(t.col1,'%')) and exists(select #8) is false)
                        -> Inner hash join
                            -> Table scan on a  (cost=0.55 rows=76)
                            -> Hash
                                -> Nested loop inner join
                                    -> Filter: (t.col1 is not null)
                                        -> Materialize table function
                                    -> Index lookup on a using ind_test4_01 (col1=t.col1)  (cost=0.30 rows=1)
                        -> Select #8 (subquery in condition; dependent)
                            -> Limit: 1 row(s)
                                -> Nested loop inner join
                                    -> Filter: ((a.col1 like concat(a.col1,'%')) and (a.col1 <> a.col1))  (cost=0.28 rows=0)
                                        -> Index lookup on a using ind_test4_02 (col2=a.col2), with index condition: (a.col2 = a.col2)  (cost=0.28 rows=1)
                                    -> Filter: ((t.col1 = t.col1) and (a.col1 like concat(t.col1,'%')))
                                        -> Materialize table function

#WA2: no_merge()
with temp_itemcode as (
   select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
), temp_data as (
   select b.col1 as rcol1, a.col2, a.col1
     from test4 a
     join temp_itemcode b
       on a.col1 like concat(b.col1,'%')
), temp_data2 as (
   select d.col1
     from temp_data c
     join temp_itemcode d
       on c.col1 = d.col1 and c.rcol1=d.col1
)
select e.*, f.*
  from temp_data2 e
  join temp_data f
    on f.rcol1 = e.col1
 where not exists (select/*+ no_merge() */ null
        from temp_data g
        where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);

col1    |rcol1   |col2    |col1
--------+--------+--------+------------
38866001|38866001|88663425|38866001
38866001|38866001|  162023|388660010001
38866001|38866001|88664116|388660010002

-> Nested loop anti-join
    -> Inner hash join (t.col1 = t.col1)
        -> Materialize table function
        -> Hash
            -> Filter: (a.col1 like concat(t.col1,'%'))
                -> Inner hash join (t.col1 = t.col1)
                    -> Materialize table function
                    -> Hash
                        -> Filter: (a.col1 like concat(t.col1,'%'))
                            -> Inner hash join
                                -> Table scan on a  (cost=0.55 rows=76)
                                -> Hash
                                    -> Nested loop inner join
                                        -> Filter: (t.col1 is not null)
                                            -> Materialize table function
                                        -> Index lookup on a using ind_test4_01 (col1=t.col1)  (cost=0.30 rows=1)
    -> Filter: ((g.col1 like concat(a.col1,'%')) and (a.col1 <> g.col1))
        -> Index lookup on g using <auto_key0> (rcol1=t.col1, col2=a.col2)
            -> Materialize CTE temp_data
                -> Filter: (a.col1 like concat(t.col1,'%'))
                    -> Inner hash join
                        -> Table scan on a  (cost=0.55 rows=76)
                        -> Hash
                            -> Materialize table function

WA3: DO NOT USE JSON_TABLE
with temp_itemcode as (
   -- select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
   select '38866001' as col1 from dual
), temp_data as (
   select b.col1 as rcol1, a.col2, a.col1
     from test4 a
     join temp_itemcode b
       on a.col1 like concat(b.col1,'%')
), temp_data2 as (
   select d.col1
     from temp_data c
     join temp_itemcode d
       on c.col1 = d.col1 and c.rcol1=d.col1
)
select e.*, f.*
  from temp_data2 e
  join temp_data f
    on f.rcol1 = e.col1
 where not exists (select null
        from temp_data g
        where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);

col1    |rcol1   |col2    |col1
--------+--------+--------+------------
38866001|38866001|88663425|38866001
38866001|38866001|  162023|388660010001
38866001|38866001|88664116|388660010002

-> Remove duplicate (b, d, b, a, a) rows using temporary table (weedout)
    -> Nested loop anti-join
        -> Inner hash join  (cost=1.44 rows=1)
            -> Filter: (a.col1 like <cache>(concat('38866001','%')))  (cost=1.09 rows=8)
                -> Table scan on a  (cost=1.09 rows=76)
            -> Hash
                -> Filter: ((a.col1 = '38866001') and (a.col1 like <cache>(concat('38866001','%'))))  (cost=0.35 rows=1)
                    -> Index lookup on a using ind_test4_01 (col1='38866001')  (cost=0.35 rows=1)
        -> Nested loop inner join
            -> Filter: (b.col1 = '38866001')
                -> Table scan on b
                    -> Materialize CTE temp_itemcode if needed (query plan printed elsewhere)
            -> Filter: ((a.col1 like concat(a.col1,'%')) and (a.col1 <> a.col1) and (a.col1 like concat(b.col1,'%')))  (cost=0.31 rows=1)
                -> Index lookup on a using ind_test4_02 (col2=a.col2)  (cost=0.31 rows=1)

#WA4: diable/invisible/remove related indexes
alter table test4 alter index ind_test4_01 invisible;
alter table test4 alter index ind_test4_02 invisible;

with temp_itemcode as (
   select col1 from json_table('["38866001"]' ,'$[*]' columns(col1 varchar(40) character set utf8mb4 path '$')) as t
), temp_data as (
   select b.col1 as rcol1, a.col2, a.col1
     from test4 a
     join temp_itemcode b
       on a.col1 like concat(b.col1,'%')
), temp_data2 as (
   select d.col1
     from temp_data c
     join temp_itemcode d
       on c.col1 = d.col1 and c.rcol1=d.col1
)
select e.*, f.*
  from temp_data2 e
  join temp_data f
    on f.rcol1 = e.col1
 where not exists (select null
        from temp_data g
        where f.rcol1=g.rcol1 and f.col2=g.col2 and g.col1 like concat(f.col1,'%') and f.col1 != g.col1);

#WA5: INNER JOIN -> LEFT JOIN
...
select e.*, f.*
  from temp_data2 e
  left join temp_data f
    on f.rcol1 = e.col1
...
[2 Jan 11:52] MySQL Verification Team
Hello yi liu,

Thank you for the report and feedback.
I quickly checked current GA 8.0.40 and confirmed that issue is no longer seen and issue fixed since 8.0.20+.  Could you please confirm? Thank you.

Joining the test results shortly.

regards,
Umesh
[2 Jan 11:53] MySQL Verification Team
8.0.18-8.0.21 and 8.0.40 test results

Attachment: 117092.results (application/octet-stream, text), 51.80 KiB.

[3 Jan 1:28] yi liu
yes, I have tested on 8.0.35, the sql produces a new plan, it looks like FIRSTMATCH strategy, not DUPSWEEDOUT in 8.0.18. But I'm wondering if generating an execution plan like in 8.0.18 on 8.0.35 would still cause the issue to occur?

-> Nested loop antijoin
    -> Inner hash join (t.col1 = t.col1)
        -> Materialize table function
        -> Hash
            -> Filter: (a.col1 like concat(t.col1,'%'))
                -> Inner hash join (t.col1 = t.col1)
                    -> Materialize table function
                    -> Hash
                        -> Filter: (a.col1 like concat(t.col1,'%'))
                            -> Inner hash join (no condition)
                                -> Table scan on a  (cost=0.547 rows=76)
                                -> Hash
                                    -> Nested loop inner join
                                        -> Filter: (t.col1 is not null)
                                            -> Materialize table function
                                        -> Covering index lookup on a using ind_test4_01 (col1=t.col1)  (cost=0.3 rows=1)
    -> Limit: 1 row(s)
        -> Nested loop inner join
            -> Filter: ((a.col1 like concat(a.col1,'%')) and (a.col1 <> a.col1))  (cost=0.278 rows=1.09)
                -> Index lookup on a using ind_test4_02 (col2=a.col2)  (cost=0.278 rows=1.09)
            -> Filter: ((t.col1 = t.col1) and (a.col1 like concat(t.col1,'%')))
                -> Materialize table function