本篇博客是对《SQL必知必会》第五版书籍的总结。
对于文章的出现的表名称,列名请参考书籍《SQL必知必会》及上一篇博客
sql必知必会(第五版)学习总结(一)--mysql预备知识汇总
目录
1:检索数据
1.1检索某个列
select prod_name from Products;
使用select语句进行检索,使用from关键字指定数据库。
1.2检索多个列
select prod_name , prod_id , prod_price from Products;
各表名之间使用逗号分开
1.3检索所有列
select * from Products;
使用通配符返回表中的所有列
1.4 distinct关键字
select distinct vend_id
from Products;
使用distinct关键字指示数据库只返回不同的值。注意,使用distinct关键字,它必须直接放在列的前面。
另外distinct关键字作用于所有的列,不仅仅是跟在其后的列。(自己理解:先得到select的结果,再从结果中除去重复的)
1.5 limit关键字
select语句返回指定表中所有匹配的行,如果只想返回第一行或者一定数量的行,就需要使用limit子句。
select prod_name
from Products
limit 5;
如果为了得到后面5行数据,需要指定检索的行数和从哪个位置开始检索。
limit 5 offset 3
其中5指的是检索的行数,3指的是从哪儿开始。
1.6 order by子句(desc)
为了明确地排序用select检索出来的数据,可以使用order by子句,order by子句取一个或者多个列的名字,据此对输出进行排序。
select prod_name
from Products
order by prod_name;
【注意1】:order by 子句的位置,指定一条order by 子句时,应该保证它是select语句的最后一条子句,如果不是最后的子句,将会出错
【注意2】:经常需要按不止一个列进行数据排序,要按多个列排序,只需指定这些列名。列名之间使用逗号隔开,也就是说只需要写一个order by。
select prod_id, prod_price, prod_name
from Products
order by prod_price , prod_name;
【注意3】:order by除了按照列名排序,还支持按相对位置排序
order by 2,3
【注意4:】数据排序不限于升序排序,这只是默认的排序顺序,还可以指定desc关键字进行降序排序
desc:desc关键字只应用到直接位于其前面的列名,如果想在多个列上进行降序,必须对每一个列指定desc关键字
order by prod_price desc , prod_name;
2:高级数据过滤
2.1where 子句
(1)数据根据where子句中指定的搜索条件进行过滤,where子句在表名(from子句)之后给出
(2)在同时使用order by 和where子句时,应该让order by 位于where之后。
(3)where子句的操作符有:between,is null , > , < 等
select vend_id , prod_name
from Products
where vend_id <> 'DLL01';
【注意】:单引号来限定字符串,如果将数值与字符串类型的列进行比较,就需要限定引号,
用来与数值列进行比较就不需要引号。
2.2 between
使用between时,必须指定两个值,所需范围的低端值和高端值,这两个值必须使用and关键字分隔。
between匹配范围中的所有值,包括指定的开始值和结束值。
select prod_name , prod_price
from Products
where prod_price between 5 and 10;
2.3 is null
检查是否具有null的列
select prod_name
from Products
where prod_price is null;
2.4 or / and
(1)为了进行更强的过滤控制,sql允许给出多个where子句,这些where子句有两种使用方式,即以and子句或者or子句方式使用
(2)像大多数语言一样,在处理or操作符前,会优先处理and操作符,若想改变优先级,则可使用圆括号对操作符进行明确分组。
select prod_id , prod_price , prod_name
from Products
where vend_id = 'DLL01' or vend_id = 'BRS01';
2.5 in / not
1) in操作符用来指定条件范围,范围中的每个条件都可以进行匹配。in取一组由逗号分隔,括在圆括号中的合法值。
where vend_id in ('DLL01','BRS01');
仔细观察,in操作符完成了与or相同的功能。
2)sql中not的作用:否定其后跟的任何条件,因为not从不单独使用(它总是与其它操作符一起使用)
where子句中的not用来否定其后条件中的关键字
select prod_name
from products
where not vend_id = 'DLL01'
order by prod_name;
上述语句的作用是列出除DLL01之外的所有供应商。
3.通配符进行过滤
3.1 通配符和搜索模式
通配符:用来匹配值的一部分特殊字符
搜索模式:由字面值,通配符或者两者组合构成的搜索条件
通配符搜索只能用于文本字段(字符串),非文本数据类型字段不能使用通配符搜索
3.2 like操作符及其通配符
通配符是SQL的WHERE子句中的特殊含义字符,子句中使用通配符必须使用LIKE操作符
1)百分号(%)通配符
% : 表示任意字符出现任意次数
select prod_id, prod_name
from Products
where prod_name like 'Fish%'
执行该条子句时,将检索任意以fish开头的词。
【注意】通配符可以在搜索模式中的任意位置使用。
select prod_id, prod_name
from Products
where prod_name like '%bean bag%';
【注意】:%:代表搜索模式中给定位置的0个,1个或者多个字符
【注意】:% 不会匹配名称为null的行
2)下划线(_)通配符
下划线的用途与%一样,但它只匹配单个字符,而不是多个字符。
select prod_id , prod_name
from Products
where prod_name like '_inch teddy bear';
【注意】:有一点与我们思维不同的是: _ 和% 之后的单词与数据库要完整,eg: '_inch te' 匹配不到任何东西。
3)方括号[]通配符用来指定一个字符集。
SELECT cust_contact FROM Customers WHERE cust_contact REGEXP '[JM]' ORDER BY cust_contact;
匹配任意带有JM的字符串的列。
3.3使用通配符的技巧
不要过度使用通配符,如果其它操作能够达到相同的目的,应该使用其它操作符
在确实需要使用通配符时,也尽量不要把他们放在搜索模式的开始处,因为通配符置于开始处,搜索起来是最慢的。
要仔细注意通配符的位置
4.计算字段
4.1 为什么要使用计算字段
计算字段是在运行时SELECT语句内创建的。
字段与列(column)意思类似,经常相互转换使用,字段通常用在计算字段的链接上。
在SQL内完成转换和格式化,比在客户机应用程序内完成,处理数度更快。
4.2 拼接字段
在mysql使用CONCAT()函数把项表连接起来(注意其它数据库可能使用“+”进行连接,但是mysql不行),而|| 通等于操作符OR 而&&通等于AND操作符
SELECT CONCAT(vend_name, ' (', vend_country, ')')
FROM Vendors
ORDER BY vend_name;
4.3 别名
拼接的地址字段,没有一个名字,无法给客户机应用,所以需要字段别名(alias)
,另一种叫法导出列(derived column)
别名可以用AS关键字赋予
SELECT CONCAT(vend_name, ' (', vend_country, ')') as vend_title
FROM Vendors
ORDER BY vend_name
5.使用函数处理数据
5.1函数的作用
函数主要给数据提供处理与转换方便。
大多数SQL实现的函数
- 用于处理文本串(删除,充值,大小写转换)
- 用于在数值的数据上进行算术(返回绝对值,代数运算)操作。
- 用于处理日期时间值并从这些值中提取特定成份。
- 返回DBMS正使用的特殊信息(用户登录信息)
5.2 常用的函数
以upper()函数举例说明:
SELECT vend_name, UPPER(vend_name) AS
vend_name_upcase
FROM Vendors
ORDER BY vend_name;
6.汇总数据
6.1 聚集函数
- 确定表中的行数
- 获得表中行组的和
- 找出表列(所有行,特定行)的最大,最小,平均值。
聚集函数需要对表中的数据汇总,而不是实际数据本身,所以可以不需要返回时间数据,浪费资源
6.2 常用的聚集函数
AVG(): 返回所有列或者某个列平均值
MAX()返回指定的列中最大的值
MIN() 返回指定列中的最小值
COUNT()函数计算表中行的数目或符合特定条件的涵数目。
忽略表列中包含的空值(NULL)与非空值,对表中数目进行计算。
使用COUNT(column) 对特定列中具有值的行进行计算,忽略NULL值
SUM()返回指定的列值的总和
举例:
返回Customers表中的客户的总数,而不管行中各列的值。
SELECT COUNT(*) AS num_cust
FROM Customers;
计算Products表中所有产品的平均价格
SELECT AVG(prod_price) AS avg_price
FROM Products;
返回有email地址的客户计数,结果为3,表述只有3个客户有电子邮件地址
SELECT COUNT(cust_email) AS num_cust
FROM Customers;
合计所以订单 item_price价格 乘以quantity数量之和的总数,WHERE子句某个订单物品
SELECT SUM(item_price*quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
使用select来组合聚集函数
SELECT COUNT(*) AS num_items,
MIN(prod_price) AS price_min,
MAX(prod_price) AS price_max,
AVG(prod_price) AS price_avg
FROM Products;
7.分组数据
7.1 创建分组
用GROUP BY 跟 HAVING子句,分组数据来汇总表内容子集
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
上述代码:SELECT
语句指定两个列,vend_id
包含供应商ID,为num_prods
计算字段结果, GROUP BY
子句指示 vend_id 排序并分组数据
7.2 GROUP BY子句规则:
- 包含任意数目的列,
- 如果在GROUP BY 子句中套入分组,数据将会最后规定的分组上进行总汇。
- GROUP BY 子句中列出的没列都必须是检索的列,有效的表达式,不能聚集函数。
- 大多数SQL不允许GROUP BY 带有长度可变的数据类型(文本,备注型字段)
- 除聚集计算语句外,SELECT 语句中,每个列都必须在GROUP BY子句中给出。
- 如果分组带有NULL值,将作为一个分组返回,如果多个将成一组。
- GROUP BY 子句必须出现在WHERE子句之后
7.3 过滤分组
过滤分组规定包含哪些分组,排除哪些分组,用HAVING
子句,与WHERE子句类似,
唯一差别的是 WHERE用来过滤行,HAVING过滤分组。
也可以说HAVING在数据分组后过滤,WHERE在数据分组前进行过滤
SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
过滤出两个以上订单的分组
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;
上述代码作用:第一行使用聚集函数,WHERE子句过滤除所有prod_price
少于4的行,
按vend_id分组,HAVING 子句过滤计数2以上分组
7.4 group by和 order by区别
-
GROUP BY
- 排序产生的输出
- 任意列都可以使用
- 可以选择是否与聚集函数一起使用
-
ORDER BY
- 分组行,输出可能不是分组循序
- 只可能使用选择列或表达式,且必须使用每个列表达式
- 如果与聚集函数一起用,则必须使用
7.5 select子句顺序
8.子查询
1)子查询一般与in操作符一起使用
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
上述代码:子查询是从内向外处理
先执行 SELECT order_num FROM OrderItems WHERE prod_id = 'RGAN01'
把返回的订单号,20007,20008两个值以IN操作符用逗号格式传递给外部查询,
再用SELECT cust_id FROM orders WHERE order_num IN (20007,20008)
2)作为计算字段使用子查询
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE cust_id = cust_id) AS orders
FROM Customers
ORDER BY cust_name;
9.联结表
9.1为什么要使用联结?
因为关系数据库设计:
- 避免相同数据出现多次
- 信息被分解成一种数据,一个表
- 各表通过某些常用值相互关联
所以:
分解多个表方便存储,方便处理,可伸缩性强。
使用链接可以用一条SELECT中关联多个表返回一组输出。
注意:在设计关系数据库,避免在另一个关系表中插入非法的ID,可以设置关系表中值,只出现合法的值
9.2 创建联结--内联结
内联结也称等值联结,它基于两个表之间的相等测试
内部联结:将两个表中存在联结关系的字段符合联结关系的那些记录形成记录集的联结。
有两种语法:建议使用第二种
1)where 语句进行的相等测试
2)使用INNER JOIN联结时,联结条件用特定的ON子句
【注意】:由没有联结条件的表关系返回的结果为笛卡儿积
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
上述代码:
SELECT vend_name, prod_name, prod_price
指定检索的列,prod_name, prod_price
在同一个表。vend_name
在另外一个表
From 指定联结两个表Vendors, Products
WHERE子句限定 Vendors.vend_id = Products.vend_id
完全限定名
建议使用下面的inner join .... on这种语法结构。
--用inner join明确联结类型
SELECT vend_name, prod_name, prod_price
FROM vendors
INNER JOIN products ON vendors.vend_id = products.vend_id
ORDER BY vend_name, prod_name;
9.3 创建联结--自联结
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones';
通过使用表别名的方式进行自己和自己联结。
9.4 创建联结--外部联结
联结包含那些在相关表中没有关联的行的行
SELECT Customers.cust_id, Orders.order_num
FROM Customers
LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
OUTER JOIN 指定联结类型,与内部联结关联两个表中不同的是,
外部联结还包含没有关联的行,用 RIGHT与LEFT关键字指定包含其所有行的表是左边还是右边。
9.5 使用联结条件
- 主要联结类型,一般使用内部联结
- 不同的DBMS联结方式不同。
- 保证使用正确的联结条件
- 使用多个联结,先分别测试每个联结。
10.组合查询
并(union) 执行多个查询并将结果作为单个查询结果返回。
一般需要使用组合查询的情况
- 单个查询中从不同的表类似返回结果数据
- 单个表执行多个查询,按单个查询返回数据
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN ('IL','IN','MI')
UNION
SELECT cust_name, cust_contact, cust_email
WHERE cust_name = 'Fun4All';
使用UNION规则
- 必须有两条以上SELECT语句组合,语句直接用关键字UNION分割。
- UNION中每个查询必须包含相同的列,表单式,聚集函数。
- 列的数据必须兼容,
是否带有重复行
UNION默认去掉重复行
如果想要所有行,可以使用UNION ALL 而不是UNION。
【注意】:在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。