3.偏头痛杨的mysql教学系列之查询篇

版权声明:偏头痛杨:本文为博主原创文章,未经作者允许不得转载,版权必究! https://blog.csdn.net/piantoutongyang/article/details/82828948

前戏

查询是MYSQL中最精彩的一章,数据存储在数据库中,如果一直存着也没有意义,
我们需要使用SQL语句将数据查询出来,然后把这些数据返回给需要使用的地方。
使用select语句来进行数据表的查询功能,
select语句用于从一个或多个数据表中选定特定行、特定列的交集。




单表查询

每次查询只查一张表,以及相关的语法,查询的基础。
查询语句主要以这个句式为基准:“SELECT * FROM 表名 WHERE 查询条件”

SELECT后面用于确定选择哪些列显示,WHERE后面用于确定选择哪些行显示。
没有WHERE条件默认显示所有行,使用*表示显示所有列。

#查询所有列所有行
SELECT * FROM t_user;

#查询符合条件的行
SELECT * FROM t_user WHERE user_id > 2;

#使用算术运算符,形成算术表达式,可以用在数值、日期、列之间的运算。
SELECT user_id+10 AS user_id FROM t_user WHERE user_id + 1 > 2;

#字符串连接使用concat()函数。
SELECT CONCAT(user_name ,'good') AS user_name FROM t_user;
SELECT CONCAT(user_name ,user_id) AS user_name FROM t_user;

#去掉重复值使用DISTINCT关键字,要紧接SELECT关键字后面。
#如果后面是多列组合,则去掉多列组合的重复项,而不是单列的重复项。
SELECT DISTINCT user_name,user_age FROM t_user;

#查询范围,user_id>=1并且user_id<=3。
SELECT * FROM t_user WHERE user_id BETWEEN 1 AND 3;

#查询范围,括号里的每个值符合都会查询出来。
SELECT * FROM t_user WHERE user_id IN (1,2,3);

#模糊查询,通配符_代表一个字符,%代表多个字符,如果不希望_与%代表通配符就需要使用\转义。
#查询包含f的名字
SELECT * FROM t_user WHERE user_name LIKE '%f%';
#查询f开头的名字
SELECT * FROM t_user WHERE user_name LIKE 'f%';
#查询f结尾的名字
SELECT * FROM t_user WHERE user_name LIKE '%f';
#查询前三个字母是fre,第五个字母是k的名字
SELECT * FROM t_user WHERE user_name LIKE 'fre_k';
#查询只有4个字母的名字
SELECT * FROM t_user WHERE user_name LIKE '____';
#查询包含_的名字
SELECT * FROM t_user WHERE user_name LIKE '%\_%';
#查询日期为2018-01-13 14:32的数据,秒随意。
SELECT * FROM t_user WHERE create_date LIKE '2018-01-13 14:32%'
#查询姓名以m及p开头
SELECT * FROM t_user WHERE user_name LIKE 'm%' OR user_name LIKE 'p%';

#查询为null的数据,注意null不是空字符串。
SELECT * FROM t_user WHERE user_age IS NULL;

#查询条件为"或"、"且"、"非"逻辑运算符。注意逻辑运算符与算术运算符的优先级,括号>比较运算符>not>and>or
SELECT * FROM t_user WHERE user_age = 1 AND user_name = 'alex1';
SELECT * FROM t_user WHERE user_age = 1 OR user_name = 'alex2';
SELECT * FROM t_user WHERE NOT user_age = 1;
SELECT * FROM t_user WHERE user_age <> 1;
SELECT * FROM t_user WHERE user_name = 'alex2' OR (user_age <> 1 AND user_name LIKE '%f%');

#按某列大小进行排序,如果没有排序则默认按照插入顺序显示,排序后默认按照升序显示
#允许对多列进行排序,当第一排序列全部满足后,第一排序列有重复的数据才会使用第二排序列进行排序。
SELECT * FROM t_user ORDER BY user_age DESC;
SELECT * FROM t_user ORDER BY user_age ASC;
SELECT * FROM t_user ORDER BY user_age;
SELECT * FROM t_user ORDER BY user_age ASC,user_name DESC;

#分页,从2条数据开始后面的三条,不包括第2条
SELECT * FROM t_user ORDER BY user_age LIMIT 2,3;
#分页,查询0-2条数据,包括第2条
SELECT * FROM t_user ORDER BY user_age LIMIT 2;

#正则表达式:
SELECT * FROM t_user WHERE user_name REGEXP '^alex';
SELECT * FROM t_user WHERE user_name REGEXP '^[0-9]';
SELECT * FROM t_user WHERE user_name REGEXP '[0-9]$';




函数

函数用于进行数据处理或复杂运算,通过对一组数据进行计算,得到最终需要输出的结果。
函数一般会有一个或多个参数输入,最终只有一个返回值输出。
函数可以出现在SQL语句的各个位置。函数可以嵌套使用,
一个函数的返回值可以当作另一个函数的参数。

函数主要分为单行函数与多行函数两大类。
单行函数对每行输入值单独计算,每行得到一个计算结果返回给用户。
多行函数对多行输入值整体计算,最后只会得到一个结果。
多行函数又称为分组函数、聚合&聚集函数,主要用于完成一些统计功能。
主要的单行函数分类:日期函数、字符函数、数值函数、转换函数等等 。

常见函数如下:

#日期函数,注意应用服务器时间与数据库时间是两个时间。

#增加7天
SELECT DATE_ADD(NOW(), INTERVAL 7 DAY); 
SELECT ADDDATE('1998-01-02', INTERVAL 7 DAY);
SELECT ADDDATE('1998-01-02', 7);

#增加2个月
SELECT DATE_ADD(NOW(), INTERVAL 2 MONTH); 
SELECT DATE_ADD('1988-02-02', INTERVAL 2 MONTH); 

#减少7天
SELECT DATE_SUB(NOW(), INTERVAL 7 DAY); 

#增加12小时
SELECT DATE_ADD(NOW(), INTERVAL 12 HOUR); 

#减少12小时
SELECT DATE_SUB(NOW(), INTERVAL 12 HOUR); 

#增加10分钟
SELECT DATE_ADD(NOW(), INTERVAL 10 MINUTE);

#减少10分钟
SELECT DATE_SUB(NOW(), INTERVAL 10 MINUTE);

#两个日期相减,返回两个日期相差的秒数,参数2减参数1
SELECT TIMESTAMPDIFF(SECOND,'2018-01-13 14:30:37','2018-01-13 14:30:38' );

#得到指定日期的年月日
SELECT DATE('2015-08-05 13:00:00');

#得到指定日期的时分秒
SELECT TIME('2015-08-05 01:28:41');

#得到当前的年月日
SELECT CURDATE();

#得到当前的时分秒
SELECT CURTIME();

#今天是星期几
SELECT (WEEKDAY(NOW())+1);

#得到当前的年月日时分秒
SELECT CURRENT_TIMESTAMP();
SELECT NOW();


#字符串函数

#计算字符长度
SELECT CHAR_LENGTH(user_name) FROM t_user;
SELECT LENGTH(user_name) FROM t_user;

#从一个字符串中取出一个字符串,从第3个字符开始截取,包含第3个字符
SELECT SUBSTR('alexyang',3);

#字符串大小写
SELECT UPPER('alex');
SELECT LOWER('ALEX');

#连接字符串,如有任何一个参数为NULL ,则返回值为 NULL
SELECT CONCAT('a','b');

#删除前后多余的空格
SELECT TRIM('   alex   ');


#NULL函数

#如果参数列是null,则返回第二个参数
SELECT IFNULL(user_age,'没有年龄') FROM t_user;

#如果参数1与参数2相等则返回null,否则返回参数1
SELECT NULLIF(user_name,'alex2') FROM t_user;

#判断参数是否为null,如果是则返回1,后则返回0
SELECT ISNULL(user_age) FROM t_user;

#类似三目运算符,当参数1为true时返回参数2,否则返回参数3
SELECT IF(ISNULL(user_age),'没有年龄','有年龄') FROM t_user;
SELECT IF(IS_NULLABLE='NO','√','') AS NULLABLE FROM t_xxx;

#流程控制函数

#第一种case
SELECT user_name,
CASE user_name
WHEN 'alex1' THEN '金牌用户'
WHEN 'alex2' THEN '银牌用户'
ELSE '其他用户'
END
FROM t_user;

#第二种case
SELECT user_age ,
CASE
WHEN user_age<18 THEN '未成年人'
WHEN user_age>=18 THEN '成年人'
ELSE '中年人'
END
FROM t_user;


#数值函数

#计算sin值
SELECT sin(1.57);


#加密函数

#MD5加密
SELECT MD5('111111');



分组函数&聚集函数&聚合函数

分组函数将一组记录作为整体计算,每组记录返回一个结果,而不是每条记录返回一个结果。
分组函数也称为聚集函数、聚合函数。

常见的聚合函数

函 数 名 描 述
sum 计算多行的总和,必须是数值类型,可以使用distinct表示不重复, NULL不参加计算。
avg 计算多行的平均值,必须是数值类型,可以使用distinct表示不重复, NULL不参加计算。
max 计算多行的最大值。
min 计算多行的最小值。
count 计算多行总条数,可以使用distinct表示不重复,count(*)计算NULL值,count(列名)不计算NULL值。
#计算多行总数,不计算NULL值
SELECT COUNT(*) FROM t_user;

#计算多行总数,计算NULL值
SELECT COUNT(user_age) FROM t_user;

#计算多行总数,计算NULL值,去掉重复值
SELECT COUNT(DISTINCT user_age) FROM t_user;

#计算多行的总和
SELECT SUM(user_age) FROM t_user;

#计算多行的总和,去掉重复值
SELECT SUM(DISTINCT user_age) FROM t_user;

#计算多行的最大值
SELECT MAX(user_age) FROM t_user;

#计算多行的最小值
SELECT MIN(user_age) FROM t_user;

#计算平均值,NULL值用0代替,否则NULL值不参加计算
SELECT AVG(IFNULL(user_age,0)) FROM t_user;

#COUNT可以用来统计重复数据,用到了下面的分组知识
SELECT COUNT(*) as repetitions, user_name FROM t_user GROUP BY user_name HAVING repetitions > 1;



分组group by

默认情况下聚合函数会将所有记录当成一组对待,为了对记录进行显式分组,
可以使用group by子句。

group by后可以跟一个或多个列名,表明查询结果根据一列或多列进行分组,
当一列或多列的值完全相同时,mysql会把这些记录当成一组对待。
一般情况下group by需与聚合函数一起使用才有意义,
group by对于分组中的空值都看成是相同的。

#将user_age值相同的记录当成一组,并对每组统计总条数
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age;

#对多列分组,user_age+user_name值相同的记录当成一组
SELECT user_age,user_name,COUNT(user_age) FROM t_user GROUP BY user_age,user_name;

#注意:对于没有包含在GROUP BY也没有包含在聚合函数中的字段,如果在SELECT中,
#则mysql会显示第一条记录的值,其他数据库可能会禁止这种语法,但mysql允许这种写法,
#很多BUG因此产生,此处一定要注意,例如sql中的user_name,这里的user_name是不准确的。
SELECT user_name,COUNT(user_age) FROM t_user GROUP BY user_age;

例如:select goods_id,goods_name,cat_id,max(shop_price) from goods group by cat_id;
这里取出来的结果中的good_name是错误的!
因为shop_price使用了max函数,那么它是取最大的,
而语句中使用了group by 分组,那么goods_name并没有使用聚合函数,
它只是cat_id下的第一个商品,并不会因为shop_price改变而改变。

对分组进行过滤,则使用having子句,后面接一个表达式,
只有表达式为true的记录才会被过滤出来。
having子句可以接聚合函数,而where却不行。
where限制的是行,having限制的是组。

where 比 having 更有效。

#使用having过滤分组
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age HAVING COUNT(user_age) >1;

注意事项

1.group by、having中使用的列名需要包含在select列表中,否则查询数据不准确。

2.having中使用的列名不一定要包含在group by子句中。

3.order by必须跟在group by之后,如果使用having,必须要跟在group by之后,在order by之前。

4.having子句是对组进行限制,而where子句是对行进行限制,二者虽然有相似之处,但是仍有很大的不同。

5.当同时包含where,group by,having,以及集合函数时,执行顺序如下:
5.1.执行where子句查找符合条件的数据。
5.2.使用group by子句对数据进行分组。
5.3.对group by子句形成的组运行聚合函数计算每一组的值。
5.4.最后使用having子句去掉不符合条件的组。

WITH ROLLUP

WITH ROLLUP关键字将会在所有记录的最后加上一条记录。
加上的这一条记录是上面所有记录的总和。

#查询结果最后多出一行,显示count的总和
SELECT user_age,COUNT(user_age) FROM t_user GROUP BY user_age WITH ROLLUP;

GROUP_CONCAT()函数

GROUP BY关键字与GROUP_CONCAT()函数一起使用时,
每个分组中指定的字段值会全部显示出来。

#按照user_age进行分组查询,使用GROUP_CONCAT()函数将每个分组的user_name字段的值都显示出来。
SELECT GROUP_CONCAT(user_name),COUNT(user_age) FROM t_user GROUP BY user_age

那么有童鞋会问了,这个函数有什么作用吗?
思考一下如何删除重复数据吧:)




多表查询

有些时候我们要查询的数据不仅仅是来自一张表而是两张或者是多张表,
那我们就需要使用多表查询。

连接查询

注意:用于连接的列必须要加索引提高查询效率。
连接查询执行顺序:
1.多表笛卡尔积
2.where过滤笛卡尔积
3.group by分组
4.having过滤分组
5.select列名
6.order by排序

1.交叉连接cross join

cross join ,等同于select * from a,b ,笛卡尔基(源自SQL92规范)。
返回两个表中所有列的组合。如果左表有m行数据,右表有n行数据,
则执行CROSS JOIN将返回m*n行数据。

SELECT * FROM t_user u
CROSS JOIN
t_class c ON c.class_id = u.class_id
WHERE u.class_id = 1
2.内连接inner join

如果没有使用ON条件的过滤,INNER JOIN和CROSS JOIN的效果是一样的。
INNER JOIN只过滤出完全符合两边的数据,查询结果是左右连接的交集,
INNER关键字可以省略掉。

SELECT * FROM t_user u
INNER JOIN
t_class c ON c.class_id = u.class_id
WHERE u.class_id = 1

3.外连接outer join

outer关键字可以省略掉

3.1left (outer) join
获取左表所有记录,即使右表没有对应匹配的记录(右表相应列中填NULL)。

SELECT * FROM t_user u
LEFT JOIN
t_class c ON c.class_id = u.class_id

3.2right (outer) join
与左外连接相反,用于获取右表所有记录,
即使左表没有对应匹配的记录(并在左表相应列中填NULL)。

SELECT * FROM t_user u
RIGHT JOIN
t_class c ON c.class_id = u.class_id

3.3full join
全外连接将把两个表中所有不满足连接条件的记录全部列出,MYSQL不支持,不再展开。

4.其他

4.1自连接
就是自己连接自己,查询出表中的数据,但要求表中有2个id用于子连接,
一个"father_id"、一个"son_id"。

select b.xx_name from t_user a 
left join t_user b on a.father_id = b.son_id;

4.2自然连接
自然连接会以两个表中的同名列作为连接条件,如果两个表中没有同名列,
则与交叉连接效果一样。

SELECT * FROM t_user u
NATURAL JOIN
t_class c
WHERE u.class_id = 1

子查询

子查询是在查询语句中嵌套另一个查询语句,可以支持多层嵌套,
子查询语句可以出现在from、where后面。

ANY、ALL

ANY和ALL可以与>、>=、<、<=、<>、=等运算符号结合使用。
与ANY结合分别表示大于、大于等于、小于、小于等于、不等于、等于其中任意一个值。
=ANY与IN作用相同。
与ALL结合分别表示大于、大于等于、小于、小于等于、不等于、等于其中全部值。

