从SELECT开始的SQL查询语句
本章节我们将从select语句开始学习如何对数据进⾏检索, |
---|
⽐如对数据进⾏单列或多列的获取,对数据进⾏过滤,排序,分组等操作。 |
这些都是⾮常基础且⾮常重要的基本语法,只有掌握了这些基础我们才能在后⾯的章节中学习更 |
复杂的SQL。 |
SQL示例: |
— 创建表 |
CREATE TABLE stu ( |
id int(10 ) unsigned NOT NULL AUTO_INCREMENT , |
name varchar(10 ) NOT NULL , |
email char(50 ) NOT NULL , |
phone char(11 ) NOT NULL , |
age tinyint(4 ) NOT NULL , |
sex char(1 ) DEFAULT NULL , |
class_id int(10 ) DEFAULT NULL , |
PRIMARY KEY (id ) |
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; |
— 添加数据 |
insert into stu values |
(null , ‘张三’ , ‘zhangsan@qq.com ‘ , ‘13701104321’ ,21 , ‘男 ‘ ,1 ), |
(null , ‘李四 ‘ , ‘lisi@qq.com ‘ , ‘13701104322’ ,22 , ‘男 ‘ ,1 ), |
(null , ‘王五 ‘ , ‘wangwu@qq.com ‘ , ‘13701104323’ ,20 , ‘⼥ ‘ ,1 ), |
(null , ‘赵六 ‘ , ‘zhaoliu@qq.com ‘ , ‘13701104324’ ,19 , ‘男 ‘ ,1 ), |
(null , ‘ ⽥七’ , ‘tianqi@qq.com ‘ , ‘13701104325’ ,23 , ‘⼥ ‘ ,1 ), |
(null , ‘王五六 ‘ , ‘wangwuliu@qq.com ‘ , ‘13701104326’ ,23 , ‘⼥ ‘ ,1 ), |
(null , ‘熊⼤’ , ‘xiongda@qq.com ‘ , ‘13701104327’ ,25 , ‘男 ‘ ,2 ), |
(null , ‘熊⼆ ‘ , ‘xionger@qq.com ‘ , ‘13701104328’ ,22 , ‘男 ‘ ,2 ), |
(null , ‘⼀⼀ ‘ , ‘yiyi@qq.com ‘ , ‘13701104329’ ,19 , ‘⼥ ‘ ,2 ), |
(null , ‘呀呀’ , ‘yaya@qq.com ‘ , ‘13701104320’ ,22 , ‘男 ‘ ,2 ); |
— 查看表数据 |
select * from stu; |
检索数据select
检索单个列
select name from stu
如果没有明确排序查询结果(下⼀章介绍),则返回的数据的顺序没有特殊意义。
返回数据的顺序可能是数据被添加到表中的顺序,也可能不是。只要返回相同数⽬的⾏,就是正 常的
检索多个列
select id,name,age,sex from stu
在选择多个列时,⼀定要在列名之间加上逗号,但最后⼀个列名后不加。
如果在最后⼀个列名后加了逗号,将出现错误。
检索所有列
select * from stu
使⽤通配符 ⼀般,除⾮你确实需要表中的每个列,否则最好别使⽤*通配符。
虽然使⽤通配符可能会使你⾃⼰省事,不⽤明确列出所需列,但检索不需要的列通常会降低检索 和应⽤程序的性能。
使⽤通配符有⼀个⼤优点。由于不明确指定列名(因为星号检索每个列),所以能检索出名字未 知的列。
检索不同的⾏ DISTINCT
select distinct classid from stu
DISTINCT关键字,顾名思义,此关键字指示MySQL只返回不同的值
DISTINCT关键字应⽤于所有列⽽不仅是前置它的列。
如果给出SELECT DISTINCTvend_id, prod_price,除⾮指定的两个列都不同,否则所有⾏都将被检 索出来
限制结果 LIMIT
select * from stu limit 3,4
LIMIT 3, 4的含义是从⾏3开始的4⾏.(跳过前3⾏ ,取4⾏)
替代语法 LIMIT 4 OFFSET 3意为从⾏3开始取4⾏,就像LIMIT 3,4⼀样。
使⽤完全限定的表名
select name from stu
select stu.name from stu
select stu.name from chuange.stu
对检索数据进⾏排序 ORDER BY
排序数据 ORDER BY
select * from stu order by age
默认查询出的数据,并不是随机排序的,如果没有指定排序,数据⼀般将以它在底层表中出现的顺序显 示
关系数据库设计理论认为,如果不明确规定排序顺序,则不应该假定检索出的数据的顺序有意义 通常, ORDER BY⼦句中使⽤的列将是为显示所选择的列。
但是,实际上并不⼀定要这样,⽤⾮检索的列排序数据是完全合法的。
按多个列排序
select * from stu order by classid,age
在需要对多列数据进⾏排序时,使⽤逗号分隔列名,并会按照前后顺序依次对⽐排序
order by 的排序默认升序,可以使⽤DESC设置降序排列
select * from stu order by classid,age DESC
以上语句就是先对classid进⾏升序排序,然后在结果中对age进⾏降序排序
注意
ORDER BY⼦句的位置 在给出ORDER BY⼦句时,应该保证它位于FROM⼦句之后。
如果使⽤LIMIT,它必须位于ORDER BY之后。使⽤⼦句的次序不对将产⽣错误消息
数据检索条件过滤WHERE
过 滤 数 据 WHERE
数据库表⼀般包含⼤量的数据,很少需要检索表中所有⾏。 | |
---|---|
通常只会根据特定操作或报告的需要提取表数据的⼦集。 | |
只检索所需数据需要指定搜索条件(searchcriteria),搜索条件也称为过滤条件(filter | |
condition)。 在SELECT语句中,数据根据WHERE⼦句中指定的搜索条件进⾏过滤。 | |
select name from stu where age = 22 | |
在同时使⽤ORDER BY和WHERE⼦句时,应该让ORDER BY位于WHERE之后,否则将会产⽣错误 | |
WHERE⼦句操作符 |
|
操作符 | 说明 |
= | 等于 |
<> != | 不等于 |
< | ⼩于 |
<= | ⼩于等于 |
> | ⼤于 |
>= | ⼤于等于 |
BETWEEN .. and.. | 指定两个值之间 |
IS NULL | 空值 |
组合WHERE⼦句
MySQL允许给出多个WHERE⼦句。
这些⼦句可以两种⽅式使⽤:以AND⼦句的⽅式或OR⼦句的⽅式使⽤。也称为逻辑操作符
select name from stu where age = 22 and sex = ‘m’
AND ⽤在WHERE⼦句中的关键字,⽤来指示检索满⾜所有给定条件的⾏
select name from stu where age = 22 or sex = ‘m’
OR ⽤来表示检索匹配任⼀给定条件的⾏
AND与OR
WHERE可包含任意数⽬的AND和OR操作符。允许两者结合以进⾏复杂和⾼级的过滤。
但是,组合AND和OR带来了⼀个有趣的问题。
例如:我需要在数据库中查询出1期或2期的学员,并且要求是⼥⽣
select name from stu where class_id=1 or class_id =2 and sex=’m’
以上语句不会按照预期检索出正确的数据,问题出在哪⾥?
原因在于计算的次序。 SQL在处理OR操作符前,优先处理AND操作符。
当SQL看到上述WHERE⼦句时,它理解是 1期班级的所有⼥⽣,或者2期的所有学员,⽽不分性别。换句话 说,由于AND在计算次序中优先级更⾼,操作符被错误地组合了
因此想要解决就需要提升优先级,使⽤圆括号明确地分组相应的操作符
select name from stu where (class_id=1 or class_id =2) and sex=’m’
因为圆括号具有较AND或OR操作符⾼的计算次序,数据库⾸先过滤圆括号内的OR条件。
这时, SQL语句变成了获取1期或2期的学员并且要求是⼥⽣
IN与NOT
IN操作符⽤来指定条件范围,范围中的每个条件都可以进⾏匹配。
select name from stu where class_id in (1,2)
IN WHERE⼦句中⽤来指定要匹配值的清单的关键字,功能与OR相当
为什么要使⽤IN操作符?其优点具体如下。 在使⽤⻓的合法选项清单时, IN操作符的语法更清楚且更直观。
在使⽤IN时,计算的次序更容易管理(因为使⽤的操作符更少)。
IN操作符⼀般⽐OR操作符清单执⾏更快。
IN的最⼤优点是可以包含其他SELECT语句,使得能够更动态地建⽴WHERE⼦句。
NOT WHERE⼦句中⽤来否定后跟条件的关键字
select name from stu where class_id not in (1,2)
为什么使⽤NOT?对于简单的WHERE⼦句,使⽤NOT确实没有什么优势。
但在更复杂的⼦句中, NOT是⾮常有⽤的。
例如,在与IN操作符联合使⽤时, NOT使找出与条件列表不匹配的⾏⾮常简单。
通配符的使⽤
LIKE与通配符
前⾯介绍的所有操作符都是针对已知值进⾏过滤的。
但是,这种过滤⽅法并不是任何时候都好⽤。
例如,怎样搜索姓名中包含⽂本a的所有⽤户?⽤简单的⽐较操作符肯定不⾏,必须使⽤通配符。 为在搜索⼦句中使⽤通配符,必须使⽤LIKE操作符。
LIKE指示MySQL,后跟的搜索模式利⽤通配符匹配⽽不是直接相等匹配进⾏⽐较。 百分号(%)通配符 在搜索串中, %表示任何字符出现任意次数
select name from stu where name like ‘a%’
select name from stu where name like ‘%a’
select name from stu where name like ‘%a%’
下划线(_)通配符 下划线的⽤途与%⼀样,但下划线只匹配单个字符⽽不是多个字符
使⽤通配符的技巧
正如所⻅, MySQL的通配符很有⽤。
但这种功能是有代价的:通配符搜索的处理⼀般要⽐前⾯讨论的其他搜索所花时间更⻓。
这⾥给出⼀些使⽤通配符要记住的技巧 不要过度使⽤通配符。如果其他操作符能达到相同的⽬的,应该使⽤其他操作符。
在确实需要使⽤通配符时,除⾮绝对有必要,否则不要把它们⽤在搜索模式的开始处。
把通配符置于搜索模式的开始处,搜索起来是最慢的。
仔细注意通配符的位置。如果放错地⽅,可能不会返回想要的数据
了解下MySQL的正则 REGEXP
所有种类的程序设计语⾔、⽂本编辑器、操作系统等都⽀持正则表达式
select name from stu where name regexp ‘[0-5]abc’
字段的计算
存储在数据库表中的数据⼀般不是应⽤程序所需要的格式。下⾯举⼏个例⼦
如果想在⼀个字段中既显示⽤户名,⼜显示班级号,但这两个信息⼀般包含在不同的表列中。
同时姓名、⼿机号和地址存储在不同的列中(应该这样),但快递单打印程序却需要把它们作为⼀ 个恰当格式的字段检索出来。
列数据是⼤⼩写混合的,但报表程序需要把所有数据按⼤写表示出来。
物品订单表存储物品的价格和数量,但不需要存储每个物品的总价格(⽤价格乘以数量即可)。为 打印发票,需要物品的总价格。
需要根据表数据进⾏总数、平均数计算或其他计算
计算字段并不实际存在于数据库表中。计算字段是运⾏时在SELECT语句内创建的
拼接 Concat
stu表包含⽤户名和⼿机号码信息。假如要⽣成⼀个学⽣报表,需要在学⽣的名字中按照
name(phone)这样的格式列出
解决办法是把两个列拼接起来。在MySQL的SELECT语句中,可使⽤Concat()函数来拼接两个列
select concat(name,’(‘,phone’)’) from stu
使⽤别名 AS
SELECT语句拼接字段可以完成。但此新计算列的名字是什么呢?实际上它没有名字,它只是⼀个 值。
如果仅在SQL查询⼯具中查看⼀下结果,这样没有什么不好。
但是,⼀个未命名的列不能⽤于客户机应⽤中,因为客户机没有办法引⽤它。
为了解决这个问题, SQL⽀持列别名。别名(alias)是⼀个字段或值的替换名。别名⽤AS关键字赋 予
select concat(name,’(‘,phone’)’) from stu as name_phone from stu
计算
在mysql中可以对列中的字段进⾏计算,使⽤基本算术操作符,此外,圆括号可⽤来区分优先顺序。
运算符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
SELECT提供了测试和试验函数与计算的⼀个很好的办法。
虽然SELECT通常⽤来从表中检索数据,但可以省略FROM⼦句以便简单地访问和处理表达式。
例如, SELECT 3*2;将返回6, SELECT Trim(‘abc’);将返回abc,⽽SELECT Now()利⽤Now()函数返回当 前⽇期和时间。
通过这些例⼦,可以明⽩如何根据需要使⽤SELECT进⾏试验
函数的使⽤
与其他⼤多数计算机语⾔⼀样, SQL⽀持利⽤函数来处理数据。
函数⼀般是在数据上执⾏的,它给数据的转换和处理提供了⽅便。
⽂本处理函数
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html
函数 | 说明 |
---|---|
Le!() | 返回串左边的字符 |
Length() | 返回串的⻓度 |
Locate() | 找出串的⼀个⼦串 |
Lower() | 将串转换为⼩写 |
LTrim() | 去掉串左边的空格 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
SubString() | 返回⼦串的字符 ** |
Upper() | 将串转换为⼤写 |
Concat | 拼接字符串. ** |
⽇期和时间处理函数
https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html
函数 | 说明 |
---|---|
AddTime() | 增加⼀个时间(时、分等) |
CurDate() | 返回当前⽇期 ** |
CurTime() | 返回当前时间 ** |
Date() | 返回⽇期时间的⽇期部分 |
DateDi”() | 计算两个⽇期之差. ** |
Date_Add() | ⾼度灵活的⽇期运算函数 |
Date_Format() | 返回⼀个格式化的⽇期或时间串 |
Day() | 返回⼀个⽇期的天数部分 |
DayOfWeek() | 对于⼀个⽇期,返回对应的星期⼏ |
Hour() | 返回⼀个时间的⼩时部分 |
Minute() | 返回⼀个时间的分钟部分 |
Month() | 返回⼀个⽇期的⽉份部分 |
Now() | 返回当前⽇期和时间. ** |
Second() | 返回⼀个时间的秒部分 |
Time() | 返回⼀个⽇期时间的时间部分 |
Year() | 返回⼀个⽇期的年份部分 |
数值处理函数
https://dev.mysql.com/doc/refman/5.7/en/numeric-functions.html
函 数 | 说 明 |
---|---|
Abs() | 返回⼀个数的绝对值 |
Cos() | 返回⼀个⻆度的余弦 |
Exp() | 返回⼀个数的指数值 |
Mod() | 返回除操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回⼀个随机数. ** |
Sin() | 返回⼀个⻆度的正弦 |
Sqrt() | 返回⼀个数的平⽅根 |
Tan() | 返回⼀个⻆度的正切 |
聚集函数与分组 Group By
聚集函数的使⽤
我们经常需要汇总数据⽽不⽤把它们实际检索出来,为此MySQL提供了专⻔的函数。
使⽤这些函数, MySQL查询可⽤于检索数据,以便分析和报表⽣成
确定表中⾏数(或者满⾜某个条件或包含某个特定值的⾏数)。
获得表中⾏组的和。
找出表列(或所有⾏或某些特定的⾏)的最⼤值、最⼩值和平均值。
上述例⼦都需要对表中数据(⽽不是实际数据本身)汇总。
因此,返回实际表数据是对时间和处理资源的⼀种浪费
函数 | 说明 |
---|---|
COUNT() | 返回某列的⾏数 |
MAX() | 返回某列的最⼤值 |
MIN() | 返回某列的最⼩值 |
SUM() | 返回某列值之和 |
AVG() | 返回某列的平均值 |
注意
在使⽤count时,如果指定列名,则指定列的值为空的⾏被忽略,但如果COUNT()函数中⽤的是星号 (*),则不忽略
数据分组 GROUP BY与HAVING
GROUP BY
SQL聚集函数可⽤来汇总数据。这使我们能够对⾏进⾏计数,计算和与平均数,获得最⼤和最⼩值
⽽不⽤检索所有数据
⽬前为⽌的所有计算都是在表的所有数据或匹配特定的WHERE⼦句的数据上进⾏的。
例如我们需要获取某个班级的学员⼈数:
select count(*) as nums from stu where class_id = 2
但如果要返回每个班级的⼈数怎么办?
此时就需要使⽤分组了,分组允许把数据分为多个逻辑组,以便能对每个组进⾏聚集计算。
select class_id,count(*) as nums from stu group by class_id
那么如果需要返回⼈数少于5⼈的班级怎么办?
或返回班级⼈数⼤于5⼈的班级怎么办?
HAVING
除了能⽤GROUP BY分组数据外, MySQL还允许过滤分组,规定包括哪些分组,排除哪些分组。 例如,需要获取班级⼈数⼩于等于5⼈的班级。
事实上,⽬前为⽌所学过的所有类型的WHERE⼦句都可以⽤HAVING来替代。
唯⼀的差别是WHERE过滤⾏,⽽HAVING过滤分组。
select class_id,count() as nums from stu group by class_id;
select class_id,count() as nums from stu group by class_id having nums >= 5;
使⽤Group By进⾏分组时的注意点
在使⽤group by进⾏分组时,按照标准的SQL模式,需要把select查询中的所有列(除了聚集函数 外)全部都列在group by后⾯ |
---|
mysql> select class_id,count(*) as nums from stu group by class_id; |
+—————+———+ |
| class_id | nums | |
+—————+———+ |
1 | 7 | | |
2 | 5 | | |
+—————+———+ |
2 rows in set (0.00 sec) |
— 在select⾥⾯有 class_id,class_name, 但是group by 后⾯只有 class_id ,那么语法错误。 |
mysql> select class_id,class_name,count(*) as nums from stu group by class_id; |
ERROR 1055 (42000 ): Expression #2 of SELECT list is not in GROUP BY clause and |
contains nonaggregated column ‘chuange.stu.class_name’ which is not functionally |
dependent on columns in GROUP BY clause; this is incompatible with |
sql_mode=only_full_group_by |
— 正确的语句,应该是,把select中所以列都作为分组条件 |
mysql> select class_id,class_name,count(*) as nums from stu group by |
class_id,class_name; |
+—————+——————+———+ |
| class_id | class_name | nums | |
+—————+——————+———+ |
| 7 | | 1 | 1班 |
| 5 | | 2 | 2班 |
+—————+——————+———+ |
2 rows in set (0.00 sec) |
总结SELECT⼦句及其顺序
⼦句 | 说明 | 是否必须 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使⽤ |
WHERE | ⾏级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使⽤ |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
LIMIT | 要检索的⾏数 | 否 |