一、MySQL子查询的位置
1
2
3
|
create
table
t1 (k1
int
primary
key
, c1
int
);
create
table
t2 (k2
int
primary
key
, c2
int
);
insert
into
t2
values
(1, 10), (2, 2), (3,30);
|
1
2
|
mysql>
select
t1.c1, (
select
t2.c2
from
t2)
from
t1, t2;
Empty
set
(0.00sec)
|
1
2
|
mysql>
insert
into
t1
values
(1, 1), (2, 2), (3, 3);
Query OK, 3
rows
affected (0.00 sec)
|
1
2
|
mysql>
select
t1.c1, (
select
t2.c2
from
t2)
from
t1, t2;
ERROR 1242(21000): Subquery
returns
more than 1 row
|
1
2
3
4
5
|
mysql>
delete
from
t2;
QueryOK, 3
rows
affected (0.00 sec)
mysql>
select
t1.c1, (
select
t2.c2
from
t2)
from
t1, t2;
Empty
set
(0.00 sec)
|
1
2
|
mysql>
insert
into
t2
values
(1, 10), (2, 2), (3, 30);
Query OK,3
rows
affected (0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
mysql>
select
t1.c1, (
select
t2.c2
from
t2
where
k2=1)
from
t1, t2;
+
------+-----------------------------------+
| c1 | (
select
t2.c2
from
t2
where
k2=1) |
+
------+-----------------------------------+
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
+
------+-----------------------------------+
|
1
2
|
mysql>
select
t1.c1, (selectt2.c2
from
t2
where
c2 > 1)
from
t1, t2;
ERROR 1242 (21000): Subqueryreturns more than 1 row
|
1
2
|
mysql>
select
*
from
t1, (
select
*
from
t2
where
t1.k1 = t2.k2);
ERROR 1248 (42000): Every derived
table
musthave its own alias
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql>
select
*
from
t1, (
select
*
from
t2)
as
a_t2;
+
----+------+----+------+
| k1 | c1 | k2 | c2 |
+
----+------+----+------+
| 1 | 1 | 1 | 10 |
| 2 | 2 | 1 | 10 |
| 3 | 3 | 1 | 10 |
| 1 | 1 | 2 | 2 |
| 2 | 2 | 2 | 2 |
| 3 | 3 | 2 | 2 |
| 1 | 1 | 3 | 30 |
| 2 | 2 | 3 | 30 |
| 3 | 3 | 3 | 30 |
+
----+------+----+------+
9
rows
in
set
(0.00 sec)
|
二、子查询的类型
1
2
3
4
5
6
7
|
mysql>
select
*
from
t1
where
c1 =
ANY
(
select
c2
from
t2
where
t2.c2 = t1.c1);
+
----+------+
| k1 | c1 |
+
----+------+
| 2 | 2 |
+
----+------+
1 row
in
set
(0.12 sec)
|
1
2
|
mysql>
select
*
from
t1
where
c1 =
ANY
(
select
c2
from
t2
where
t2.c2=10);
Empty
set
(0.02 sec)
|
三、如何对子查询进行优化
1
2
3
4
5
6
7
8
9
10
11
12
|
mysql>
select
*
from
t1
where
k1 < 10
and
(
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
t2.c2 = 1)
or
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
t2.c2 = 2)
-> );
+
----+------+
| k1 | c1 |
+
----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+
----+------+
3
rows
in
set
(0.12 sec)
|
1
2
3
4
5
6
7
8
9
10
11
12
|
mysql> explain extended
select
*
from
t1
where
k1 < 10
and
(
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
t2.c2 = 1)
or
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
t2.c2 = 2)
-> );
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 |
PRIMARY
| t1 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | 100.00 | Using
where
|
| 3 | SUBQUERY | t2 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | 100.00 | Using
where
|
| 2 | SUBQUERY | t2 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | 100.00 | Using
where
|
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
3
rows
in
set
, 1 warning (0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
11
|
mysql>
select
*
from
t1
where
k1 < 10
and
(
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
(t2.c2 = 1
or
t2.c2 = 2))
-> );
+
----+------+
| k1 | c1 |
+
----+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+
----+------+
3
rows
in
set
(0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
|
mysql> explain extended
select
*
from
t1
where
k1 < 10
and
(
-> exists(
select
k2
from
t2
where
t2.k2 < 5
and
(t2.c2 = 1
or
t2.c2 = 2))
-> );
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 |
PRIMARY
| t1 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | 100.00 | Using
where
|
| 2 | SUBQUERY | t2 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | 100.00 | Using
where
|
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+-------------+
2
rows
in
set
, 1 warning (0.00 sec)
|
1
2
3
4
5
6
7
8
9
|
mysql> explain
select
*
from
t1, (
select
*
from
t2
where
t2.k2 > 10) v_t2
where
t1.k1 < 10
and
v_t2.k2 < 20;
+
----+-------------+------------+-------+---------------+---------+---------+------+------+----------------------------------------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| Extra |
+
----+-------------+------------+-------+---------------+---------+---------+------+------+----------------------------------------------------+
| 1 |
PRIMARY
| t1 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 3 | Using
where
|
| 1 |
PRIMARY
| <derived2> |
ALL
|
NULL
|
NULL
|
NULL
|
NULL
| 2 | Using
where
; Using
join
buffer (Block Nested Loop) |
| 2 | DERIVED | t2 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 1 | Using
where
|
+
----+-------------+------------+-------+---------------+---------+---------+------+------+----------------------------------------------------+
3
rows
in
set
(0.00 sec)
|
1
2
3
4
5
6
7
8
|
mysql> explain extended
select
*
from
t1
where
t1.a1 < 100
and
t1.a1
in
(
select
a2
from
t2
where
t2.a2 > 10);
+
----+-------------+-------+--------+---------------+---------+---------+--------------+------+----------+-------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+--------+---------------+---------+---------+--------------+------+----------+-------------+
| 1 | SIMPLE | t1 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 88 | 100.00 | Using
where
|
| 1 | SIMPLE | t2 | eq_ref |
PRIMARY
|
PRIMARY
| 4 | testdb.t1.a1 | 1 | 100.00 | Using
index
|
+
----+-------------+-------+--------+---------------+---------+---------+--------------+------+----------+-------------+
2
rows
in
set
, 1 warning (0.32 sec)
|
四、MySQL可以支持什么格式的子查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import
MySQLdb as mdb
import
random
host
=
'10.12.128.12'
name
=
'root'
password
=
'123456'
db
=
'testdb'
try
:
conn
=
mdb.connect(host, name, password, db)
cur
=
conn.cursor()
for
i
in
range
(
1
,
4
):
sql
=
'create table t%d(a%d int primary key auto_increment, b%d int)'
%
(i, i, i)
cur.execute(sql)
for
j
in
range
(
1
,
15000
):
value
=
random.randint(
1
,
15000
)
s
=
'insert into t%d(b%d) values(%d)'
%
(i, i, value)
cur.execute(s);
cur.close()
finally
:
if
conn:
conn.close()
|
1
2
3
4
5
6
7
8
|
mysql> EXPLAIN EXTENDED
SELECT
*
FROM
t1
WHERE
t1.a1>(
SELECT
MIN
(t2.a2)
FROM
t2);
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+------------------------------+
| 1 |
PRIMARY
| t1 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 7534 | 100.00 | Using
where
|
| 2 | SUBQUERY |
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
Select
tables optimized away |
+
----+-------------+-------+-------+---------------+---------+---------+------+------+----------+------------------------------+
2
rows
in
set
, 1 warning (0.11 sec)
|
1
2
3
4
5
6
7
8
|
mysql> explain extended
select
*
from
t1
where
t1.a1
NOT
IN
(
select
a2
from
t2
where
t2.a2 > 10);
+
----+-------------+-------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
| 1 |
PRIMARY
| t1 |
ALL
|
NULL
|
NULL
|
NULL
|
NULL
| 15068 | 100.00 | Using
where
|
| 2 | SUBQUERY | t2 | range |
PRIMARY
|
PRIMARY
| 4 |
NULL
| 7534 | 100.00 | Using
where
; Using
index
|
+
----+-------------+-------+-------+---------------+---------+---------+------+-------+----------+--------------------------+
2
rows
in
set
, 1 warning (0.00 sec)
|
1
2
3
4
5
6
|
mysql> show warnings\G
*************************** 1. row ***************************
Level
: Note
Code: 1003
Message: /*
select
#1 */
select
`testdb`.`t1`.`a1`
AS
`a1`,`testdb`.`t1`.`b1`
AS
`b1`
from
`testdb`.`t1`
where
(
not
(<in_optimizer>(`testdb`.`t1`.`a1`,`testdb`.`t1`.`a1`
in
( <materialize> (/*
select
#2 */
select
`testdb`.`t2`.`a2`
from
`testdb`.`t2`
where
(`testdb`.`t2`.`a2` > 10) ), <primary_index_lookup>(`testdb`.`t1`.`a1`
in
<
temporary
table
>
on
<auto_key>
where
((`testdb`.`t1`.`a1` = `materialized-subquery`.`a2`)))))))
1 row
in
set
(0.00 sec)
|
1
2
3
4
5
6
7
8
|
mysql> explain extended
select
*
from
t1
where
t1.a1 >
ALL
(
select
a2
from
t2
where
t2.a2 > 10);
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
| 1 |
PRIMARY
| t1 |
ALL
|
NULL
|
NULL
|
NULL
|
NULL
| 15068 | 100.00 | Using
where
|
| 2 | SUBQUERY |
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
Select
tables optimized away |
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
2
rows
in
set
, 1 warning (0.03 sec)
|
1
2
3
4
5
6
|
mysql> show warnings\G
*************************** 1. row ***************************
Level
: Note
Code: 1003
Message: /*
select
#1 */
select
`testdb`.`t1`.`a1`
AS
`a1`,`testdb`.`t1`.`b1`
AS
`b1`
from
`testdb`.`t1`
where
<
not
>((`testdb`.`t1`.`a1` <= (/*
select
#2 */
select
max
(`testdb`.`t2`.`a2`)
from
`testdb`.`t2`
where
(`testdb`.`t2`.`a2` > 10))))
1 row
in
set
(0.00 sec)
|
1
2
3
4
5
6
7
8
|
mysql> explain extended
select
*
from
t1
where
t1.a1 >
SOME
(
select
a2
from
t2
where
t2.a2 > 10);
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
| id | select_type |
table
| type | possible_keys |
key
| key_len | ref |
rows
| filtered | Extra |
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
| 1 |
PRIMARY
| t1 |
ALL
|
NULL
|
NULL
|
NULL
|
NULL
| 15068 | 100.00 | Using
where
|
| 2 | SUBQUERY |
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
NULL
|
Select
tables optimized away |
+
----+-------------+-------+------+---------------+------+---------+------+-------+----------+------------------------------+
2
rows
in
set
, 1 warning (0.00 sec)
|
1
2
3
4
5
6
|
mysql> show warnings\G
*************************** 1. row ***************************
Level
: Note
Code: 1003
Message: /*
select
#1 */
select
`testdb`.`t1`.`a1`
AS
`a1`,`testdb`.`t1`.`b1`
AS
`b1`
from
`testdb`.`t1`
where
<nop>((`testdb`.`t1`.`a1` > (/*
select
#2 */
select
min
(`testdb`.`t2`.`a2`)
from
`testdb`.`t2`
where
(`testdb`.`t2`.`a2` > 10))))
1 row
in
set
(0.00 sec)
|
附:explain的用法
-
select_type:表示select类型,常见的取值有SIMPLE(不使用表连接或子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的或者后面的查询语句)、SUBQUERY(子查询中的第一个select)等。
-
table:输出结果集的表。
-
type:表示表的连接类型,性能由好到差的连接类型为system(表中仅有一行,即常量表)、const(单表中最多有一个匹配行,例如PRIMARY KEY或者UNIQUE INDEX)、eq_ref(对于前面的每一行,在此表中只查询一条记录,简单来说,就是多表连接中使用PRIMARYKEY或者UNIQUE INDEX)、ref(与eq_ref类似,区别在于不使用PRIMARYKEY或者UNIQUE INDEX,而是使用普通的索引)、ref_of_null(与ref类似,区别在于条件中包含对NULL的查询)、index_merge(索引合并化)、unique_subquery(in的后面是一个查询主键字段的子查询)、index_subquery(与unique_subquery类似,区别在于in的后面是查询非唯一索引字段的子查询)、range(单表中的范围查询)、index(对于前面的每一行都通过查询索引来得到数据)、all(对于前面的每一行的都通过全表扫描来获得数据)。
-
possible_keys:表示查询时,可能使用到的索引。
-
key:表示实际使用的索引
-
key_len:索引字段的长度
-
rows:扫描行的数量
-
extra:执行情况的说明和描述。