注意事项:
1.子查询语句要用括号包起来。
2.当子查询语句出现在FROM后面时,可为子查询语句起别名。
3.当子查询语句出现在WHERE后面时,如果子查询返回单行单列记录则可以使用各种运算符,
如果是多行单列则可以使用IN、ANY、ALL来处理,
如果是多行多列可以使用圆括号多个列组合起来,看下面的例子。

#在FROM后面使用子查询
SELECT * FROM (SELECT * FROM t_user ) AS u WHERE u.user_age = 1;

#在WHERE后面使用子查询
SELECT * FROM t_user WHERE class_id IN (SELECT class_id FROM t_class WHERE class_name = '向日葵一班');

#=ANY来代替IN
SELECT * FROM t_user WHERE class_id =ANY (SELECT class_id FROM t_class WHERE class_name = '向日葵一班');

#>=ANY用法,查询大于t_class表中的class_id的所有t_user记录
SELECT * FROM t_user WHERE class_id > ANY (SELECT class_id FROM t_class WHERE class_name = '向日葵一班')

#子查询返回多列多行,使用圆括号
SELECT * FROM t_user WHERE (class_id,user_name) =ANY (SELECT class_id,class_name FROM t_class WHERE class_name = '向日葵一班')
无关子查询

在外围查询之前执行,然后返回数据供外围查询使用,它和外围查询的联系仅此而已。

SELECT name,sex,id,salary FROM Employee WHERE dno IN (select dno FROM Employee where salary > 4000)

子查询完全不依赖外围查询。
执行过程:
子查询独立运行,它的执行结果是一个部门号清单,部门清单产生后被用作外围查询的IN子句的参数。然后部门号清单中的内容被外围查询用来和外围查询访问的表中的数据进行比较,生成最后的查询结果。

相关子查询

在该子查询执行时要使用到外围查询的数据。子查询执行结束后再将它的查询结果返回到它的外围查询中,供外围查询比较使用。

SELECT dno Department,name Manager FROM Employee e WHERE 0 <= ANY(
     SELECT count(1) FROM Department WHERE e.id = mgrid 
)

执行过程:
1.外围查询从表Employee中按序读取一条记录中的内容。
2.接着运行子查询,并将外围查询所获得的记录内容用于子查询的WHERE子句中进行比较。
3.子查询将它查询的结果值回传给外围查询的WHERE子句。
4.继续执行外围查询,如果此时外围查询的WHERE子句为“真”,则回到第一步继续循环。
(注:如果外围查询有24条记录,那么子查询就要循环24次,性能较低。)

集合运算

SELECT语句查询的结果是一个包含多条数据的结果集,
查询结果可以进行交(intersect)、并(union)和差(minus)运算。

为了集合运算,2个结果集必须满足如下条件:
1.两个结果集所包含的数据列的数量必须相等。
2.两个结果集所包含的数据列的数据类型必须一一对应。

1.union
UNION运算可以把多个查询的结果合并到一个结果集里显示。
缺省的情况下,UNION子句不返回重复的记录,如果想显示所有记录,可以加ALL。

select 语句 union select 语句
SELECT key_id,course_title,create_date,course_type FROM t_course_one2many 
UNION 
SELECT key_id,course_title,create_date,'course_type1' as course_type FROM t_course_one2one;

2.minus(mysql不支持)

select 语句 minus select 语句
select student_id,student_name from student_table minus select teacher_id,teacher_name from teacher_table;
(从学生记录中减去与老师记录相同的ID,姓名的记录)
mysql并不支持,我们换成下面的:
select student_id,student_name from student_table where (student_id,student_name) not in (select teacher_id,teacher_name from teacher_table)

3.intersect(mysql不支持)
select 语句 intersect select 语句
mysql用join on的形式代替吧。


总结

本篇文章的内容可谓是相当的多,查询语句也是mysql的重头戏,
请务必掌握单表查询与多表查询,尤其是join连接查询。

猜你喜欢

转载自blog.csdn.net/piantoutongyang/article/details/82828948