权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少。权限管理几乎出现在任何系统里面,只要有用户和密码的系统。很多人常将“用户身份认证”、“密码加密”、“系统管理”等概念与权限管理概念混淆。
一、常用
grant
权限分类
- 全局权限
- db权限
- 表权限
使用
- 新建一个sshuser用户,密码123456
create user 'sshuser'@'%' identified by '123456';
- 授予权限
- 全局权限(生产环境一般不推荐)
grant all privileges on *.* to 'sshuser'@'%' with grant option;
- db权限
grant all privileges on db1.* to 'sshuser'@'%' with grant option;
- 表权限
grant all privileges on db1.table_name to 'sshuser'@'%' with grant option;GRANT SELECT, INSERT ON db1.table_name TO 'sshuser'@'%' with grant option;
- 列权限
GRANT SELECT(id), INSERT (id,a) ON db1.table_name TO 'sshuser'@'%' with grant option;
- 全局权限(生产环境一般不推荐)
- 收回权限
revoke all privileges on *.* from 'sshuser'@'%';
- 新建一个sshuser用户,密码123456
group
in和exists
- 定义表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15CREATE TABLE ie_table_in (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
name char(20) NOT NULL DEFAULT '',
phone char(11) NOT NULL DEFAULT '',
created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
CREATE TABLE ie_table_exist (
id int(11) unsigned NOT NULL AUTO_INCREMENT,
name char(20) NOT NULL DEFAULT '',
phone char(11) NOT NULL DEFAULT '',
created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4- 定义存储过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15delimiter $$
create procedure init_in_exist()
begin
declare i int;
set i=0;
while(i<10000)do
insert into ie_table_in(name,phone) value(substring(md5(rand()),1,20),concat('1',ceiling(rand()*9000000000+1000000000)));
insert into ie_table_exist(name,phone) value(substring(md5(rand()),1,20),concat('1',ceiling(rand()*9000000000+1000000000)));
set i=i+1;
end while;
end$$
delimiter ;- 调用存储过程
call init_in_exist();
in
执行查询
1
2
3
4
5select * from ie_table_in t1
where t1.id in (
select id from ie_table_exist t2
where t1.id=t2.id
);查看执行计划
explain select * from ie_table_in t1 where t1.id in (select id from ie_table_exist t2 where t1.id=t2.id)\G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t2
partitions: NULL
type: index
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 9863
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: exec.t2.id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 2 warnings (0.00 sec)同样可开启profiling和optimizer_trace
- show profile all for query [ID];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307*************************** 1. row ***************************
Status: starting
Duration: 0.000111
CPU_user: 0.000101
CPU_system: 0.000010
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: NULL
Source_file: NULL
Source_line: NULL
*************************** 2. row ***************************
Status: checking permissions
Duration: 0.000011
CPU_user: 0.000007
CPU_system: 0.000004
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: check_access
Source_file: sql_authorization.cc
Source_line: 802
*************************** 3. row ***************************
Status: checking permissions
Duration: 0.000011
CPU_user: 0.000009
CPU_system: 0.000002
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: check_access
Source_file: sql_authorization.cc
Source_line: 802
*************************** 4. row ***************************
Status: checking permissions
Duration: 0.000005
CPU_user: 0.000003
CPU_system: 0.000002
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: check_access
Source_file: sql_authorization.cc
Source_line: 802
*************************** 5. row ***************************
Status: checking permissions
Duration: 0.000005
CPU_user: 0.000003
CPU_system: 0.000002
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: check_access
Source_file: sql_authorization.cc
Source_line: 802
*************************** 6. row ***************************
Status: Opening tables
Duration: 0.000027
CPU_user: 0.000025
CPU_system: 0.000001
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: open_tables
Source_file: sql_base.cc
Source_line: 5714
*************************** 7. row ***************************
Status: init
Duration: 0.000168
CPU_user: 0.000113
CPU_system: 0.000056
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 1
Page_faults_minor: 0
Swaps: 0
Source_function: handle_query
Source_file: sql_select.cc
Source_line: 121
*************************** 8. row ***************************
Status: System lock
Duration: 0.000015
CPU_user: 0.000012
CPU_system: 0.000003
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: mysql_lock_tables
Source_file: lock.cc
Source_line: 323
*************************** 9. row ***************************
Status: optimizing
Duration: 0.000036
CPU_user: 0.000033
CPU_system: 0.000002
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: optimize
Source_file: sql_optimizer.cc
Source_line: 151
*************************** 10. row ***************************
Status: statistics
Duration: 0.000113
CPU_user: 0.000111
CPU_system: 0.000002
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: optimize
Source_file: sql_optimizer.cc
Source_line: 367
*************************** 11. row ***************************
Status: preparing
Duration: 0.000034
CPU_user: 0.000031
CPU_system: 0.000003
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: optimize
Source_file: sql_optimizer.cc
Source_line: 475
*************************** 12. row ***************************
Status: executing
Duration: 0.000005
CPU_user: 0.000003
CPU_system: 0.000003
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: exec
Source_file: sql_executor.cc
Source_line: 119
*************************** 13. row ***************************
Status: Sending data
Duration: 0.019846
CPU_user: 0.019599
CPU_system: 0.000246
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 75
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: exec
Source_file: sql_executor.cc
Source_line: 195
*************************** 14. row ***************************
Status: end
Duration: 0.000011
CPU_user: 0.000004
CPU_system: 0.000007
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: handle_query
Source_file: sql_select.cc
Source_line: 199
*************************** 15. row ***************************
Status: query end
Duration: 0.000009
CPU_user: 0.000009
CPU_system: 0.000001
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: mysql_execute_command
Source_file: sql_parse.cc
Source_line: 4946
*************************** 16. row ***************************
Status: closing tables
Duration: 0.000011
CPU_user: 0.000010
CPU_system: 0.000001
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: mysql_execute_command
Source_file: sql_parse.cc
Source_line: 4998
*************************** 17. row ***************************
Status: freeing items
Duration: 0.000017
CPU_user: 0.000012
CPU_system: 0.000004
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 1
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: mysql_parse
Source_file: sql_parse.cc
Source_line: 5610
*************************** 18. row ***************************
Status: cleaning up
Duration: 0.000016
CPU_user: 0.000016
CPU_system: 0.000000
Context_voluntary: 0
Context_involuntary: 0
Block_ops_in: 0
Block_ops_out: 0
Messages_sent: 0
Messages_received: 0
Page_faults_major: 0
Page_faults_minor: 0
Swaps: 0
Source_function: dispatch_command
Source_file: sql_parse.cc
Source_line: 1924
18 rows in set, 1 warning (0.01 sec)SELECT TRACE INTO outfile '/Users/liuyulong/mysql_trace/in.log' FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
exists
执行查询
1
2
3
4
5select * from ie_table_in t1
where exists(
select id from ie_table_exist t2
where t1.id=t2.id
);查看执行计划
explain select * from ie_table_in t1 where exists(select id from ie_table_exist t2 where t1.id=t2.id)\G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: t1
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 9890
filtered: 100.00
Extra: Using where
*************************** 2. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: t2
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: exec.t1.id
rows: 1
filtered: 100.00
Extra: Using index
2 rows in set, 2 warnings (0.00 sec)
对比
show profiles\G
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16*************************** 6. row ***************************
Query_ID: 6
Duration: 0.01895000
Query: select * from ie_table_in t1
where t1.id in (
select id from ie_table_exist t2
where t1.id=t2.id
)
*************************** 7. row ***************************
Query_ID: 7
Duration: 0.05620200
Query: select * from ie_table_in t1
where exists(
select id from ie_table_exist t2
where t1.id=t2.id
)
在内外表数据量相同的情况下,发现in语句反而比exists快
in语句
1 | mysql> show profile all for query 6\G |
exists语句
1
大量的executing和Sending data
order
全字段排序
- 初始化表
1
2
3
4
5
6
7
8
9CREATE TABLE `t` (
`id` int(11) NOT NULL,
`city` varchar(16) NOT NULL,
`name` varchar(16) NOT NULL,
`age` int(11) NOT NULL,
`addr` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `city` (`city`)
) ENGINE=InnoDB;查询
select city,name,age from t where city='杭州' order by name limit 1000;
查看执行计划
explain select city,name,age from t where city='杭州' order by name limit 1000;
1
2
3
4
5
6
7
8
9
10
11
12id: 1
select_type: SIMPLE
table: table_order
partitions: NULL
type: ref
possible_keys: city
key: city
key_len: 50
ref: const
rows: 1
filtered: 100.00
Extra: Using index condition; Using filesort- Extra这个字段中的“Using filesort”表示的就是需要排序,MySQL会给每个线程分配一块内存用于排序,称为sort_buffer。
- 从图中可以看到,满足
city='杭州’
条件的行,是从ID_X到ID_(X+N)的这些记录。 - 通常情况下,这个语句执行流程如下所示 :
- 初始化sort_buffer,确定放入name、city、age这三个字段;
- 从索引city找到第一个满足city=’杭州’条件的主键id,也就是图中的ID_X;
- 到主键id索引取出整行,取name、city、age三个字段的值,存入sort_buffer中;
- 从索引city取下一个记录的主键id;
- 重复步骤3、4直到city的值不满足查询条件为止,对应的主键id也就是图中的 ID_Y;
- 对sort_buffer中的数据按照字段name做快速排序;
- 按照排序结果取前1000行返回给客户端。
我们暂且把这个排序过程,称为全字段排序,执行流程的示意图如下所示,下一篇文章中我们还会用到这个排序。
图中“按name排序”这个动作可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数sort_buffer_size。sort_buffer_size,就是MySQL为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于sort_buffer_size排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序,也称外部排序。在使用外部排序时,MySQL会分成好几份单独的临时文件用来存放排序后的数据,然后在将这些文件合并成一个大文件。
rowid排序
接下来修改一个参数,让MySQL采用另外一种算法
SET max_length_for_sort_data = 16;
max_length_for_sort_data
是MySQL中专门控制用于排序的行数据的长度的一个参数。它的意思是如果单行的长度超过这个值,MySQL就认为单行太大,要换一个算法。
- city、name、age这三个字段的定义总长度是36,把max_length_for_sort_data设置为16,我们再来看看计算过程有什么改变。
- 新的算法放入sort_buffer的字段,只有要排序的列(即name字段)和主键id。
- 但这时,排序的结果就因为少了city和age字段的值,不能直接返回了,整个执行流程就变成如下所示的样子:
- 初始化sort_buffer,确定放入两个字段,即name和id;
- 从索引city找到第一个满足city=’杭州’条件的主键id,也就是图中的ID_X;
- 到主键id索引取出整行,取name、id这两个字段,存入sort_buffer 中;
- 从索引city取下一个记录的主键 id;
- 重复步骤3、4直到不满足city=’杭州’条件为止,也就是图中的ID_Y;
- 对sort_buffer中的数据按照字段name 行排序;
- 遍历排序结果,取前1000行,并按照id的值回到原表中取出city、name和age三个字段返回给客户端。
优化
- 创建联合索引减少排序
- 使用覆盖索引减少排序
optimize table
analyze table
alter table
explain
引入
EXPLAIN
显示了MySQL
如何使用索引来处理SELECT
语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句。EXPLAIN
命令用法十分简单, 在SQL语句前加上Explain
就可以了,如:EXPLAIN SELECT * FROM 数据库.表;
详解
EXPLAIN SELECT * FROM users;\G
结果如下:1
2
3
4
5
6
7
8
9
10
11
12id: 1
select_type: SIMPLE
table: users
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
filtered: 100.00
Extra: NULL
字段 | 说明 |
---|---|
id | 查询序列号(值相同时默认由上而下执行,不同时值越大代表优先级越高,则越先被执行) |
select_type | 查询类型,详情见下表 |
table | 查询的表 |
partitions | 匹配的分区 |
type | 联接类型 |
possible_keys | 可能选用的索引,如果该列是NULL,则没有相关的索引,这种情况下可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高查询性能 |
key | 实际使用的索引 |
key_len | MySQL决定使用的键长度,如果键是NULL,则长度为NULL |
ref | 哪个字段或常数与key一起被使用 |
rows | 扫描了多少行 |
filtered | 通过条件过滤出的行数的百分比估计值 |
Extra | 额外的信息 |
id
- 值为NULL时说明这一行数据是由另外两个SQL语句进行 UNION操作后产生的结果集
- 值相同时说明SQL执行顺序是按照显示的从上至下执行的
- 值不同时,值越大代表优先级越高,越先被执行
select_type
查询类型 | 说明 |
---|---|
SIMPLE | 简单SELECT(不使用UNION或子查询) |
PRIMARY | 最外面的SELECT,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY |
UNION | UNION中的第二个或后面的SELECT语句 |
DEPENDENT UNION | UNION中的第二个或后面的SELECT语句,取决于外面的查询 |
UNION RESULT | UNION 的结果 |
SUBQUERY | 子查询中的第一个SELECT |
DEPENDENT SUBQUERY | 子查询中的第一个SELECT,取决于外面的查询 |
DERIVED | 派生表的SELECT, FROM子句的子查询 |
UNCACHEABLE SUBQUERY | 一个子查询的结果不能被缓存,必须重新评估外链接的第一行 |
- type:从上到下性能最佳到最坏
类型 | 说明 |
---|---|
system |
表中只有一条数据,这个类型是特殊的const类型 |
const |
针对主键或唯一索引的等值查询扫描,最多只返回一行数据 |
eq_ref |
通常出现在多表的join查询,表示对于前表的每一个结果,都只能匹配到后表的一行结果,并且查询的比较操作通常是=,查询效率较高 |
ref |
通常出现在多表的join查询,针对于非唯一或非主键索引,或者是使用了最左前缀规则索引的查询 |
ref_or_null |
该联接类型如同ref,但是添加了MySQL可以专门搜索包含NULL值的行 |
index_merge |
该联接类型表示使用了索引合并优化方法 |
unique_subquery |
该类型替换了下面形式的IN子查询的ref:value IN (SELECT primary_key FROM single_table WHERE some_expr) unique_subquery是一个索引查找函数,可以完全替换子查询,效率更高 |
index_subquery |
该联接类型类似于unique_subquery。可以替换IN子查询,但只适合下列形式的子查询中的非唯一索引: value IN (SELECT key_column FROM single_table WHERE some_expr) |
range |
表示使用索引范围查询,通过索引字段范围获取表中部分数据记录。这个类型通常出现在 =,<>,>,>=,<,<=,IS NULL,<=>,BETWEEN,IN() 操作中。当type是range时,那么EXPLAIN输出的ref字段为NULL,并且key_len字段是此次查询使用到的索引的最长的那个 |
index |
表示全索引扫描(full index scan),和ALL类型类似,只不过ALL类型是全表扫描,而index类型则仅仅扫描所有的索引而不扫描数据。index类型通常出现在覆盖索引的情况。 |
ALL |
表示全表扫描,这个类型的查询是性能最差的查询之一 |
- extra: 查询的额外信息
类型 | 说明 |
---|---|
Distinct | MySQL发现第1个匹配行后,停止为当前的行组合搜索更多的行。 |
Not exists | MySQL能够对查询进行LEFT JOIN优化,发现1个匹配LEFT JOIN标准的行后,不再为前面的的行组合在该表内检查更多的行。 |
range checked for each record (index map: #) | MySQL没有发现好的可以使用的索引,但发现如果来自前面的表的列值已知,可能部分索引可以使用 |
Using filesort | MySQL需要额外的一次传递,以找出如何按排序顺序检索行。 |
Using index | 覆盖索引 |
Using where | 服务层使用WHERE条件来过滤数据 |
Using index condition | 5.6+引入的新特性索引条件下推(index condition pushdown) |
Using temporary | 为了解决查询,MySQL需要创建一个临时表来容纳结果。 |
Using sort_union(…), Using union(…), Using intersect(…) | 这些函数说明如何为index_merge联接类型合并索引扫描。 |
Using index for group-by | 类似于访问表的Using index方式,Using index for group-by表示MySQL发现了一个索引,可以用来查 询GROUP BY或DISTINCT查询的所有列,而不要额外搜索硬盘访问实际的表。 |
using index condition
会先过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用WHERE子句中的其他条件去过滤这些数据行
- 用不到索引
- 数据类型出现隐式转换
- 复合索引的情况下,查询条件不满足索引最左的原则
- Mysql估计使用索引比全表扫描慢
- 用or分割开的条件,or前条件有索引,or后的列没有索引
- 负向查询(not,not in,not like,<>,!=,!>,!<)不会使用索引
- 独立的列索引不能是表达式的一部分
join
- 构造数据
- 技术
- MRR
- BKA
- BNL转BKA
- 在业务上优化
- 构造数据
union
- 建表
1
2
3
4
5
6
7
8
9
10
11create table union_one(
id int(11) unsigned not null primary key auto_increment,
name varchar(100) not null default '',
created datetime not null default current_timestamp
);
create table union_two(
id int(11) unsigned not null primary key auto_increment,
name varchar(100) not null default '',
created datetime not null default current_timestamp
);- 初始化数据
1
2
3
4
5
6
7
8insert into union_one(`name`) values ('tom');
insert into union_one(`name`) values ('tim');
insert into union_one(`name`) values ('jack');
insert into union_two(`name`) values ('tom');
insert into union_two(`name`) values ('tim');
insert into union_two(`name`) values ('jack');
insert into union_two(`name`) values ('john');- 查询
- union
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55select * from union_one union select * from union_two;
+----+------+---------------------+
| id | name | created |
+----+------+---------------------+
| 1 | tom | 2019-11-05 17:40:15 |
| 2 | tim | 2019-11-05 17:40:15 |
| 3 | jack | 2019-11-05 17:40:15 |
| 4 | john | 2019-11-05 17:40:15 |
+----+------+---------------------+
4 rows in set (0.00 sec)
explain select * from union_one union select * from union_two\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: union_one
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: UNION
table: union_two
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
filtered: 100.00
Extra: NULL
*************************** 3. row ***************************
id: NULL
select_type: UNION RESULT
table: <union1,2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: Using temporary
3 rows in set, 1 warning (0.00 sec)- union all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44select * from union_one union all select * from union_two;
+----+------+---------------------+
| id | name | created |
+----+------+---------------------+
| 1 | tom | 2019-11-05 17:40:15 |
| 2 | tim | 2019-11-05 17:40:15 |
| 3 | jack | 2019-11-05 17:40:15 |
| 1 | tom | 2019-11-05 17:40:15 |
| 2 | tim | 2019-11-05 17:40:15 |
| 3 | jack | 2019-11-05 17:40:15 |
| 4 | john | 2019-11-05 17:40:15 |
+----+------+---------------------+
7 rows in set (0.00 sec)
explain select * from union_one union all select * from union_two\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: union_one
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: UNION
table: union_two
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)- 对比
- union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序,效率低
- union all:对两个结果集进行并集操作,包括重复行,不进行排序,效率高
group by与sql_mode
- 建表并初始化记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`phone` char(11) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `student` VALUES (1, '张三', '18812345678');
INSERT INTO `student` VALUES (2, '李四', '18856781234');
INSERT INTO `student` VALUES (3, '王五', '18811111111');
INSERT INTO `student` VALUES (4, '马六', '18811112222');
CREATE TABLE `score` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sid` int(11) NOT NULL,
`score` float(4,2) NOT NULL,
`subject` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
INSERT INTO `score` VALUES (1, 1, 80.00, '语文');
INSERT INTO `score` VALUES (2, 1, 85.00, '数学');
INSERT INTO `score` VALUES (3, 1, 89.00, '英语');
INSERT INTO `score` VALUES (4, 2, 50.00, '语文');
INSERT INTO `score` VALUES (5, 2, 30.00, '数学');
INSERT INTO `score` VALUES (6, 2, 90.00, '英语');
INSERT INTO `score` VALUES (7, 3, 60.00, '语文');
INSERT INTO `score` VALUES (8, 3, 30.00, '数学');
INSERT INTO `score` VALUES (9, 3, 79.00, '英语');
INSERT INTO `score` VALUES (10, 4, 70.00, '语文');
INSERT INTO `score` VALUES (11, 4, 35.00, '数学');
INSERT INTO `score` VALUES (12, 4, 95.00, '英语');- 查询总分排名前三的记录
1
2
3
4
5select a.id,a.name,sum(b.score) total_score from score b
join student a on a.id=b.sid
group by b.sid
order by total_score desc
limit 0,3
注
尝试以student表为主表关联查询
1
2
3
4
5select a.id,a.name,sum(b.score) total_score from student a
left join score b on a.id=b.sid
group by b.sid
order by total_score desc
limit 0,3报错:
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'xiaohe.a.id'; this is incompatible with sql_mode=only_full_group_by
在MySQL5.7.5后,默认开启了ONLY_FULL_GROUP_BY,所以导致了之前的一些SQL无法正常执行。对于GROUP BY聚合操作,如果在SELECT中的列没有在GROUP BY中出现,那么这个SQL是不合法的。传送门
sql_mode介绍
- ONLY_FULL_GROUP_BY:在该模式下,target list中的值要么是来自于聚集函数的结果,要么是来自于group by list中的表达式的值。
- STRICT_TRANS_TABLES:在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做限制。
- NO_ZERO_IN_DATE:在严格模式下,不允许日期和月份为零。
- NO_ZERO_DATE:设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告。
- ERROR_FOR_DIVISION_BY_ZERO:在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如果未给出该模式,那么数据被零除时MySQL返回NULL。
- NO_AUTO_CREATE_USER:禁止GRANT创建密码为空的用户。
- NO_ENGINE_SUBSTITUTION:如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常。
- PIPES_AS_CONCAT:将”||”视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似。
- ANSI_QUOTES:启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符。
- NO_AUTO_VALUE_ON_ZERO:该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户 希望插入的值为0,而该列又是自增长的,那么这个选项就有用了。
解决方案
方案一、修改配置文件
my.cnf
,文件最后加入以下设置,重启mysql
,永久有效sql_mode = "STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER"
方案二、修改当前会话配置,mysql重启后失效
查看当前模式
1
2
3show variables like '%sql_mode';
show session variables like '%sql_mode%';
show global variables like '%sql_mode%';修改当前模式
1
2set global sql_mode='';
set session sql_mode='';