Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
593b2fc2
Commit
593b2fc2
authored
Feb 27, 2003
by
unknown
Browse files
Options
Browse Files
Download
Plain Diff
Merge ibabaev@work.mysql.com:/home/bk/mysql-4.1
into hundin.mysql.fi:/home/igor/dev/mysql-4.1
parents
4d36263e
c015eb9c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1014 additions
and
293 deletions
+1014
-293
mysql-test/r/func_group.result
mysql-test/r/func_group.result
+250
-6
mysql-test/t/func_group.test
mysql-test/t/func_group.test
+149
-2
sql/opt_sum.cc
sql/opt_sum.cc
+615
-285
No files found.
mysql-test/r/func_group.result
View file @
593b2fc2
...
@@ -179,6 +179,17 @@ explain select max(t1.a1), max(t2.a2) from t1, t2;
...
@@ -179,6 +179,17 @@ explain select max(t1.a1), max(t2.a2) from t1, t2;
id select_type table type possible_keys key key_len ref rows Extra
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No matching min/max row
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No matching min/max row
insert into t2 values('AAA', 10, 0.5);
insert into t2 values('AAA', 10, 0.5);
insert into t2 values('BBB', 20, 1.0);
select t1.a1, t1.a2, t2.a1, t2.a2 from t1,t2;
a1 a2 a1 a2
10 aaa AAA 10
10 NULL AAA 10
10 bbb AAA 10
20 zzz AAA 10
10 aaa BBB 20
10 NULL BBB 20
10 bbb BBB 20
20 zzz BBB 20
select max(t1.a1), max(t2.a1) from t1, t2 where t2.a2=9;
select max(t1.a1), max(t2.a1) from t1, t2 where t2.a2=9;
max(t1.a1) max(t2.a1)
max(t1.a1) max(t2.a1)
NULL NULL
NULL NULL
...
@@ -188,19 +199,252 @@ NULL NULL
...
@@ -188,19 +199,252 @@ NULL NULL
select t1.a1, t1.a2, t2.a1, t2.a2 from t1 left outer join t2 on t1.a1=10;
select t1.a1, t1.a2, t2.a1, t2.a2 from t1 left outer join t2 on t1.a1=10;
a1 a2 a1 a2
a1 a2 a1 a2
10 aaa AAA 10
10 aaa AAA 10
10 aaa BBB 20
10 NULL AAA 10
10 NULL AAA 10
10 NULL BBB 20
10 bbb AAA 10
10 bbb AAA 10
10 bbb BBB 20
20 zzz NULL NULL
20 zzz NULL NULL
select max(t1.a2) from t1 left outer join t2 on t1.a1=10;
select max(t1.a2) from t1 left outer join t2 on t1.a1=10;
max(t1.a2)
max(t1.a2)
zzz
zzz
select max(t
1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1
=20;
select max(t
2.a1) from t2 left outer join t1 on t2.a2=10 where t2.a2
=20;
max(t
1.a2
)
max(t
2.a1
)
zzz
BBB
select max(t
1.a2) from t1 left outer join t2 on t1.a1=10 where t1.a1
=10;
select max(t
2.a1) from t2 left outer join t1 on t2.a2=10 where t2.a2
=10;
max(t
1.a2
)
max(t
2.a1
)
bbb
AAA
select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1='AAA';
select max(t2.a1) from t1 left outer join t2 on t1.a2=t2.a1 and 1=0 where t2.a1='AAA';
max(t2.a1)
max(t2.a1)
NULL
NULL
select max(t1.a2),max(t2.a1) from t1 left outer join t2 on t1.a1=10;
max(t1.a2) max(t2.a1)
zzz BBB
drop table t1,t2;
drop table t1,t2;
create table t1(
a1 char(3) primary key,
a2 smallint,
a3 char(3),
a4 real,
a5 date,
key k1(a2,a3),
key k2(a4 desc,a1),
key k3(a5,a1)
);
create table t2(
a1 char(3) primary key,
a2 char(17),
a3 char(2),
a4 char(3),
key k1(a3, a2),
key k2(a4)
);
insert into t1 values('AME',0,'SEA',0.100,date'1942-02-19');
insert into t1 values('HBR',1,'SEA',0.085,date'1948-03-05');
insert into t1 values('BOT',2,'SEA',0.085,date'1951-11-29');
insert into t1 values('BMC',3,'SEA',0.085,date'1958-09-08');
insert into t1 values('TWU',0,'LAX',0.080,date'1969-10-05');
insert into t1 values('BDL',0,'DEN',0.080,date'1960-11-27');
insert into t1 values('DTX',1,'NYC',0.080,date'1961-05-04');
insert into t1 values('PLS',1,'WDC',0.075,date'1949-01-02');
insert into t1 values('ZAJ',2,'CHI',0.075,date'1960-06-15');
insert into t1 values('VVV',2,'MIN',0.075,date'1959-06-28');
insert into t1 values('GTM',3,'DAL',0.070,date'1977-09-23');
insert into t1 values('SSJ',null,'CHI',null,date'1974-03-19');
insert into t1 values('KKK',3,'ATL',null,null);
insert into t1 values('XXX',null,'MIN',null,null);
insert into t2 values('TKF','Seattle','WA','AME');
insert into t2 values('LCC','Los Angeles','CA','TWU');
insert into t2 values('DEN','Denver','CO','BDL');
insert into t2 values('SDC','San Diego','CA','TWU');
insert into t2 values('NOL','New Orleans','LA','GTM');
insert into t2 values('LAK','Los Angeles','CA','TWU');
select * from t1;
a1 a2 a3 a4 a5
AME 0 SEA 0.1 1942-02-19
HBR 1 SEA 0.085 1948-03-05
BOT 2 SEA 0.085 1951-11-29
BMC 3 SEA 0.085 1958-09-08
TWU 0 LAX 0.08 1969-10-05
BDL 0 DEN 0.08 1960-11-27
DTX 1 NYC 0.08 1961-05-04
PLS 1 WDC 0.075 1949-01-02
ZAJ 2 CHI 0.075 1960-06-15
VVV 2 MIN 0.075 1959-06-28
GTM 3 DAL 0.07 1977-09-23
SSJ NULL CHI NULL 1974-03-19
KKK 3 ATL NULL NULL
XXX NULL MIN NULL NULL
select * from t2;
a1 a2 a3 a4
TKF Seattle WA AME
LCC Los Angeles CA TWU
DEN Denver CO BDL
SDC San Diego CA TWU
NOL New Orleans LA GTM
LAK Los Angeles CA TWU
explain
select min(a1) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a1) from t1;
min(a1)
AME
explain
select max(a4) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a4) from t1;
max(a4)
0.1
explain
select min(a5), max(a5) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a5), max(a5) from t1;
min(a5) max(a5)
1942-02-19 1977-09-23
explain
select min(a3) from t1 where a2 = 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a3) from t1 where a2 = 2;
min(a3)
CHI
explain
select min(a1), max(a1) from t1 where a4 = 0.080;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a1), max(a1) from t1 where a4 = 0.080;
min(a1) max(a1)
BDL TWU
explain
select min(t1.a5), max(t2.a3) from t1, t2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(t1.a5), max(t2.a3) from t1, t2;
min(t1.a5) max(t2.a3)
1942-02-19 WA
explain
select min(t1.a3), max(t2.a2) from t1, t2 where t1.a2 = 0 and t2.a3 = 'CA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(t1.a3), max(t2.a2) from t1, t2 where t1.a2 = 0 and t2.a3 = 'CA';
min(t1.a3) max(t2.a2)
DEN San Diego
explain
select min(a1) from t1 where a1 > 'KKK';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a1) from t1 where a1 > 'KKK';
min(a1)
PLS
explain
select min(a1) from t1 where a1 >= 'KKK';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a1) from t1 where a1 >= 'KKK';
min(a1)
KKK
explain
select max(a3) from t1 where a2 = 2 and a3 < 'SEA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a3) from t1 where a2 = 2 and a3 < 'SEA';
max(a3)
MIN
explain
select max(a5) from t1 where a5 < date'1970-01-01';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a5) from t1 where a5 < date'1970-01-01';
max(a5)
1969-10-05
explain
select max(a3) from t1 where a2 is null;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a3) from t1 where a2 is null;
max(a3)
MIN
explain
select max(a3) from t1 where a2 = 0 and a3 between 'K' and 'Q';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a3) from t1 where a2 = 0 and a3 between 'K' and 'Q';
max(a3)
LAX
explain
select min(a1), max(a1) from t1 where a1 between 'A' and 'P';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a1), max(a1) from t1 where a1 between 'A' and 'P';
min(a1) max(a1)
AME KKK
explain
select max(a3) from t1 where a3 < 'SEA' and a2 = 2 and a3 <= 'MIN';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a3) from t1 where a3 < 'SEA' and a2 = 2 and a3 <= 'MIN';
max(a3)
MIN
explain
select max(a3) from t1 where a3 = 'SEA' and a2 = 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a3) from t1 where a3 = 'SEA' and a2 = 2;
max(a3)
SEA
explain
select max(a3) from t1 where a3 = 'DEN' and a2 = 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No matching min/max row
select max(a3) from t1 where a3 = 'DEN' and a2 = 2;
max(a3)
NULL
explain
select max(t1.a3), min(t2.a2) from t1, t2 where t1.a2 = 2 and t1.a3 < 'MIN' and t2.a3 = 'CA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(t1.a3), min(t2.a2) from t1, t2 where t1.a2 = 2 and t1.a3 < 'MIN' and t2.a3 = 'CA';
max(t1.a3) min(t2.a2)
CHI Los Angeles
explain
select max(a3) from t1 where a2 is null and a2 = 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
select max(a3) from t1 where a2 is null and a2 = 2;
max(a3)
NULL
explain
select max(a2) from t1 where a2 >= 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select max(a2) from t1 where a2 >= 1;
max(a2)
3
explain
select min(a3) from t1 where a2 = 2 and a3 < 'SEA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
select min(a3) from t1 where a2 = 2 and a3 < 'SEA';
min(a3)
CHI
explain
select min(a1) from t1 where a1 > 'KKK' or a1 < 'XXX';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 0 NULL 15 Using where; Using index
explain
select min(a1) from t1 where a1 != 'KKK';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL PRIMARY 3 NULL 14 Using where; Using index
explain
select max(a3) from t1 where a2 < 2 and a3 < 'SEA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range k1 k1 3 NULL 5 Using where; Using index
explain
select max(t1.a3), min(t2.a2) from t1, t2 where t1.a2 = 2 and t1.a3 < 'MIN' and t2.a3 > 'CA';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range k1 k1 7 NULL 1 Using where; Using index
1 SIMPLE t2 range k1 k1 3 NULL 4 Using where; Using index
drop table if exists t1, t2;
mysql-test/t/func_group.test
View file @
593b2fc2
...
@@ -121,11 +121,158 @@ explain select min(a2) from t1;
...
@@ -121,11 +121,158 @@ explain select min(a2) from t1;
explain
select
max
(
t1
.
a1
),
max
(
t2
.
a2
)
from
t1
,
t2
;
explain
select
max
(
t1
.
a1
),
max
(
t2
.
a2
)
from
t1
,
t2
;
insert
into
t2
values
(
'AAA'
,
10
,
0.5
);
insert
into
t2
values
(
'AAA'
,
10
,
0.5
);
insert
into
t2
values
(
'BBB'
,
20
,
1.0
);
select
t1
.
a1
,
t1
.
a2
,
t2
.
a1
,
t2
.
a2
from
t1
,
t2
;
select
max
(
t1
.
a1
),
max
(
t2
.
a1
)
from
t1
,
t2
where
t2
.
a2
=
9
;
select
max
(
t1
.
a1
),
max
(
t2
.
a1
)
from
t1
,
t2
where
t2
.
a2
=
9
;
select
max
(
t2
.
a1
),
max
(
t1
.
a1
)
from
t1
,
t2
where
t2
.
a2
=
9
;
select
max
(
t2
.
a1
),
max
(
t1
.
a1
)
from
t1
,
t2
where
t2
.
a2
=
9
;
select
t1
.
a1
,
t1
.
a2
,
t2
.
a1
,
t2
.
a2
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
;
select
t1
.
a1
,
t1
.
a2
,
t2
.
a1
,
t2
.
a2
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
;
select
max
(
t1
.
a2
)
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
;
select
max
(
t1
.
a2
)
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
;
select
max
(
t
1
.
a2
)
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
where
t1
.
a1
=
20
;
select
max
(
t
2
.
a1
)
from
t2
left
outer
join
t1
on
t2
.
a2
=
10
where
t2
.
a2
=
20
;
select
max
(
t
1
.
a2
)
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
where
t1
.
a1
=
10
;
select
max
(
t
2
.
a1
)
from
t2
left
outer
join
t1
on
t2
.
a2
=
10
where
t2
.
a2
=
10
;
select
max
(
t2
.
a1
)
from
t1
left
outer
join
t2
on
t1
.
a2
=
t2
.
a1
and
1
=
0
where
t2
.
a1
=
'AAA'
;
select
max
(
t2
.
a1
)
from
t1
left
outer
join
t2
on
t1
.
a2
=
t2
.
a1
and
1
=
0
where
t2
.
a1
=
'AAA'
;
select
max
(
t1
.
a2
),
max
(
t2
.
a1
)
from
t1
left
outer
join
t2
on
t1
.
a1
=
10
;
drop
table
t1
,
t2
;
drop
table
t1
,
t2
;
#
# Tests to check MIN/MAX query optimization
#
# Create database schema
create
table
t1
(
a1
char
(
3
)
primary
key
,
a2
smallint
,
a3
char
(
3
),
a4
real
,
a5
date
,
key
k1
(
a2
,
a3
),
key
k2
(
a4
desc
,
a1
),
key
k3
(
a5
,
a1
)
);
create
table
t2
(
a1
char
(
3
)
primary
key
,
a2
char
(
17
),
a3
char
(
2
),
a4
char
(
3
),
key
k1
(
a3
,
a2
),
key
k2
(
a4
)
);
# Populate table t1
insert
into
t1
values
(
'AME'
,
0
,
'SEA'
,
0.100
,
date
'1942-02-19'
);
insert
into
t1
values
(
'HBR'
,
1
,
'SEA'
,
0.085
,
date
'1948-03-05'
);
insert
into
t1
values
(
'BOT'
,
2
,
'SEA'
,
0.085
,
date
'1951-11-29'
);
insert
into
t1
values
(
'BMC'
,
3
,
'SEA'
,
0.085
,
date
'1958-09-08'
);
insert
into
t1
values
(
'TWU'
,
0
,
'LAX'
,
0.080
,
date
'1969-10-05'
);
insert
into
t1
values
(
'BDL'
,
0
,
'DEN'
,
0.080
,
date
'1960-11-27'
);
insert
into
t1
values
(
'DTX'
,
1
,
'NYC'
,
0.080
,
date
'1961-05-04'
);
insert
into
t1
values
(
'PLS'
,
1
,
'WDC'
,
0.075
,
date
'1949-01-02'
);
insert
into
t1
values
(
'ZAJ'
,
2
,
'CHI'
,
0.075
,
date
'1960-06-15'
);
insert
into
t1
values
(
'VVV'
,
2
,
'MIN'
,
0.075
,
date
'1959-06-28'
);
insert
into
t1
values
(
'GTM'
,
3
,
'DAL'
,
0.070
,
date
'1977-09-23'
);
insert
into
t1
values
(
'SSJ'
,
null
,
'CHI'
,
null
,
date
'1974-03-19'
);
insert
into
t1
values
(
'KKK'
,
3
,
'ATL'
,
null
,
null
);
insert
into
t1
values
(
'XXX'
,
null
,
'MIN'
,
null
,
null
);
# Populate table t2
insert
into
t2
values
(
'TKF'
,
'Seattle'
,
'WA'
,
'AME'
);
insert
into
t2
values
(
'LCC'
,
'Los Angeles'
,
'CA'
,
'TWU'
);
insert
into
t2
values
(
'DEN'
,
'Denver'
,
'CO'
,
'BDL'
);
insert
into
t2
values
(
'SDC'
,
'San Diego'
,
'CA'
,
'TWU'
);
insert
into
t2
values
(
'NOL'
,
'New Orleans'
,
'LA'
,
'GTM'
);
insert
into
t2
values
(
'LAK'
,
'Los Angeles'
,
'CA'
,
'TWU'
);
# Show the table contents
select
*
from
t1
;
select
*
from
t2
;
# Queries with min/max functions
# which regular min/max optimization are applied to
explain
select
min
(
a1
)
from
t1
;
select
min
(
a1
)
from
t1
;
explain
select
max
(
a4
)
from
t1
;
select
max
(
a4
)
from
t1
;
explain
select
min
(
a5
),
max
(
a5
)
from
t1
;
select
min
(
a5
),
max
(
a5
)
from
t1
;
explain
select
min
(
a3
)
from
t1
where
a2
=
2
;
select
min
(
a3
)
from
t1
where
a2
=
2
;
explain
select
min
(
a1
),
max
(
a1
)
from
t1
where
a4
=
0.080
;
select
min
(
a1
),
max
(
a1
)
from
t1
where
a4
=
0.080
;
explain
select
min
(
t1
.
a5
),
max
(
t2
.
a3
)
from
t1
,
t2
;
select
min
(
t1
.
a5
),
max
(
t2
.
a3
)
from
t1
,
t2
;
explain
select
min
(
t1
.
a3
),
max
(
t2
.
a2
)
from
t1
,
t2
where
t1
.
a2
=
0
and
t2
.
a3
=
'CA'
;
select
min
(
t1
.
a3
),
max
(
t2
.
a2
)
from
t1
,
t2
where
t1
.
a2
=
0
and
t2
.
a3
=
'CA'
;
# Queries with min/max functions
# which extended min/max optimization are applied to
explain
select
min
(
a1
)
from
t1
where
a1
>
'KKK'
;
select
min
(
a1
)
from
t1
where
a1
>
'KKK'
;
explain
select
min
(
a1
)
from
t1
where
a1
>=
'KKK'
;
select
min
(
a1
)
from
t1
where
a1
>=
'KKK'
;
explain
select
max
(
a3
)
from
t1
where
a2
=
2
and
a3
<
'SEA'
;
select
max
(
a3
)
from
t1
where
a2
=
2
and
a3
<
'SEA'
;
explain
select
max
(
a5
)
from
t1
where
a5
<
date
'1970-01-01'
;
select
max
(
a5
)
from
t1
where
a5
<
date
'1970-01-01'
;
explain
select
max
(
a3
)
from
t1
where
a2
is
null
;
select
max
(
a3
)
from
t1
where
a2
is
null
;
explain
select
max
(
a3
)
from
t1
where
a2
=
0
and
a3
between
'K'
and
'Q'
;
select
max
(
a3
)
from
t1
where
a2
=
0
and
a3
between
'K'
and
'Q'
;
explain
select
min
(
a1
),
max
(
a1
)
from
t1
where
a1
between
'A'
and
'P'
;
select
min
(
a1
),
max
(
a1
)
from
t1
where
a1
between
'A'
and
'P'
;
explain
select
max
(
a3
)
from
t1
where
a3
<
'SEA'
and
a2
=
2
and
a3
<=
'MIN'
;
select
max
(
a3
)
from
t1
where
a3
<
'SEA'
and
a2
=
2
and
a3
<=
'MIN'
;
explain
select
max
(
a3
)
from
t1
where
a3
=
'SEA'
and
a2
=
2
;
select
max
(
a3
)
from
t1
where
a3
=
'SEA'
and
a2
=
2
;
explain
select
max
(
a3
)
from
t1
where
a3
=
'DEN'
and
a2
=
2
;
select
max
(
a3
)
from
t1
where
a3
=
'DEN'
and
a2
=
2
;
explain
select
max
(
t1
.
a3
),
min
(
t2
.
a2
)
from
t1
,
t2
where
t1
.
a2
=
2
and
t1
.
a3
<
'MIN'
and
t2
.
a3
=
'CA'
;
select
max
(
t1
.
a3
),
min
(
t2
.
a2
)
from
t1
,
t2
where
t1
.
a2
=
2
and
t1
.
a3
<
'MIN'
and
t2
.
a3
=
'CA'
;
explain
select
max
(
a3
)
from
t1
where
a2
is
null
and
a2
=
2
;
select
max
(
a3
)
from
t1
where
a2
is
null
and
a2
=
2
;
explain
select
max
(
a2
)
from
t1
where
a2
>=
1
;
select
max
(
a2
)
from
t1
where
a2
>=
1
;
explain
select
min
(
a3
)
from
t1
where
a2
=
2
and
a3
<
'SEA'
;
select
min
(
a3
)
from
t1
where
a2
=
2
and
a3
<
'SEA'
;
# Queries to which max/min optimization is not applied
explain
select
min
(
a1
)
from
t1
where
a1
>
'KKK'
or
a1
<
'XXX'
;
explain
select
min
(
a1
)
from
t1
where
a1
!=
'KKK'
;
explain
select
max
(
a3
)
from
t1
where
a2
<
2
and
a3
<
'SEA'
;
explain
select
max
(
t1
.
a3
),
min
(
t2
.
a2
)
from
t1
,
t2
where
t1
.
a2
=
2
and
t1
.
a3
<
'MIN'
and
t2
.
a3
>
'CA'
;
# Clean up
drop
table
if
exists
t1
,
t2
;
\ No newline at end of file
sql/opt_sum.cc
View file @
593b2fc2
...
@@ -15,27 +15,59 @@
...
@@ -15,27 +15,59 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Optimizing of many different type of queries with GROUP functions */
/*
Optimising of MIN(), MAX() and COUNT(*) queries without 'group by' clause
by replacing the aggregate expression with a constant.
Given a table with a compound key on columns (a,b,c), the following
types of queries are optimised (assuming the table handler supports
the required methods)
SELECT COUNT(*) FROM t1[,t2,t3,...]
SELECT MIN(b) FROM t1 WHERE a=const
SELECT MAX(c) FROM t1 WHERE a=const AND b=const
SELECT MAX(b) FROM t1 WHERE a=const AND b<const
SELECT MIN(b) FROM t1 WHERE a=const AND b>const
SELECT MIN(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
SELECT MAX(b) FROM t1 WHERE a=const AND b BETWEEN const AND const
Instead of '<' one can use '<=', '>', '>=' and '=' as well.
Instead of 'a=const' the condition 'a IS NULL' can be used.
If all selected fields are replaced then we will also remove all
involved tables and return the answer without any join. Thus, the
following query will be replaced with a row of two constants:
SELECT MAX(b), MIN(d) FROM t1,t2
WHERE a=const AND b<const AND d>const
(assuming a index for column d of table t2 is defined)
*/
#include "mysql_priv.h"
#include "mysql_priv.h"
#include "sql_select.h"
#include "sql_select.h"
static
bool
find_range_key
(
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
);
static
bool
find_key_for_maxmin
(
bool
max_fl
,
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
,
uint
*
range_fl
,
uint
*
key_prefix_length
);
static
int
reckey_in_range
(
bool
max_fl
,
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
,
uint
range_fl
,
uint
prefix_len
);
static
int
maxmin_in_range
(
bool
max_fl
,
Field
*
field
,
COND
*
cond
);
/*
/*
Substitutes constants for some COUNT(), MIN() and MAX() functions.
Substitutes constants for some COUNT(), MIN() and MAX() functions.
SYNOPSIS
SYNOPSIS
opt_sum_query()
opt_sum_query()
tables
Tables in query
tables
Tables in query
all_fields
All fields to be returned
all_fields
All fields to be returned
conds
WHERE clause
conds
WHERE clause
NOTE:
NOTE:
This function is only called for queries with sum functions and no
This function is only called for queries with sum functions and no
GROUP BY part.
GROUP BY part.
RETURN VALUES
RETURN VALUES
0 No errors
0 No errors
1 if all items was resolved
1 if all items was resolved
-1 on impossible conditions
-1 on impossible conditions
...
@@ -62,13 +94,13 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
...
@@ -62,13 +94,13 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
outer_tables
|=
tl
->
table
->
map
;
outer_tables
|=
tl
->
table
->
map
;
/*
/*
We can't optimise LEFT JOIN in cases where the WHERE condition
We can't optimise LEFT JOIN in cases where the WHERE condition
restricts the table that is used, like in:
restricts the table that is used, like in:
SELECT MAX(t1.a) FROM t1 LEFT JOIN t2 join-condition
SELECT MAX(t1.a) FROM t1 LEFT JOIN t2 join-condition
WHERE t2.field IS NULL;
WHERE t2.field IS NULL;
*/
*/
if
(
tl
->
table
->
map
&
where_tables
)
if
(
tl
->
table
->
map
&
where_tables
)
return
0
;
return
0
;
}
}
else
else
used_tables
|=
tl
->
table
->
map
;
used_tables
|=
tl
->
table
->
map
;
...
@@ -86,350 +118,648 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
...
@@ -86,350 +118,648 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
Item_sum
*
item_sum
=
(((
Item_sum
*
)
item
));
Item_sum
*
item_sum
=
(((
Item_sum
*
)
item
));
switch
(
item_sum
->
sum_func
())
{
switch
(
item_sum
->
sum_func
())
{
case
Item_sum
:
:
COUNT_FUNC
:
case
Item_sum
:
:
COUNT_FUNC
:
/*
/*
If the expr in count(expr) can never be null we can change this
If the expr in count(expr) can never be null we can change this
to the number of rows in the tables
to the number of rows in the tables
*/
*/
if
(
!
conds
&&
!
((
Item_sum_count
*
)
item
)
->
args
[
0
]
->
maybe_null
)
if
(
!
conds
&&
!
((
Item_sum_count
*
)
item
)
->
args
[
0
]
->
maybe_null
)
{
{
longlong
count
=
1
;
longlong
count
=
1
;
TABLE_LIST
*
table
;
TABLE_LIST
*
table
;
for
(
table
=
tables
;
table
;
table
=
table
->
next
)
for
(
table
=
tables
;
table
;
table
=
table
->
next
)
{
{
if
(
outer_tables
||
(
table
->
table
->
file
->
table_flags
()
&
if
(
outer_tables
||
(
table
->
table
->
file
->
table_flags
()
&
HA_NOT_EXACT_COUNT
))
HA_NOT_EXACT_COUNT
))
{
{
const_result
=
0
;
// Can't optimize left join
const_result
=
0
;
// Can't optimize left join
break
;
break
;
}
}
tables
->
table
->
file
->
info
(
HA_STATUS_VARIABLE
|
HA_STATUS_NO_LOCK
);
tables
->
table
->
file
->
info
(
HA_STATUS_VARIABLE
|
HA_STATUS_NO_LOCK
);
count
*=
table
->
table
->
file
->
records
;
count
*=
table
->
table
->
file
->
records
;
}
}
if
(
!
table
)
if
(
!
table
)
{
{
((
Item_sum_count
*
)
item
)
->
make_const
(
count
);
((
Item_sum_count
*
)
item
)
->
make_const
(
count
);
recalc_const_item
=
1
;
recalc_const_item
=
1
;
}
}
}
}
else
else
const_result
=
0
;
const_result
=
0
;
break
;
break
;
case
Item_sum
:
:
MIN_FUNC
:
case
Item_sum
:
:
MIN_FUNC
:
{
{
/*
/*
If MIN(expr) is the first part of a key or if all previous
If MIN(expr) is the first part of a key or if all previous
parts of the key is found in the COND, then we can use
parts of the key is found in the COND, then we can use
indexes to find the key.
indexes to find the key.
*/
*/
Item
*
expr
=
item_sum
->
args
[
0
];
Item
*
expr
=
item_sum
->
args
[
0
];
if
(
expr
->
type
()
==
Item
::
FIELD_ITEM
)
if
(
expr
->
type
()
==
Item
::
FIELD_ITEM
)
{
{
byte
key_buff
[
MAX_KEY_LENGTH
];
byte
key_buff
[
MAX_KEY_LENGTH
];
TABLE_REF
ref
;
TABLE_REF
ref
;
ref
.
key_buff
=
key_buff
;
uint
range_fl
,
prefix_len
;
Item_field
*
item_field
=
((
Item_field
*
)
expr
);
TABLE
*
table
=
item_field
->
field
->
table
;
ref
.
key_buff
=
key_buff
;
Item_field
*
item_field
=
((
Item_field
*
)
expr
);
if
((
outer_tables
&
table
->
map
)
||
TABLE
*
table
=
item_field
->
field
->
table
;
(
!
find_range_key
(
&
ref
,
item_field
->
field
,
conds
)))
{
/*
const_result
=
0
;
Look for a partial key that can be used for optimization.
break
;
If we succeed, ref.key_length will contain the length of
}
this key, while prefix_len will contain the length of
bool
error
=
table
->
file
->
index_init
((
uint
)
ref
.
key
);
the beginning of this key without field used in MIN()
enum
ha_rkey_function
find_flag
=
HA_READ_KEY_OR_NEXT
;
*/
uint
prefix_len
=
ref
.
key_length
;
if
((
outer_tables
&
table
->
map
)
||
/*
!
find_key_for_maxmin
(
0
,
&
ref
,
item_field
->
field
,
conds
,
If we are doing MIN() on a column with NULL fields
&
range_fl
,
&
prefix_len
))
we must read the key after the NULL column
{
*/
const_result
=
0
;
if
(
item_field
->
field
->
null_bit
)
break
;
{
}
ref
.
key_buff
[
ref
.
key_length
++
]
=
1
;
bool
error
=
table
->
file
->
index_init
((
uint
)
ref
.
key
);
find_flag
=
HA_READ_AFTER_KEY
;
enum
ha_rkey_function
find_flag
=
range_fl
&
NEAR_MIN
?
}
HA_READ_AFTER_KEY
:
HA_READ_KEY_OR_NEXT
;
/*
if
(
!
ref
.
key_length
)
If we are doing MIN() on a column with NULL fields
error
=
table
->
file
->
index_first
(
table
->
record
[
0
])
!=
0
;
we must read the key after the NULL column
else
*/
error
=
table
->
file
->
index_read
(
table
->
record
[
0
],
key_buff
,
if
(
item_field
->
field
->
null_bit
)
ref
.
key_length
,
{
find_flag
)
||
ref
.
key_buff
[
ref
.
key_length
++
]
=
1
;
key_cmp
(
table
,
key_buff
,
ref
.
key
,
prefix_len
);
find_flag
=
HA_READ_AFTER_KEY
;
if
(
table
->
key_read
)
}
{
table
->
key_read
=
0
;
if
(
!
ref
.
key_length
)
table
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
error
=
table
->
file
->
index_first
(
table
->
record
[
0
])
!=
0
;
}
else
table
->
file
->
index_end
();
error
=
table
->
file
->
index_read
(
table
->
record
[
0
],
key_buff
,
if
(
error
)
ref
.
key_length
,
return
-
1
;
// No rows matching where
find_flag
)
||
removed_tables
|=
table
->
map
;
reckey_in_range
(
0
,
&
ref
,
item_field
->
field
,
}
conds
,
range_fl
,
prefix_len
);
else
if
(
!
expr
->
const_item
())
// This is VERY seldom false
if
(
table
->
key_read
)
{
{
const_result
=
0
;
table
->
key_read
=
0
;
break
;
table
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
}
}
((
Item_sum_min
*
)
item_sum
)
->
reset
();
table
->
file
->
index_end
();
((
Item_sum_min
*
)
item_sum
)
->
make_const
();
if
(
error
)
recalc_const_item
=
1
;
return
-
1
;
// No rows matching where
break
;
removed_tables
|=
table
->
map
;
}
else
if
(
!
expr
->
const_item
())
// This is VERY seldom false
{
const_result
=
0
;
break
;
}
((
Item_sum_min
*
)
item_sum
)
->
reset
();
((
Item_sum_min
*
)
item_sum
)
->
make_const
();
recalc_const_item
=
1
;
break
;
}
}
case
Item_sum
:
:
MAX_FUNC
:
case
Item_sum
:
:
MAX_FUNC
:
{
{
/*
/*
If MAX(expr) is the first part of a key or if all previous
If MAX(expr) is the first part of a key or if all previous
parts of the key is found in the COND, then we can use
parts of the key is found in the COND, then we can use
indexes to find the key.
indexes to find the key.
*/
*/
Item
*
expr
=
item_sum
->
args
[
0
];
Item
*
expr
=
item_sum
->
args
[
0
];
if
(
expr
->
type
()
==
Item
::
FIELD_ITEM
)
if
(
expr
->
type
()
==
Item
::
FIELD_ITEM
)
{
{
byte
key_buff
[
MAX_KEY_LENGTH
];
byte
key_buff
[
MAX_KEY_LENGTH
];
TABLE_REF
ref
;
TABLE_REF
ref
;
ref
.
key_buff
=
key_buff
;
uint
range_fl
,
prefix_len
;
TABLE
*
table
=
((
Item_field
*
)
expr
)
->
field
->
table
;
ref
.
key_buff
=
key_buff
;
if
((
outer_tables
&
table
->
map
)
||
Item_field
*
item_field
=
((
Item_field
*
)
expr
);
!
find_range_key
(
&
ref
,
((
Item_field
*
)
expr
)
->
field
,
conds
))
TABLE
*
table
=
item_field
->
field
->
table
;
{
const_result
=
0
;
/*
break
;
Look for a partial key that can be used for optimization.
}
If we succeed, ref.key_length will contain the length of
if
((
table
->
file
->
table_flags
()
&
HA_NOT_READ_AFTER_KEY
))
this key, while prefix_len will contain the length of
{
the beginning of this key without field used in MAX()
const_result
=
0
;
*/
break
;
if
((
outer_tables
&
table
->
map
)
||
}
!
find_key_for_maxmin
(
1
,
&
ref
,
item_field
->
field
,
conds
,
bool
error
=
table
->
file
->
index_init
((
uint
)
ref
.
key
);
&
range_fl
,
&
prefix_len
))
{
if
(
!
ref
.
key_length
)
const_result
=
0
;
error
=
table
->
file
->
index_last
(
table
->
record
[
0
])
!=
0
;
break
;
else
}
{
if
((
table
->
file
->
table_flags
()
&
HA_NOT_READ_AFTER_KEY
))
error
=
table
->
file
->
index_read
(
table
->
record
[
0
],
key_buff
,
{
ref
.
key_length
,
const_result
=
0
;
HA_READ_PREFIX_LAST
)
||
break
;
key_cmp
(
table
,
key_buff
,
ref
.
key
,
ref
.
key_length
);
}
}
bool
error
=
table
->
file
->
index_init
((
uint
)
ref
.
key
);
if
(
table
->
key_read
)
{
if
(
!
ref
.
key_length
)
table
->
key_read
=
0
;
error
=
table
->
file
->
index_last
(
table
->
record
[
0
])
!=
0
;
table
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
else
}
error
=
table
->
file
->
index_read
(
table
->
record
[
0
],
key_buff
,
table
->
file
->
index_end
();
ref
.
key_length
,
if
(
error
)
range_fl
&
NEAR_MAX
?
return
-
1
;
// Impossible query
HA_READ_BEFORE_KEY
:
removed_tables
|=
table
->
map
;
HA_READ_PREFIX_LAST_OR_PREV
)
||
}
reckey_in_range
(
1
,
&
ref
,
item_field
->
field
,
else
if
(
!
expr
->
const_item
())
// This is VERY seldom false
conds
,
range_fl
,
prefix_len
);
{
if
(
table
->
key_read
)
const_result
=
0
;
{
break
;
table
->
key_read
=
0
;
}
table
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
((
Item_sum_min
*
)
item_sum
)
->
reset
();
}
((
Item_sum_min
*
)
item_sum
)
->
make_const
();
table
->
file
->
index_end
();
recalc_const_item
=
1
;
if
(
error
)
break
;
return
-
1
;
// Impossible query
removed_tables
|=
table
->
map
;
}
else
if
(
!
expr
->
const_item
())
// This is VERY seldom false
{
const_result
=
0
;
break
;
}
((
Item_sum_min
*
)
item_sum
)
->
reset
();
((
Item_sum_min
*
)
item_sum
)
->
make_const
();
recalc_const_item
=
1
;
break
;
}
}
default:
default:
const_result
=
0
;
const_result
=
0
;
break
;
break
;
}
}
}
}
else
if
(
const_result
)
else
if
(
const_result
)
{
{
if
(
recalc_const_item
)
if
(
recalc_const_item
)
item
->
update_used_tables
();
item
->
update_used_tables
();
if
(
!
item
->
const_item
())
if
(
!
item
->
const_item
())
const_result
=
0
;
const_result
=
0
;
}
}
}
}
/*
/*
If we have a where clause, we can only ignore searching in the
If we have a where clause, we can only ignore searching in the
tables if MIN/MAX optimisation replaced all used tables
tables if MIN/MAX optimisation replaced all used tables
This is to not to
use replaced values in case of:
We do not
use replaced values in case of:
SELECT MIN(key) FROM table_1, empty_table
SELECT MIN(key) FROM table_1, empty_table
removed_tables is != 0 if we have used MIN() or MAX().
removed_tables is != 0 if we have used MIN() or MAX().
*/
*/
if
(
removed_tables
&&
used_tables
!=
removed_tables
)
if
(
removed_tables
&&
used_tables
!=
removed_tables
)
const_result
=
0
;
// We didn't remove all tables
const_result
=
0
;
// We didn't remove all tables
return
const_result
;
return
const_result
;
}
}
/* Count in how many times table is used (up to MAX_KEY_PARTS+1) */
uint
count_table_entries
(
COND
*
cond
,
TABLE
*
table
)
/*
{
Test if the predicate compares a field with constants
if
(
cond
->
type
()
==
Item
::
COND_ITEM
)
{
if
(((
Item_cond
*
)
cond
)
->
functype
()
==
Item_func
::
COND_OR_FUNC
)
return
(
cond
->
used_tables
()
&
table
->
map
)
?
MAX_REF_PARTS
+
1
:
0
;
List_iterator_fast
<
Item
>
li
(
*
((
Item_cond
*
)
cond
)
->
argument_list
());
SYNOPSIS
Item
*
item
;
simple_pred()
uint
count
=
0
;
func_item in: Predicate item
while
((
item
=
li
++
))
args out: Here we store the field followed by constants
inv_order out: Is set to 1 if the predicate is of the form 'const op field'
RETURN
0 func_item is a simple predicate: a field is compared with constants
1 Otherwise
*/
static
bool
simple_pred
(
Item_func
*
func_item
,
Item
**
args
,
bool
*
inv_order
)
{
Item
*
item
;
*
inv_order
=
0
;
switch
(
func_item
->
argument_count
())
{
case
1
:
/* field IS NULL */
item
=
func_item
->
arguments
()[
0
];
if
(
item
->
type
()
!=
Item
::
FIELD_ITEM
)
return
0
;
args
[
0
]
=
item
;
break
;
case
2
:
/* 'field op const' or 'const op field' */
item
=
func_item
->
arguments
()[
0
];
if
(
item
->
type
()
==
Item
::
FIELD_ITEM
)
{
{
if
((
count
+=
count_table_entries
(
item
,
table
))
>
MAX_REF_PARTS
)
args
[
0
]
=
item
;
return
MAX_REF_PARTS
+
1
;
item
=
func_item
->
arguments
()[
1
];
if
(
!
item
->
const_item
())
return
0
;
args
[
1
]
=
item
;
}
}
return
count
;
else
if
(
item
->
const_item
())
}
if
(
cond
->
type
()
==
Item
::
FUNC_ITEM
&&
(((
Item_func
*
)
cond
)
->
functype
()
==
Item_func
::
EQ_FUNC
||
(((
Item_func
*
)
cond
)
->
functype
()
==
Item_func
::
EQUAL_FUNC
))
&&
cond
->
used_tables
()
==
table
->
map
)
{
Item
*
left_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
0
];
Item
*
right_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
1
];
if
(
left_item
->
type
()
==
Item
::
FIELD_ITEM
)
{
{
if
(
!
(((
Item_field
*
)
left_item
)
->
field
->
flags
&
PART_KEY_FLAG
)
||
args
[
1
]
=
item
;
!
right_item
->
const_item
())
item
=
func_item
->
arguments
()[
1
];
return
MAX_REF_PARTS
+
1
;
if
(
item
->
type
()
!=
Item
::
FIELD_ITEM
)
return
1
;
return
0
;
args
[
0
]
=
item
;
*
inv_order
=
1
;
}
}
if
(
right_item
->
type
()
==
Item
::
FIELD_ITEM
)
else
return
0
;
break
;
case
3
:
/* field BETWEEN const AND const */
item
=
func_item
->
arguments
()[
0
];
if
(
item
->
type
()
==
Item
::
FIELD_ITEM
)
{
{
if
(
!
(((
Item_field
*
)
right_item
)
->
field
->
flags
&
PART_KEY_FLAG
)
||
args
[
0
]
=
item
;
!
left_item
->
const_item
())
for
(
int
i
=
1
;
i
<=
2
;
i
++
)
return
MAX_REF_PARTS
+
1
;
{
return
1
;
item
=
func_item
->
arguments
()[
i
];
if
(
!
item
->
const_item
())
return
0
;
args
[
i
]
=
item
;
}
}
}
else
return
0
;
}
}
return
(
cond
->
used_tables
()
&
table
->
map
)
?
MAX_REF_PARTS
+
1
:
0
;
return
1
;
}
}
/* check that the field is usable as key part */
/*
Check whether a condition matches a key to get {MAX|MIN}(field):
SYNOPSIS
matching_cond()
max_fl in: Set to 1 if we are optimising MAX()
ref in/out: Reference to the structure we store the key value
keyinfo in Reference to the key info
field_part in: Pointer to the key part for the field
cond in WHERE condition
key_part_used in/out: Map of matchings parts
range_fl in/out: Says whether including key will be used
prefix_len out: Length of common key part for the range
where MAX/MIN is searched for
DESCRIPTION
For the index specified by the keyinfo parameter, index that
contains field as its component (field_part), the function
checks whether the condition cond is a conjunction and all its
conjuncts referring to the columns of the same table as column
field are one of the following forms:
- f_i= const_i or const_i= f_i or f_i is null,
where f_i is part of the index
- field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field
- field between const1 and const2
RETURN
0 Index can't be used.
1 We can use index to get MIN/MAX value
*/
bool
part_of_cond
(
COND
*
cond
,
Field
*
field
)
static
bool
matching_cond
(
bool
max_fl
,
TABLE_REF
*
ref
,
KEY
*
keyinfo
,
KEY_PART_INFO
*
field_part
,
COND
*
cond
,
key_part_map
*
key_part_used
,
uint
*
range_fl
,
uint
*
prefix_len
)
{
{
if
(
!
cond
)
return
1
;
Field
*
field
=
field_part
->
field
;
if
(
!
(
cond
->
used_tables
()
&
field
->
table
->
map
))
{
/* Condition doesn't restrict the used table */
return
1
;
}
if
(
cond
->
type
()
==
Item
::
COND_ITEM
)
if
(
cond
->
type
()
==
Item
::
COND_ITEM
)
{
{
if
(((
Item_cond
*
)
cond
)
->
functype
()
==
Item_func
::
COND_OR_FUNC
)
if
(((
Item_cond
*
)
cond
)
->
functype
()
==
Item_func
::
COND_OR_FUNC
)
return
0
;
// Already checked
return
0
;
/* AND */
List_iterator_fast
<
Item
>
li
(
*
((
Item_cond
*
)
cond
)
->
argument_list
());
List_iterator_fast
<
Item
>
li
(
*
((
Item_cond
*
)
cond
)
->
argument_list
());
Item
*
item
;
Item
*
item
;
while
((
item
=
li
++
))
while
((
item
=
li
++
))
{
{
if
(
part_of_cond
(
item
,
field
))
if
(
!
matching_cond
(
max_fl
,
ref
,
keyinfo
,
field_part
,
item
,
return
1
;
key_part_used
,
range_fl
,
prefix_len
))
return
0
;
}
}
return
1
;
}
if
(
cond
->
type
()
!=
Item
::
FUNC_ITEM
)
return
0
;
// Not operator, can't optimize
bool
eq_type
=
0
;
// =, <=> or IS NULL
bool
noeq_type
=
0
;
// < or >
bool
less_fl
=
0
;
// < or <=
bool
is_null
=
0
;
bool
between
=
0
;
switch
(((
Item_func
*
)
cond
)
->
functype
())
{
case
Item_func
:
:
ISNULL_FUNC
:
is_null
=
1
;
/* fall through */
case
Item_func
:
:
EQ_FUNC
:
case
Item_func
:
:
EQUAL_FUNC
:
eq_type
=
1
;
break
;
case
Item_func
:
:
LT_FUNC
:
noeq_type
=
1
;
/* fall through */
case
Item_func
:
:
LE_FUNC
:
less_fl
=
1
;
break
;
case
Item_func
:
:
GT_FUNC
:
noeq_type
=
1
;
/* fall through */
case
Item_func
:
:
GE_FUNC
:
break
;
case
Item_func
:
:
BETWEEN
:
between
=
1
;
break
;
default:
return
0
;
// Can't optimize function
}
Item
*
args
[
3
];
bool
inv
;
/* Test if this is a comparison of a field and constant */
if
(
!
simple_pred
((
Item_func
*
)
cond
,
args
,
&
inv
))
return
0
;
if
(
inv
&&
!
eq_type
)
less_fl
=
1
-
less_fl
;
// Convert '<' -> '>' (etc)
/* Check if field is part of the tested partial key */
byte
*
key_ptr
=
ref
->
key_buff
;
KEY_PART_INFO
*
part
;
for
(
part
=
keyinfo
->
key_part
;
;
key_ptr
+=
part
++->
store_length
)
{
if
(
part
>
field_part
)
return
0
;
// Field is beyond the tested parts
if
(
part
->
field
->
eq
(((
Item_field
*
)
args
[
0
])
->
field
))
break
;
// Found a part od the key for the field
}
bool
is_field_part
=
part
==
field_part
;
if
(
!
(
is_field_part
||
eq_type
))
return
0
;
return
0
;
key_part_map
org_key_part_used
=
*
key_part_used
;
if
(
eq_type
||
between
||
max_fl
==
less_fl
)
{
uint
length
=
(
key_ptr
-
ref
->
key_buff
)
+
part
->
store_length
;
if
(
ref
->
key_length
<
length
)
/* Ultimately ref->key_length will contain the length of the search key */
ref
->
key_length
=
length
;
if
(
!*
prefix_len
&&
part
+
1
==
field_part
)
*
prefix_len
=
length
;
if
(
is_field_part
&&
eq_type
)
*
prefix_len
=
ref
->
key_length
;
*
key_part_used
|=
(
key_part_map
)
1
<<
(
part
-
keyinfo
->
key_part
);
}
}
if
(
cond
->
type
()
==
Item
::
FUNC_ITEM
&&
(((
Item_func
*
)
cond
)
->
functype
()
==
Item_func
::
EQ_FUNC
||
if
(
org_key_part_used
!=
*
key_part_used
||
((
Item_func
*
)
cond
)
->
functype
()
==
Item_func
::
EQUAL_FUNC
)
&&
(
is_field_part
&&
cond
->
used_tables
()
==
field
->
table
->
map
)
(
between
||
max_fl
==
less_fl
)
&&
!
cond
->
val_int
())
)
{
{
Item
*
left_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
0
];
/*
Item
*
right_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
1
];
It's the first predicate for this part or a predicate of the
if
(
left_item
->
type
()
==
Item
::
FIELD_ITEM
)
following form that moves upper/lower bounds for max/min values:
- field BETWEEN const AND const
- field {<|<=} const, when searching for MAX
- field {>|>=} const, when searching for MIN
*/
if
(
is_null
)
{
{
if
(((
Item_field
*
)
left_item
)
->
field
!=
field
||
part
->
field
->
set_null
();
!
right_item
->
const_item
())
*
key_ptr
=
(
byte
)
1
;
return
0
;
}
}
else
if
(
right_item
->
type
()
==
Item
::
FIELD_ITEM
)
else
{
{
if
(((
Item_field
*
)
right_item
)
->
field
!=
field
||
store_val_in_field
(
part
->
field
,
args
[
between
&&
max_fl
?
2
:
1
]);
!
left_item
->
const_item
())
if
(
part
->
null_bit
)
return
0
;
*
key_ptr
++=
(
byte
)
test
(
part
->
field
->
is_null
());
right_item
=
left_item
;
// const item in right
part
->
field
->
get_key_image
((
char
*
)
key_ptr
,
part
->
length
,
part
->
field
->
charset
(),
Field
::
itRAW
);
}
if
(
is_field_part
)
{
if
(
between
)
*
range_fl
&=
~
(
NO_MAX_RANGE
|
NO_MIN_RANGE
);
else
{
*
range_fl
&=
~
(
max_fl
?
NO_MAX_RANGE
:
NO_MIN_RANGE
);
if
(
noeq_type
)
*
range_fl
|=
(
max_fl
?
NEAR_MAX
:
NEAR_MIN
);
else
*
range_fl
&=
~
(
max_fl
?
NEAR_MAX
:
NEAR_MIN
);
}
}
}
store_val_in_field
(
field
,
right_item
);
return
1
;
}
}
return
0
;
else
if
(
eq_type
)
{
if
(
!
is_null
&&
!
cond
->
val_int
()
||
is_null
&&
!
test
(
part
->
field
->
is_null
()))
return
0
;
// Impossible test
}
else
if
(
is_field_part
)
*
range_fl
&=
~
(
max_fl
?
NO_MIN_RANGE
:
NO_MAX_RANGE
);
return
1
;
}
}
/* Check if we can get value for field by using a key */
/*
Check whether we can get value for {max|min}(field) by using a key.
static
bool
find_range_key
(
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
)
SYNOPSIS
find_key_for_maxmin()
max_fl in: 0 for MIN(field) / 1 for MAX(field)
ref in/out Reference to the structure we store the key value
field in: Field used inside MIN() / MAX()
cond in: WHERE condition
range_fl in/out Bit flags for how to search if key is ok
prefix_len out: Length of prefix for the search range
DESCRIPTION
If where condition is not a conjunction of 0 or more conjuct the
function returns false, otherwise it checks whether there is an
index including field as its k-th component/part such that:
1. for each previous component f_i there is one and only one conjunct
of the form: f_i= const_i or const_i= f_i or f_i is null
2. references to field occur only in conjucts of the form:
field {<|<=|>=|>|=} const or const {<|<=|>=|>|=} field or
field BETWEEN const1 AND const2
3. all references to the columns from the same table as column field
occur only in conjucts mentioned above.
If such an index exists the function through the ref parameter
returns the key value to find max/min for the field using the index,
the length of first (k-1) components of the key and flags saying
how to apply the key for the search max/min value.
(if we have a condition field = const, prefix_len contains the length
of the whole search key)
RETURN
0 Index can not be used to optimize MIN(field)/MAX(field)
1 Can use key to optimize MIN()/MAX()
In this case ref, range_fl and prefix_len are updated
*/
static
bool
find_key_for_maxmin
(
bool
max_fl
,
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
,
uint
*
range_fl
,
uint
*
prefix_len
)
{
{
if
(
!
(
field
->
flags
&
PART_KEY_FLAG
))
if
(
!
(
field
->
flags
&
PART_KEY_FLAG
))
return
0
;
// Not part of a key. Skip it
return
0
;
// Not key field
*
prefix_len
=
0
;
TABLE
*
table
=
field
->
table
;
uint
idx
=
0
;
TABLE
*
table
=
field
->
table
;
KEY
*
keyinfo
,
*
keyinfo_end
;
uint
idx
=
0
;
for
(
keyinfo
=
table
->
key_info
,
keyinfo_end
=
keyinfo
+
table
->
keys
;
keyinfo
!=
keyinfo_end
;
/* Check if some key has field as first key part */
keyinfo
++
,
idx
++
)
if
((
field
->
key_start
&
field
->
table
->
keys_in_use_for_query
)
&&
{
(
!
cond
||
!
(
cond
->
used_tables
()
&
table
->
map
)))
if
(
table
->
file
->
index_flags
(
idx
)
&
HA_WRONG_ASCII_ORDER
)
{
break
;
for
(
key_map
key
=
field
->
key_start
;;)
{
KEY_PART_INFO
*
part
,
*
part_end
;
for
(;
!
(
key
&
1
)
;
idx
++
)
key_part_map
key_part_to_use
=
0
;
key
>>=
1
;
for
(
part
=
keyinfo
->
key_part
,
part_end
=
part
+
keyinfo
->
key_parts
;
if
(
!
(
table
->
file
->
index_flags
(
idx
)
&
HA_WRONG_ASCII_ORDER
))
part
!=
part_end
;
break
;
// Key is ok
part
++
,
key_part_to_use
=
(
key_part_to_use
<<
1
)
|
1
)
/* Can't use this key, for looking up min() or max(), end if last one */
if
(
key
==
1
)
return
0
;
}
ref
->
key_length
=
0
;
ref
->
key
=
idx
;
if
(
field
->
part_of_key
&
((
key_map
)
1
<<
idx
))
{
{
table
->
key_read
=
1
;
if
(
field
->
eq
(
part
->
field
))
table
->
file
->
extra
(
HA_EXTRA_KEYREAD
);
{
ref
->
key
=
idx
;
ref
->
key_length
=
0
;
key_part_map
key_part_used
=
0
;
*
range_fl
=
NO_MIN_RANGE
|
NO_MAX_RANGE
;
if
(
matching_cond
(
max_fl
,
ref
,
keyinfo
,
part
,
cond
,
&
key_part_used
,
range_fl
,
prefix_len
)
&&
!
(
key_part_to_use
&
~
key_part_used
))
{
if
(
!
max_fl
&&
key_part_used
==
key_part_to_use
&&
part
->
null_bit
)
{
/*
SELECT MIN(key_part2) FROM t1 WHERE key_part1=const
If key_part2 may be NULL, then we want to find the first row
that is not null
*/
ref
->
key_buff
[
ref
->
key_length
++
]
=
1
;
*
range_fl
&=
~
NO_MIN_RANGE
;
*
range_fl
|=
NEAR_MIN
;
// > NULL
}
/*
The following test is false when the key in the key tree is
converted (for example to upper case)
*/
if
(
field
->
part_of_key
&
((
key_map
)
1
<<
idx
))
{
table
->
key_read
=
1
;
table
->
file
->
extra
(
HA_EXTRA_KEYREAD
);
}
return
1
;
}
}
}
}
return
1
;
// Ok to use key
}
}
/*
return
0
;
** Check if WHERE consist of exactly the previous key parts for some key
}
*/
if
(
!
cond
)
return
0
;
/*
uint
table_entries
=
count_table_entries
(
cond
,
table
);
Check whether found key is in range specified by conditions
if
(
!
table_entries
||
table_entries
>
MAX_REF_PARTS
)
SYNOPSIS
reckey_in_range()
max_fl in: 0 for MIN(field) / 1 for MAX(field)
ref in: Reference to the key value and info
field in: Field used the MIN/MAX expression
cond in: WHERE condition
range_fl in: Says whether there is a condition to to be checked
prefix_len in: Length of the constant part of the key
RETURN
0 ok
1 WHERE was not true for the found row
*/
static
int
reckey_in_range
(
bool
max_fl
,
TABLE_REF
*
ref
,
Field
*
field
,
COND
*
cond
,
uint
range_fl
,
uint
prefix_len
)
{
if
(
key_cmp
(
field
->
table
,
ref
->
key_buff
,
ref
->
key
,
prefix_len
))
return
1
;
if
(
!
cond
||
(
range_fl
&
(
max_fl
?
NO_MIN_RANGE
:
NO_MAX_RANGE
)))
return
0
;
return
0
;
return
maxmin_in_range
(
max_fl
,
field
,
cond
);
}
KEY
*
keyinfo
,
*
keyinfo_end
;
idx
=
0
;
/*
for
(
keyinfo
=
table
->
key_info
,
keyinfo_end
=
keyinfo
+
table
->
keys
;
Check whether {MAX|MIN}(field) is in range specified by conditions
keyinfo
!=
keyinfo_end
;
SYNOPSIS
keyinfo
++
,
idx
++
)
maxmin_in_range()
max_fl in: 0 for MIN(field) / 1 for MAX(field)
field in: Field used the MIN/MAX expression
cond in: WHERE condition
RETURN
0 ok
1 WHERE was not true for the found row
*/
static
int
maxmin_in_range
(
bool
max_fl
,
Field
*
field
,
COND
*
cond
)
{
/* If AND/OR condition */
if
(
cond
->
type
()
==
Item
::
COND_ITEM
)
{
{
if
(
table_entries
<
keyinfo
->
key_parts
)
List_iterator_fast
<
Item
>
li
(
*
((
Item_cond
*
)
cond
)
->
argument_list
());
Item
*
item
;
while
((
item
=
li
++
))
{
{
byte
*
key_ptr
=
ref
->
key_buff
;
if
(
maxmin_in_range
(
max_fl
,
field
,
item
))
KEY_PART_INFO
*
part
,
*
part_end
;
return
1
;
int
left_length
=
MAX_KEY_LENGTH
;
for
(
part
=
keyinfo
->
key_part
,
part_end
=
part
+
table_entries
;
part
!=
part_end
;
part
++
)
{
if
(
!
part_of_cond
(
cond
,
part
->
field
)
||
left_length
<
part
->
store_length
||
(
table
->
file
->
index_flags
(
idx
)
&
HA_WRONG_ASCII_ORDER
))
break
;
// Save found constant
if
(
part
->
null_bit
)
*
key_ptr
++=
(
byte
)
test
(
part
->
field
->
is_null
());
part
->
field
->
get_key_image
((
char
*
)
key_ptr
,
part
->
length
,
part
->
field
->
charset
(),
Field
::
itRAW
);
key_ptr
+=
part
->
store_length
-
test
(
part
->
null_bit
);
left_length
-=
part
->
store_length
;
}
if
(
part
==
part_end
&&
part
->
field
==
field
)
{
ref
->
key_length
=
(
uint
)
(
key_ptr
-
ref
->
key_buff
);
ref
->
key
=
idx
;
if
(
field
->
part_of_key
&
((
key_map
)
1
<<
idx
))
{
table
->
key_read
=
1
;
table
->
file
->
extra
(
HA_EXTRA_KEYREAD
);
}
return
1
;
// Ok to use key
}
}
}
return
0
;
}
}
return
0
;
// No possible key
if
(
cond
->
used_tables
()
!=
field
->
table
->
map
)
return
0
;
bool
less_fl
=
0
;
switch
(((
Item_func
*
)
cond
)
->
functype
())
{
case
Item_func
:
:
BETWEEN
:
return
cond
->
val_int
()
==
0
;
// Return 1 if WHERE is false
case
Item_func
:
:
LT_FUNC
:
case
Item_func
:
:
LE_FUNC
:
less_fl
=
1
;
case
Item_func
:
:
GT_FUNC
:
case
Item_func
:
:
GE_FUNC
:
{
Item
*
item
=
((
Item_func
*
)
cond
)
->
arguments
()[
1
];
/* In case of 'const op item' we have to swap the operator */
if
(
!
item
->
const_item
())
less_fl
=
1
-
less_fl
;
/*
We only have to check the expression if we are using an expression like
SELECT MAX(b) FROM t1 WHERE a=const AND b>const
not for
SELECT MAX(b) FROM t1 WHERE a=const AND b<const
*/
if
(
max_fl
!=
less_fl
)
return
cond
->
val_int
()
==
0
;
// Return 1 if WHERE is false
return
0
;
}
case
Item_func
:
:
EQ_FUNC
:
case
Item_func
:
:
EQUAL_FUNC
:
break
;
default:
// Keep compiler happy
DBUG_ASSERT
(
1
);
// Impossible
break
;
}
return
0
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment