Bug #99381 ORDER_BY Index-level optimizer hint implies NO_JOIN_INDEX for second table
Submitted: 28 Apr 2020 7:26 Modified: 29 Apr 2020 7:05
Reporter: Jesper Wisborg Krogh Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S2 (Serious)
Version:8.0.20 OS:Any
Assigned to: CPU Architecture:Any

[28 Apr 2020 7:26] Jesper Wisborg Krogh
Description:
If you try to use the new index-level optimizer hints, then in some cases the optimizer hint is not used correctly by the optimizer.

For example with a NO_INDEX() on the world.country table for the primary key and a ORDER_INDEX() on the world.city table for the CountryCode index, then the join is performed as a hash join. As comparison, the corresponding query using the old style index hints uses the CountryCode index for the join as well as the sorting.

The issue seems only to happen for joins. For single table queries, the ORDER_BY() index hint behaves as expected.

How to repeat:
Old style index hints:

EXPLAIN
SELECT ci.CountryCode, co.Name AS Country, ci.Name AS City, ci.District
  FROM world.country co IGNORE INDEX (Primary)
       INNER JOIN world.city ci FORCE INDEX FOR ORDER BY (CountryCode)
                  ON ci.CountryCode = co.Code
 WHERE co.Continent = 'Asia'
 ORDER BY ci.CountryCode, ci.ID\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: co
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 239
     filtered: 14.285715103149414
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: ci
   partitions: NULL
         type: ref
possible_keys: CountryCode
          key: CountryCode
      key_len: 3
          ref: world.co.Code
         rows: 18
     filtered: 100
        Extra: NULL
2 rows in set, 1 warning (0.0008 sec)

Note that as per the JSON formatted output, the query cost of this plan is 857.45 with my index statistics.

New index-level optimizer hint:

EXPLAIN
SELECT /*+ NO_INDEX(co PRIMARY) ORDER_INDEX(ci CountryCode) */
       ci.CountryCode, co.Name AS Country, ci.Name AS City, ci.District
  FROM world.country co
       INNER JOIN world.city ci
                  ON ci.CountryCode = co.Code
 WHERE co.Continent = 'Asia'
 ORDER BY ci.CountryCode, ci.ID\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: co
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 239
     filtered: 14.285715103149414
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: ci
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4188
     filtered: 0.43103447556495667
        Extra: Using where; Using join buffer (hash join)
2 rows in set, 1 warning (0.0007 sec)

This query has a cost of 14947.06.
[28 Apr 2020 7:26] Jesper Wisborg Krogh
Workaround, to get the expected query plan back, you need to add a JOIN_INDEX() hint as well:

EXPLAIN
SELECT /*+ ORDER_INDEX(ci CountryCode) JOIN_INDEX(ci CountryCode) */
       ci.CountryCode, co.Name AS Country, ci.Name AS City, ci.District
  FROM world.country co
       INNER JOIN world.city ci
                  ON ci.CountryCode = co.Code
 WHERE co.Continent = 'Asia'
 ORDER BY ci.CountryCode, ci.ID\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: co
   partitions: NULL
         type: ALL
possible_keys: PRIMARY
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 239
     filtered: 14.285715103149414
        Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: ci
   partitions: NULL
         type: ref
possible_keys: CountryCode
          key: CountryCode
      key_len: 3
          ref: world.co.Code
         rows: 18
     filtered: 100
        Extra: NULL
2 rows in set, 1 warning (0.0008 sec)
[29 Apr 2020 7:05] MySQL Verification Team
Dear Jesper,

Thank you for the report and feedback.

regards,
Umesh