本文链接
http://alex-my.xyz/books/database/MySQL必知必会
http://blog.csdn.net/alex_my/article/details/72357498
1 基础知识
1 主键
- 唯一标识表中每行的这个列(这组列)称为主键。
- 应该总是定义主键,虽然并不总是需要主键。
- 任意两行都不具有相同的主键值。
- 每一行都必须具有一个主键值,不可为NULL。
2 常用命令
- SHOW DATABASES;
- SHOW TABLES;
- SHOW COLUMNS FROM table;
- SHOW STATUS;
- SHOW CREATE DATABASE database;
- SHOW CREATE TABLE table;
2 检索数据
除非确实需要绝大部分列或全部列,否则最好不使用通配符* 来获取所有的列。检索不需要的列通常会降低搜索和应用程序的性能。
1 DISTINCT和GROUP BY
SELECT DISTINCT `role_id` FROM user;
SELECT `role_id` FROM user GROUP BY `role_id`;
- 二者都可以达到去重的效果。
- DISTINCT把列中的全部内容存储到内存中,可以理解为一个hash,最后的到hash中的key就可以得到结果。比较耗内存。
- GROUP BY先将列排序,然后去重。排序比较耗时间。
2 LIMIT
从0开始算
SELECT `role_id` FROM user LIMIT 5 OFFSET 3;
SELECT `role_id` FROM user LIMIT 3, 5;
以上两条命令都表示从3开始的5行。
3 排序数据
默认为升序 ASC
。
SELECT `role_id`, `account_id` FROM user_pay ORDER BY `account_id`, `role_id` DESC LIMIT 20;
以上语句中, account_id默认为升序排列,也可以写上ASC。
4 过滤数据
1 IN操作符
SELECT * FROM user WHERE role_id IN (1000001, 1000002);
2 NOT操作符
SELECT * FROM user WHERE role_id IN (1000001, 1000002) LIMIT 10;
3 LIKE操作符
SELECT * FROM user WHERE name LIKE 't%';
SELECT * FROM user WHERE name LIKE 't_';
SELECT * FROM user WHERE name LIKE 't__';
- 以上两句都是模糊匹配用户名以t开头。
- %: 匹配任意0个或者多个字符。
- _: 一个_匹配1个任意字符,且必须有一个。
5 正则表达式搜索
MYSQL仅支持多数正则表达式实现的一个很小的子集。
1 基本字符匹配
SELECT name FROM user WHERE name REGEXP 't';
SELECT name FROM user WHERE name REGEXP 't.';
-
.
表示匹配任意一个字符。
- LIKE和REGEXP区别:
- LIKE 要求整个列匹配(使用通配符除外), REGEXP只要列中某个片段匹配即可。
- 假设有用户名为s123。则以下例子中, LIKE没有得到结果。
SELECT name FROM user WHERE name LIKE 's1';
SELECT name FROM user WHERE name REGEXP 's1';
2 OR匹配
SELECT name FROM user WHERE name REGEXP 's1|s2' ORDER BY name;
使用|功能上类似于SELECT中的OR语句。多个OR语句可以使用正则表达式替代,更简洁。
3 匹配几个字符之一
SELECT name FROM user WHERE name REGEXP 's[1238]' ORDER BY name;
相当于
SELECT name FROM user WHERE name REGEXP 's1|s2|s3|s8' ORDER BY name;
也可以添加^,来匹配除指定以外的内容
SELECT name FROM user WHERE name REGEXP 's[^1238]' ORDER BY name;
4 匹配范围
SELECT name FROM user WHERE name REGEXP 's[1-8]' ORDER BY name;
SELECT name FROM user WHERE name REGEXP '[a-z][1-8]' ORDER BY name;
5 匹配特殊字符
为了匹配特殊字符,必须用\\
为前导。
SELECT name FROM user WHERE name REGEXP 's\\-' ORDER BY name;
\\
也用来引用具有特殊含义的字符
特殊字符 |
含义 |
\\f |
换页 |
\\n |
换行 |
\\r |
回车 |
\\t |
制表 |
\\v |
纵向制表 |
多数正则表达式使用\
转义特殊字符,以便能使用这些字符本身。但MySQL要求用\\
。
MySQL解释一个,正则表达式解释另外一个。
6 匹配字符类
为了方便工作,可以使用预定义的字符集
类 |
说明 |
[:alnum:] |
任意字母和数字 ([a-zA-Z0-9]) |
[:alpha:] |
任意字符 ([a-zA-Z]) |
[:blank:] |
空格和指标 (\\t) |
[:cntrl:] |
ASCII控制字符 (ASCII 0~31, 127) |
[:digit:] |
任意数字 ([0-9]) |
[:graph:] |
与[:print:]相同,但不包括空格 |
[:lower:] |
任意小写字母 ([a-z]) |
[:print:] |
任意可打印的数字 |
[:punct:] |
同时不在[:alnum:][:cntrl:]中的任意字符 |
[:space:] |
包括空格在内的任意空白字符 ([\\t\\n\\r\\t\\v]) |
[:upper:] |
任意大写字母 ([A-Z]) |
[:xdigit:] |
任意十六进制数字 ([a-fA-F0-9]) |
示例:
SELECT name FROM user WHERE name REGEXP '[[:alpha:]]1' ORDER BY name;
7 匹配多个实例
字符 |
说明 |
* |
0个或多个匹配 |
+ |
1个或多个匹配 ({1, }) |
? |
0个或1个匹配 ({0, 1}) |
{n} |
指定数目的匹配 |
{n, } |
不少于指定数目的匹配 |
{n, m} |
匹配数目范围, m 不超过255 |
SELECT name FROM user WHERE name REGEXP '[[:digit:]]{4}' ORDER BY name;
8 定位符
元字符 |
说明 |
^ |
文本的开始 |
$ |
文本的结尾 |
[[:<:]] |
词的开始 |
[[:>:]] |
词的结尾 |
* 示例1
假设要找到以字母开头的用户名
SELECT name FROM user WHERE name REGEXP '[a-zA-Z]';
以上语句将会在文本任意位置进行查找匹配,并不符合以字母开头这依规定. 这里可以使用^
SELECT name FROM user WHERE name REGEXP '^[a-zA-Z]';
6 创建计算字段
存储在表中的数据不一定是应用程序所需要的。我们可以直接从数据库中检索出转换,计算或格式化过的数据。而不是检索出原始数据然后在应用程序中重新格式化。
1 拼接 CONCAT
表中含有role_id, name字段,应用程序需要这样的格式 role_name(role_id)
SELECT CONCAT(name, '(', role_id, ')') FROM user LIMIT 1;
SELECT CONCAT(RTRIM(name), '(', role_id, ')') FROM user LIMIT 1;
- RTRIM()函数去掉了值右边的所有空格。其余有LTRIM(), TRIM()
2 别名 AS
拼接处的结果没有名字,应用程序没法引用。可以使用别名解决这个问题。
SELECT CONCAT(name, '(', role_id, ')') AS info FROM user LIMIT 1;
这样,应用程序就可以使用info这个列,就像它本来就存在于表中一样。
3 执行算术计算
假设用户充值了money(元),每元可以换成10个代币,这里通过计算直接得出获得的总代币。
SELECT role_id, money, money * 10 AS total_gold FROM user LIMIT 10;
7 使用数据处理函数
1 字符串函数
函数 |
说明 |
示例 |
结果 |
CHAR_LENGTH(S) |
返回字符串s字符数 |
SELECT CHAR_LENGTH(‘abc 你好’); |
6 |
LENGTH(S) |
返回字符串s的长度 |
SELECT LENGTH(‘abc 你好’); |
10 |
CONCAT(S1,S2,…) |
合并为一个字符串 |
SELECT CONCAT(‘hello’, ’ abc’); |
hello abc |
CONCAT_WS(x, s1, s2,…) |
同CONCAT,但会加上x |
SELECT CONCAT_WS(‘+’, ‘1’, ‘2’, ‘3’); |
1+2+3 |
INSERT(s1, x, length, s2) |
将字符串s2替换s1的x位置开始长度为length的字符串 |
SELECT INSERT(‘abcdefg’, 2, 3, ‘123’); |
a123efg |
UPPER(s) |
将字符串s的所有字母变成大写字母 |
SELECT UPPER(‘abcd’); |
ABCD |
LOWER(s) |
将字符串s的所有字母变成小写字母 |
SELECT LOWER(‘ABCD’); |
abcd |
LEFT(s, n) |
返回字符串s的前n个字符 |
SELECT LEFT(‘abcdef’, 3); |
abc |
RIGHT(s, n) |
返回字符串s的后n个字符 |
SELECT RIGHT(‘abcdef’, 3); |
def |
LPAD(s1, length, s2) |
字符串s2来填充s1的开始处,使字符串长度达到length |
SELECT LPAD(‘abc’, 8, ‘123’); |
12312abc |
RPAD(s1, length, s2) |
字符串s2来填充s1的结尾处,使字符串的长度达到length |
SELECT RPAD(‘abc’, 8, ‘123’); |
abc12312 |
LTRIM(s) |
去掉字符串s开始处的空格 |
SELECT LTRIM(’ abc ‘); |
‘abc ‘ |
RTRIM(s) |
去掉字符串s结尾处的空格 |
SELECT RTRIM(’ abc ‘); |
’ abc’ |
TRIM(s) |
去掉字符串s开始和结尾处的空格 |
SELECT TRIM(’ abc ‘); |
‘abc’ |
TRIM(s1 FROM s) |
去掉字符串s中开始处和结尾处的字符串s1 |
SELECT TRIM(‘-’ FROM ‘—hello–’); |
hello |
REPEAT(s, n) |
将字符串s重复n次 |
SELECT REPEAT(‘abc’, 3); |
abcabcabc |
SPACE(n) |
返回n个空格 |
SELECT SPACE(3); |
‘ ’ |
REPLACE(s, s1, s2) |
将字符串s2替代字符串s中的字符串s1 |
SELECT REPLACE(‘abcdef’, ‘abc’, ‘12’); |
12def |
STRCMP(s1, s2) |
比较字符串s1和s2 |
SELECT STRCMP(‘abc’, ‘abc’); |
0 |
STRCMP(s1, s2) |
比较字符串s1和s2 |
SELECT STRCMP(‘abc’, ‘abcd’); |
-1 |
STRCMP(s1, s2) |
比较字符串s1和s2 |
SELECT STRCMP(‘abc’, ‘ab’); |
1 |
SUBSTRING(s, n, length) |
获取从字符串s中的第n个位置开始长度为length的字符串 |
SELECT SUBSTRING(‘abcdefg’, 2, 3); |
bcd |
MID(s, n, length) |
同SUBSTRING |
SELECT MID(‘abcdefg’, 3, 2); |
cd |
LOCATE(s1, s) |
从字符串s中获取s1的开始位置 |
SELECT LOCATE(‘de’, ‘abcdefg’); |
4 |
POSITION(s1, s) |
从字符串s中获取s1的开始位置 |
SELECT POSITION(‘de’ IN ‘abcdefg’); |
4 |
INSTR(s, s1) |
从字符串s中获取s1的开始位置 |
SELECT INSTR(‘abcdefg’, ‘de’); |
4 |
REVERSE(s) |
将字符串s的顺序反过来 |
SELECT REVERSE(‘a,b,c,d,e,f’); |
f,e,d,c,b,a |
ELT(n, s1, s2, …) |
返回第n个字符串 |
SELECT ELT(3, ‘abc’, ‘def’, ‘ghi’, ‘jkl’); |
ghi |
EXPORT_SET(…) |
见示例 |
SELECT EXPORT_SET(6, ‘y’, ‘n’, ‘_’, 3); |
n_y_y |
FIELD(s, s1, s2, …) |
返回第一个与字符串s匹配的字符串位置 |
SELECT FIELD(‘b’, ‘a’, ‘b’, ‘c’); |
2 |
FIND_IN_SET(str, str_list) |
见示例 |
SELECT FIND_IN_SET(‘4’, ‘6,5,4,3,2,1’); |
3 |
2 数学函数
函数 |
说明 |
示例 |
结果 |
ABS(x) |
返回x的绝对值 |
|
|
CEIL(x) |
返回大于或等于x的最小整数 |
|
|
CEILING(x) |
返回大于或等于x的最小整数 |
|
|
FLOOR(x) |
返回小于或等于x的最大整数 |
|
|
RAND() |
返回0->1的随机数 |
|
|
RAND(x) |
返回0->1的随机数,x值相同时返回的随机数相同 |
|
|
SIGN(x) |
返回x的符号,x是负数、0、正数分别返回-1、0和1 |
|
|
PI() |
返回圆周率(3.141593) |
|
|
TRUNCATE(x, y) |
返回数值x保留到小数点后y位的值(不会四舍五入) |
|
|
ROUND(x) |
返回离x最近的整数 |
|
|
ROUND(x, y) |
保留x小数点后y位的值(四舍五入) |
|
|
POW(x, y) |
返回x的y次方 |
|
|
POWER(x, y) |
返回x的y次方 |
|
|
SQRT(x) |
返回x的平方根 |
|
|
EXP(x) |
返回e的x次方 |
|
|
MOD(x, y) |
返回x除以y以后的余数 |
|
|
LOG(x) |
返回自然对数(以e为底的对数) |
|
|
LOG10(x) |
返回以10为底的对数 |
|
|
RADIANS(x) |
将角度转换为弧度 |
|
|
DEGREES(x) |
将弧度转换为角度 |
|
|
SIN(x) |
求正弦值(参数是弧度) |
|
|
ASIN(x) |
求反正弦值(参数是弧度) |
|
|
COS(x) |
求余弦值(参数是弧度) |
|
|
ACOS(x) |
求反余弦值(参数是弧度) |
|
|
TAN(x) |
求正切值(参数是弧度) |
|
|
ATAN(), ATAN2() |
求反正切值(参数是弧度) |
|
|
COT() |
求余切值(参数是弧度) |
|
|
3 日期时间函数
函数 |
说明 |
示例 |
结果 |
CURDATE(), CURRENT_DATE() |
返回当前日期 |
SELECT CURRENT_DATE(); |
2017-05-11 |
CURTIME(), CURRENT_TIME |
返回当前时间 |
SELECT CURRENT_TIME(); |
19:01:11 |
NOW() |
返回当前日期和时间 |
SELECT NOW(); |
2017-05-11 19:01:30 |
CURRENT_TIMESTAMP() |
返回当前日期和时间 |
同上 |
|
LOCALTIME() |
返回当前日期和时间 |
同上 |
|
SYSDATE() |
返回当前日期和时间 |
同上 |
|
LOCALTIMESTAMP() |
返回当前日期和时间 |
同上 |
|
UNIX_TIMESTAMP() |
以UNIX时间戳的形式返回当前时间 |
SELECT UNIX_TIMESTAMP(); |
1494500521 |
UNIX_TIMESTAMP(d) |
将时间d以UNIX时间戳的形式返回 |
SELECT UNIX_TIMESTAMP(‘2017-05-11 19:02:01’); |
1494500521 |
FROM_UNIXTIME(d) |
将UNIX时间戳的时间转换为普通格式的时间 |
SELECT FROM_UNIXTIME(1494500521); |
2017-05-11 19:02:01 |
UTC_DATE() |
返回UTC日期 |
SELECT UTC_DATE(); |
2017-05-11 |
UTC_TIME() |
返回UTC时间 |
SELECT UTC_TIME(); |
11:06:13 |
MONTH(d) |
返回日期d中的月份值,1->12 |
SELECT MONTH(‘2017-05-11’); |
5 |
MONTHNAME(d) |
返回日期当中的月份名称 |
SELECT MONTHNAME(‘2017-05-11’); |
May |
DAYNAME(d) |
返回日期d是星期几 |
SELECT DAYNAME(‘2017-05-11 19:07:12’); |
Thursday |
DAYOFWEEK(d) |
日期d今天是星期几,1星期日,2星期一 |
SELECT DAYOFWEEK(‘2017-05-11’); |
5 |
WEEKDAY(d) |
日期d今天是星期几,0表示星期一,1表示星期二 |
SELECT WEEKDAY(‘2017-05-11’); |
3 |
WEEK(d),WEEKOFYEAR(d) |
计算日期d是本年的第几个星期,范围是0->53 |
SELECT WEEK(‘2017-05-11’); |
19 |
DAYOFYEAR(d) |
计算日期d是本年的第几天 |
SELECT DAYOFYEAR(‘2017-05-11’); |
131 |
DAYOFMONTH(d) |
计算日期d是本月的第几天 |
SELECT DAYOFMONTH(‘2017-05-11’); |
11 |
QUARTER(d) |
返回日期d是第几季节,返回1->4 |
SELECT QUARTER(‘2017-05-11’); |
2 |
HOUR(t) |
返回t中的小时值 |
SELECT HOUR(‘2017-05-11 19:11:23’); |
19 |
MINUTE(t) |
返回t中的分钟值 |
SELECT MINUTE(‘2017-05-11 19:11:23’); |
11 |
SECOND(t) |
返回t中的秒钟值 |
SELECT SECOND(‘2017-05-11 19:11:23’); |
23 |
EXTRACT(type FROM d) |
从日期d中获取指定的值,type指定返回的值(见下文) |
SELECT EXTRACT(WEEK FROM ‘2017-05-11 19:11:23’); |
19 |
TIME_TO_SEC(t) |
将时间t转换为秒 |
SELECT TIME_TO_SEC(‘19:11:23’); |
69083 |
SEC_TO_TIME(s) |
将以秒为单位的时间s转换为时分秒的格式 |
SELECT SEC_TO_TIME(69083); |
19:11:23 |
TO_DAYS(d) |
计算日期d距离0000年1月1日的天数 |
SELECT TO_DAYS(‘2017-05-11 19:11:23’); |
736825 |
FROM_DAYS(n) |
计算从0000年1月1日开始n天后的日期 |
SELECT FROM_DAYS(736825); |
2017-05-11 |
DATEDIFF(d1,d2) |
计算日期d1->d2之间相隔的天数 |
SELECT DATEDIFF(‘2017-05-11’, ‘2017-05-12’); |
-1 |
ADDDATE(d,n) |
计算其实日期d加上n天的日期 |
SELECT ADDDATE(‘2017-05-11 19:11:23’, 3); |
2017-05-14 19:11:23 |
ADDDATE(d,INTERVAL expr type) |
计算起始日期d加上一个时间段后的日期 |
SELECT ADDDATE(‘2017-05-11 19:11:23’, INTERVAL 3 HOUR); |
2017-05-11 22:11:23 |
DATE_ADD(d,INTERVAL expr type) |
同上 |
SELECT DATE_ADD(‘2017-05-11 19:11:23’, INTERVAL 10 HOUR); |
2017-05-12 05:11:23 |
SUBDATE(d,n) |
日期d减去n天后的日期 |
SELECT SUBDATE(‘2017-05-12 05:11:23’, 13); |
2017-04-29 05:11:23 |
SUBDATE(d,INTERVAL expr type) |
日期d减去一个时间段后的日期 |
SELECT SUBDATE(‘2017-04-29 05:11:23’, INTERVAL 10 MINUTE); |
2017-04-29 05:01:23 |
ADDTIME(t,n) |
时间t加上n秒的时间 |
SELECT ADDTIME(‘2017-04-29 05:01:23’, 30); |
2017-04-29 05:01:53 |
SUBTIME(t,n) |
时间t减去n秒的时间 |
SELECT SUBTIME(‘2017-04-29 05:01:53’, 30); |
2017-04-29 05:01:23 |
DATE_FORMAT(d,f) |
按表达式f的要求显示日期d |
SELECT DATE_FORMAT(‘2017-04-29 05:01:23’, ‘%Y-%m-%d’); |
2017-04-29 |
TIME_FORMAT(t,f) |
按表达式f的要求显示时间t |
SELECT TIME_FORMAT(‘2017-04-29 05:01:23’, ‘%r’); |
05:01:23 AM |
* type的值可以为:
MICROSECOND
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
SECOND_MICROSECOND
MINUTE_MICROSECOND
MINUTE_SECOND
HOUR_MICROSECOND
HOUR_SECOND
HOUR_MINUTE
DAY_MICROSECOND
DAY_SECOND
DAY_MINUTE
DAY_HOUR
YEAR_MONTH
4 条件判断函数
-
IF (expr, v1, v2);
SELECT IF (1 > 0, 'Y', 'N');
-
IFNULL(v1, v2);
如果v1不为NULL, 返回v1,否则返回v2
SELECT IFNULL('a', 'b');
5 系统信息函数
函数 |
说明 |
示例 |
结果 |
VERSION() |
返回数据库的版本号 |
SELECT VERSION(); |
5.7.11 |
CONNECTION_ID() |
返回服务器的连接数 |
SELECT CONNECTION_ID(); |
13 |
DATABASE() |
返回当前数据库名 |
SELECT DATABASE(); |
database-learn |
|
|
|
|
USER() |
返回当前用户 |
SELECT USER(); |
root@localhost |
CHARSET(s) |
返回字符串s的字符集 |
SELECT CHARSET(“123”); |
utf8 |
COLLATION(s) |
返回字符串s的字符排列方式 |
SELECT COLLATION(“a123”); |
utf8_general_ci |
LAST_INSERT_ID() |
返回最近生成的AUTO_INCREMENT值 |
SELECT LAST_INSERT_ID(); |
0 |
8 分组数据
1 数据分组
假设要获取用户的充值次数,最低充值额度,最高充值额度,平均充值额度,可以用以下命令:
SELECT role_id, COUNT(*) AS num, MIN(money) as min_money, MAX(money) as max_money, AVG(money) AS avg_money FROM user_pay;
以上得出的是总的信息,如果要获取每个用户的这些信息,就可以使用分组了。
SELECT role_id, COUNT(*) AS num, MIN(money) as min_money, MAX(money) as max_money, AVG(money) AS avg_money FROM user_pay GROUP BY role_id ORDER BY num;
以上按照每个用户来计算结果。
- 需要注意的是,GROUP BY 必须出现在 WHERE 之后,ORDER BY 之前
-
可以使用WITH ROLLUP得到汇总的值
SELECT role_id, COUNT(*) AS num, MIN(money) as min_money, MAX(money) as max_money, AVG(money) AS avg_money FROM user_pay GROUP BY role_id WITH ROLLUP;
以上在在结果的最后,会附上总的结果。
2 分组过滤
假设只需要得到充值2次(包含)以上用户的数据,则需要使用HAVING来过滤。
SELECT role_id, COUNT(*) AS num, MIN(money) as min_money, MAX(money) as max_money, AVG(money) AS avg_money FROM user_pay GROUP BY role_id HAVING num >= 2 ORDER BY num;
- 注意HAVING跟GROUP BY后面。
-
也可以同时使用WHERE和HAVING。
SELECT role_id, COUNT(*) AS num, MIN(money) as min_money, MAX(money) as max_money, AVG(money) AS avg_money FROM user_pay WHERE time >= 1483200000 GROUP BY role_id HAVING num >= 2 ORDER BY num;
以上通过WHERE新增了条件,2017年以来充值的。
-
当sql_mode为ONLY_FULL_GROUP_BY需要注意
-
查看sql_mode值
SELECT @@sql_mode;
结果:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
-
在sql_mode=ONLY_FULL_GROUP_BY的模式下,以下句子报错
SELECT role_id, money FROM user_pay GROUP BY role_id;
错误: SELECT list is not in GROUP BY clause and contains nonaggregated column ...
表中的列
,出现在SELECT中时,也得出现在GROUP BY中。
SELECT role_id, money FROM user_pay GROUP BY role_id, money;
同样,ORDER BY也需要注意这个问题。
3 SELECT字句顺序
SELECT > FROM > WHERE > GROUP BY > HAVING > ORDER BY > LIMIT
9 子查询
1 子查询过滤
假设要得出充值用户的用户信息
SELECT role_id, name FROM user WHERE role_id in (SELECT role_id FROM user_pay);
2 做为计算字段使用子查询
假设要得出用户的充值次数(user_pay)以及用户信息(user)
SELECT role_id,
name,
(SELECT COUNT(*)
FROM user_pay
WHERE user_pay.role_id = user.role_id) AS recharge_count
FROM user
LIMIT 10;
10 联结
1 内联结
同9.2 假设要得出用户的充值次数(user_pay)以及用户信息(user), 以下两种方法都可以获得结果。
使用WHERE子句
SELECT role_id, COUNT(money)
FROM user, user_pay
WHERE user.role_id = user_pay.rid
GROUP BY role_id;
使用INNER JOIN
SELECT role_id,
COUNT(money)
FROM user
INNER JOIN user_pay
ON user_pay.role_id = user.role_id
GROUP BY role_id;
2 外联结
外联结使用OUTER JOIN来表示。
必须在OUTER 前加上LEFT或RIGHT关键字。OUTER可以省略不写。
LEFT: 表示选中OUTER左侧表的所有行。
RIGHT: 表示选中OUTER右侧表的所有行。
SELECT a.role_id, SUM(b.money) AS total_recharge
FROM user a
LEFT JOIN user_pay b
ON a.role_id = b.role_id
GROUP BY a.role_id;
以上信息获取用户的充值信息,如果有用户没有充值,则total_recharge=NULL。
如果使用RIGHT JOIN,如果user_pay中有用户数据在user表中找不到,则role_id=NULL。
11 组合查询
1 UNION
假设需要获取充值额度为30的用户, 以及渠道为1001的用户,使用组合查询:
SELECT role_id, money FROM user_pay WHERE money = 30
UNION
SELECT role_id, money FROM user_pay WHERE channel_id = 1001;
- 组合使用UNION将独立的SELECT相连。
- 每个SELECT查询都必须包含相同的列,表达式或函数。但次序不必相同。
2 UNION ALL
UNION从查询结果中自动去除了重复的行。比如渠道1001也有人充值30的。
如果不想被去除重复的行,可以使用UNION ALL。
3 组合查询结果排序
可以在最后一条的SELECT后添加ORDER BY语句对结果进行排序。
12 全文本搜索
1 引擎支持
- MyISAM和InnoDB(5.6)都支持全文本搜索。
TODO
13 视图
视图可以简化操作,保护数据。
1 创建视图
- 使用CREATE VIEW创建视图。
- 使用DROP VIEW删除视图。
- 这边使用 CREATE OR REPLACE VIEW
-
创建一个视图,该视图从用户表(user), 用户充值表(user_pay)获取用户基本信息,总充值额度。
CREATE OR REPLACE VIEW user_pay_info AS
SELECT b.role_id, b.name, SUM(a.money) AS total_money
FROM user_pay a
RIGHT JOIN user b
ON a.role_id = b.role_id
GROUP BY b.role_id;
使用SHOW TABLES可以发现多了一个表,user_pay_info。
2 使用视图 SELECT
3 更新视图 UPDATE
视图中存在以下操作,则不可更新:
- 分组 (GROUP BY, HAVING)
- 联结
- 子查询
- 聚集函数 (MIN, COUNT, SUM)
- DISTINCT
但凡MySQL不能确定能够正确更新到实际表(user, user_pay),则不允许进行视图更新。
一般,应该将视图用于检索,而不用于更新。
14 存储过程
相当于调用预编译好的sql集合。
1 创建存储过程 CREATE PROCEDURE
假设要知道每个用户的充值总额
CREATE PROCEDURE user_pay_total()
BEGIN
SELECT role_id, SUM(money) AS total_recharge
FROM user_pay
GROUP BY role_id;
END
以上就创建好了。
需要注意的是,如果在命令行工具中直接用以上语句创建,会报错。
因为命令行工具也用;
做为分隔符,sql语句中也是用;
做为分隔符,存在冲突。
是用 DELIMITER 可以自定义命令行工具的分隔符
DELIMITER //
CREATE PROCEDURE user_pay_total()
BEGIN
SELECT role_id, SUM(money) AS total_recharge
FROM user_pay
GROUP BY role_id;
END //
DELIMITER ;
以上DELIMITER告诉命令行工具,使用//
做为分隔符。最后一句恢复回;
做为分隔符。
2 使用存储过程 CALL
CALL user_pay_total();
以上语句会执行刚才创建的存储过程。
3 删除存储过程 DROP
可以直接使用DROP删除
DROP PROCEDURE user_pay_total;
但是,如果不存在user_pay_total(),就会报错。
所以,建议用以下命令:
DROP PROCEDURE IF EXISTS user_pay_total;
4 使用参数
参数可以用IN, OUT, INOUT修饰。
TODO
5 检测存储过程
以下语句可以显示创建存储过程的鳄鱼局
SHOW CREATE PROCEDURE user_pay_info;
15 触发器
1 创建触发器
- MySQL触发器只响应以下语句: INSERT, UPDATE, DELETE
- 保持每个数据库触发器名称唯一。
- 只有表才支持触发器,视图,临时表不支持。
创建tb1, tb2同tb1
CREATE TABLE `tb1` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '索引值',
`value` int(11) NOT NULL COMMENT '数据',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
创建触发器,命令行下别忘了 DELIMITER
DELIMITER //
CREATE TRIGGER tb1_cp_tb2
AFTER INSERT ON tb1
FOR EACH ROW
BEGIN
INSERT INTO tb2(id, value) VALUES (NEW.id, NEW.value);
END //
DELIMITER ;
以上触发器在tb1执行INSERT操作时触发,会给tb2插入相同的数据。
2 删除触发器
DROP TRIGGER IF EXISTS tb1_cp_tb2;
3 触发说明
- INSERT
- INSERT触发器可在INSERT执行之前或之后触发。
- 在触发器代码内,可以使用一个名为NEW的虚拟表,访问被插入的行。
- 对于AUTO_INCREMENT列,NEW在INSERT之前为0,在INSERT执行之后为自动生成的值。
- UPDATE
同INSERT
-
DELETE
- DELETE触发器可在DELETE执行之前或之后触发。
-
在触发器代码内,可以使用一个名为OLD的虚拟表,访问被插入的行。
DELIMITER //
CREATE TRIGGER tb1_cp_tb2
AFTER DELETE ON tb1
FOR EACH ROW
BEGIN
INSERT INTO tb2(id, value) VALUES (OLD.id, OLD.value);
END //
DELIMITER ;
从tb1删除的数据会被复制到tb2中。
16 事务处理
事务处理可以用来维护数据库的完整性,保证多个SQL命令要么完全执行,要么完全不执行。
1 事务处理示例
SELECT * FROM tb1;
START TRANSACTION;
DELETE FROM tb1;
SELECT * FROM tb1;
ROLLBACK;
SELECT * FROM tb1;
- 以上语句中,当删除tb1后,再次查询,没有内容。当回滚后,数据又出现了。
- 可以使用COMMIT将事务提交上去执行。
- 不能回退SELECT, CREATE, DROP操作。
- 当执行COMMIT或ROLLBACK后,事务会自动关闭。
2 保留点
复杂的事务处理中,可能存在需要部分回退或者部分提交的情况。
可以使用保留点来处理。
SAVEPOINT d1;
...
ROLLBACK TO d1;
当事务关闭后,保留点会自动释放。
3 autocommit
InnoDB默认 autocommit=on,即每一条sql语句都是当成一个事务,执行后就提交。
当写下START TRANSACTION时,autocommit的设置就无效了。需要等待COMMIT或ROLLBACK来结束事务。
autocommit针对的是每个与MySQL的链接,改变其值不会影响其它链接。