You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Optimizer chooses loose index scan (QUICK_GROUP_MIN_MAX_SELECT)
for a group by even when there exists a predicate with disjunction.
This is due to that either of the below two cases are encountered
and not handled properly.
1. A range tree created for index merge scan is not taken
into account while checking for the presence of disjuncted
conditions in get_best_group_min_max().
2. A disjunction condition could lead to null tree if an index
merge scan is not possible. A NULL tree will cause
get_best_group_min_max() to skip many relevent checks
like WA2.
There are two scenarios here,
(a) The WHERE clause is a disjunction of conditions on
MIN/MAX column only.
=> We can use min_max optimization considering all
other criteria (eg:SA2) are met.
(b) MIN/MAX column is participating in a disjunctive WHERE
clause along with other columns.
=> min_max optimization is not applicable in this scenario.
Fix:
Skip loose index scan in below scenarios involving disjoint conditions
in WHERE clause.
1. When the condition in WHERE clause results in more than
one disjoint range trees (when index merge scan is possible).
2. The range tree is null and MIN/MAX column participates
in the WHERE clause along with other columns.
[There is no range tree if WHERE condition can't be represented
in a single range tree and index merge is not possible]
Change-Id: I2ec8ef815d9dfa64e0bf60e97d3b6129c070e24b
Copy file name to clipboardExpand all lines: mysql-test/r/group_min_max.result
+277Lines changed: 277 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -3794,3 +3794,280 @@ Handler_read_rnd 0
3794
3794
Handler_read_rnd_next 0
3795
3795
DROP TABLE t1;
3796
3796
# End of test for Bug#18109609
3797
+
#
3798
+
# Bug#24423143 - WRONG RESULTS FOR AGGREGATE QUERY
3799
+
#
3800
+
# Test index merge tree scenario
3801
+
CREATE TABLE A (
3802
+
aggr_col int,
3803
+
group_by_col int,
3804
+
KEY aggr_col_key (aggr_col),
3805
+
KEY group_by_col_key (group_by_col, aggr_col)
3806
+
) ENGINE=InnoDB;
3807
+
set optimizer_trace_max_mem_size=1048576;
3808
+
set @@session.optimizer_trace='enabled=on';
3809
+
set end_markers_in_json=on;
3810
+
INSERT INTO A VALUES (2,3),(5,6),(6,3),(7,NULL),(9,NULL),(10,6);
3811
+
ANALYZE TABLE A;
3812
+
Table Op Msg_type Msg_text
3813
+
test.A analyze status OK
3814
+
SELECT group_by_col, MIN(aggr_col) FROM A
3815
+
WHERE (group_by_col IN (70, 9)) OR (aggr_col > 2) GROUP BY group_by_col;
3816
+
group_by_col MIN(aggr_col)
3817
+
NULL 7
3818
+
3 6
3819
+
6 5
3820
+
EXPLAIN SELECT group_by_col, MIN(aggr_col) FROM A
3821
+
WHERE (group_by_col IN (70 ,9)) OR (aggr_col > 2) GROUP BY group_by_col;
3822
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3823
+
1 SIMPLE A NULL index aggr_col_key,group_by_col_key group_by_col_key 10 NULL 6 55.55 Using where; Using index
3824
+
Warnings:
3825
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` in (70,9)) or (`test`.`A`.`aggr_col` > 2)) group by `test`.`A`.`group_by_col`
3826
+
SELECT TRACE RLIKE 'disjuntive_predicate_present' AS OK
3827
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3828
+
OK
3829
+
1
3830
+
SELECT group_by_col, MAX(aggr_col) FROM A
3831
+
WHERE (group_by_col IN (70, 9)) OR (aggr_col < 9) GROUP BY group_by_col;
3832
+
group_by_col MAX(aggr_col)
3833
+
NULL 7
3834
+
3 6
3835
+
6 5
3836
+
EXPLAIN SELECT group_by_col, MAX(aggr_col) FROM A
3837
+
WHERE (group_by_col IN (70 , 9)) OR (aggr_col < 9) GROUP BY group_by_col;
3838
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3839
+
1 SIMPLE A NULL index aggr_col_key,group_by_col_key group_by_col_key 10 NULL 6 55.55 Using where; Using index
3840
+
Warnings:
3841
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` in (70,9)) or (`test`.`A`.`aggr_col` < 9)) group by `test`.`A`.`group_by_col`
3842
+
SELECT TRACE RLIKE 'disjuntive_predicate_present' AS OK
3843
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3844
+
OK
3845
+
1
3846
+
# Test IMPOSSIBLE TREE scenario
3847
+
ALTER TABLE A DROP KEY aggr_col_key;
3848
+
SELECT group_by_col, MIN(aggr_col) FROM A
3849
+
WHERE (group_by_col IN (70 ,9)) OR (aggr_col > 2) GROUP BY group_by_col;
3850
+
group_by_col MIN(aggr_col)
3851
+
NULL 7
3852
+
3 6
3853
+
6 5
3854
+
EXPLAIN SELECT group_by_col, MIN(aggr_col) FROM A
3855
+
WHERE (group_by_col IN (70, 9)) OR (aggr_col > 2) GROUP BY group_by_col;
3856
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3857
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 6 55.55 Using where; Using index
3858
+
Warnings:
3859
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` in (70,9)) or (`test`.`A`.`aggr_col` > 2)) group by `test`.`A`.`group_by_col`
3860
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3861
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3862
+
OK
3863
+
1
3864
+
SELECT group_by_col, MAX(aggr_col) FROM A
3865
+
WHERE (group_by_col IN (70, 9)) OR (aggr_col < 9) GROUP BY group_by_col;
3866
+
group_by_col MAX(aggr_col)
3867
+
NULL 7
3868
+
3 6
3869
+
6 5
3870
+
EXPLAIN SELECT group_by_col, MAX(aggr_col) FROM A
3871
+
WHERE (group_by_col IN (70, 9)) OR (aggr_col < 9) GROUP BY group_by_col;
3872
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3873
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 6 55.55 Using where; Using index
3874
+
Warnings:
3875
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` in (70,9)) or (`test`.`A`.`aggr_col` < 9)) group by `test`.`A`.`group_by_col`
3876
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3877
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3878
+
OK
3879
+
1
3880
+
# Scenario 3: aggregate field used as equal expression.
3881
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3882
+
WHERE (group_by_col IN (3, 9)) OR (aggr_col = 9) GROUP BY group_by_col;
3883
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3884
+
NULL 9 9
3885
+
3 2 6
3886
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3887
+
WHERE (group_by_col IN (3, 9)) OR (aggr_col = 9) GROUP BY group_by_col;
3888
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3889
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 6 44.44 Using where; Using index
3890
+
Warnings:
3891
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` in (3,9)) or (`test`.`A`.`aggr_col` = 9)) group by `test`.`A`.`group_by_col`
3892
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3893
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3894
+
OK
3895
+
1
3896
+
# Scenario 4: non aggregate field used as equal expression.
3897
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3898
+
WHERE (group_by_col = 3) OR (aggr_col > 8) GROUP BY group_by_col;
3899
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3900
+
NULL 9 9
3901
+
3 2 6
3902
+
6 10 10
3903
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3904
+
WHERE (group_by_col = 3) OR (aggr_col > 8) GROUP BY group_by_col;
3905
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3906
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 6 55.55 Using where; Using index
3907
+
Warnings:
3908
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` = 3) or (`test`.`A`.`aggr_col` > 8)) group by `test`.`A`.`group_by_col`
3909
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3910
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3911
+
OK
3912
+
1
3913
+
# Scenario 5: aggregate field used as non-zero expression.
3914
+
INSERT INTO A VALUES(0, 3);
3915
+
INSERT INTO A VALUES(0, 9);
3916
+
INSERT INTO A VALUES(8, 0);
3917
+
ANALYZE TABLE A;
3918
+
Table Op Msg_type Msg_text
3919
+
test.A analyze status OK
3920
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3921
+
WHERE (group_by_col = 9) OR aggr_col GROUP BY group_by_col;
3922
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3923
+
NULL 7 9
3924
+
0 8 8
3925
+
3 2 6
3926
+
6 5 10
3927
+
9 0 0
3928
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3929
+
WHERE group_by_col = 9 OR aggr_col GROUP BY group_by_col;
3930
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3931
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 9 91.11 Using where; Using index
3932
+
Warnings:
3933
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`group_by_col` = 9) or `test`.`A`.`aggr_col`) group by `test`.`A`.`group_by_col`
3934
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3935
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3936
+
OK
3937
+
1
3938
+
# Scenario 6: non aggregate field used as non-zero expression.
3939
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3940
+
WHERE group_by_col OR (aggr_col < 9) GROUP BY group_by_col;
3941
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3942
+
NULL 7 7
3943
+
0 8 8
3944
+
3 0 6
3945
+
6 5 10
3946
+
9 0 0
3947
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3948
+
WHERE group_by_col OR (aggr_col < 9) GROUP BY group_by_col;
3949
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3950
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 9 92.59 Using where; Using index
3951
+
Warnings:
3952
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`group_by_col` or (`test`.`A`.`aggr_col` < 9)) group by `test`.`A`.`group_by_col`
3953
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3954
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3955
+
OK
3956
+
1
3957
+
# Scenario 7: aggregate field used in equal exp without a CONST
3958
+
INSERT INTO A VALUES(1,1),(1,2),(2,1);
3959
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3960
+
WHERE aggr_col = group_by_col GROUP BY group_by_col;
3961
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3962
+
1 1 1
3963
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3964
+
WHERE aggr_col = group_by_col GROUP BY group_by_col;
3965
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3966
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 12 100.00 Using where; Using index
3967
+
Warnings:
3968
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`group_by_col` = `test`.`A`.`aggr_col`) group by `test`.`A`.`group_by_col`
3969
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3970
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3971
+
OK
3972
+
1
3973
+
# Scenario 8: aggregate field used in a non-eq exp without a CONST
3974
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3975
+
WHERE aggr_col < group_by_col GROUP BY group_by_col;
3976
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3977
+
2 1 1
3978
+
3 0 2
3979
+
6 5 5
3980
+
9 0 0
3981
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3982
+
WHERE aggr_col < group_by_col GROUP BY group_by_col;
3983
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
3984
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 12 33.33 Using where; Using index
3985
+
Warnings:
3986
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`aggr_col` < `test`.`A`.`group_by_col`) group by `test`.`A`.`group_by_col`
3987
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
3988
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
3989
+
OK
3990
+
1
3991
+
# Scenario 8
3992
+
INSERT INTO A VALUES(0,1),(1,0),(0,0);
3993
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
3994
+
WHERE aggr_col OR group_by_col GROUP BY group_by_col;
3995
+
group_by_col MIN(aggr_col) MAX(aggr_col)
3996
+
NULL 7 9
3997
+
0 1 8
3998
+
1 0 2
3999
+
2 1 1
4000
+
3 0 6
4001
+
6 5 10
4002
+
9 0 0
4003
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4004
+
WHERE aggr_col OR group_by_col GROUP BY group_by_col;
4005
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
4006
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 15 99.00 Using where; Using index
4007
+
Warnings:
4008
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`aggr_col` or `test`.`A`.`group_by_col`) group by `test`.`A`.`group_by_col`
4009
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
4010
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
4011
+
OK
4012
+
1
4013
+
# Scenario 9
4014
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4015
+
WHERE aggr_col AND group_by_col GROUP BY group_by_col;
4016
+
group_by_col MIN(aggr_col) MAX(aggr_col)
4017
+
1 1 2
4018
+
2 1 1
4019
+
3 2 6
4020
+
6 5 10
4021
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4022
+
WHERE aggr_col AND group_by_col GROUP BY group_by_col;
4023
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
4024
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 15 81.00 Using where; Using index
4025
+
Warnings:
4026
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`aggr_col` and `test`.`A`.`group_by_col`) group by `test`.`A`.`group_by_col`
4027
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
4028
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
4029
+
OK
4030
+
1
4031
+
# Scenario 10: Added for completion. This fix does not have an impact.
4032
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4033
+
WHERE aggr_col<>0 AND group_by_col<>0 GROUP BY group_by_col;
4034
+
group_by_col MIN(aggr_col) MAX(aggr_col)
4035
+
1 1 2
4036
+
2 1 1
4037
+
3 2 6
4038
+
6 5 10
4039
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4040
+
WHERE aggr_col<>0 AND group_by_col<>0 GROUP BY group_by_col;
4041
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
4042
+
1 SIMPLE A NULL range group_by_col_key group_by_col_key 10 NULL 7 100.00 Using where; Using index for group-by
4043
+
Warnings:
4044
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where ((`test`.`A`.`aggr_col` <> 0) and (`test`.`A`.`group_by_col` <> 0)) group by `test`.`A`.`group_by_col`
4045
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
4046
+
FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
4047
+
OK
4048
+
0
4049
+
# Scenario 11: ITEM_FUNC as an argument of ITEM_FUNC
4050
+
SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4051
+
WHERE group_by_col OR (group_by_col < (aggr_col = 1)) GROUP BY group_by_col;
4052
+
group_by_col MIN(aggr_col) MAX(aggr_col)
4053
+
0 1 1
4054
+
1 0 2
4055
+
2 1 1
4056
+
3 0 6
4057
+
6 5 10
4058
+
9 0 0
4059
+
EXPLAIN SELECT group_by_col, MIN(aggr_col), MAX(aggr_col) FROM A
4060
+
WHERE group_by_col OR (group_by_col < (aggr_col = 1)) GROUP BY group_by_col;
4061
+
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
4062
+
1 SIMPLE A NULL index group_by_col_key group_by_col_key 10 NULL 15 93.33 Using where; Using index
4063
+
Warnings:
4064
+
Note 1003 /* select#1 */ select `test`.`A`.`group_by_col` AS `group_by_col`,min(`test`.`A`.`aggr_col`) AS `MIN(aggr_col)`,max(`test`.`A`.`aggr_col`) AS `MAX(aggr_col)` from `test`.`A` where (`test`.`A`.`group_by_col` or (`test`.`A`.`group_by_col` < (`test`.`A`.`aggr_col` = 1))) group by `test`.`A`.`group_by_col`
4065
+
SELECT TRACE RLIKE 'minmax_keypart_in_disjunctive_query' AS OK
0 commit comments