描述:SQL必知必会(第四版)读书笔记~~作者:Ben Forta(美)—-译:钟鸣、刘晓霞 作者:王璐 时间:2019/1/15
第1课 了解sql
- 数据库(database):
- 表(table):
- 列(column):
- 行(row):
- 主键(primary key):
第2课 检索数据
小提示:
- sql不区分大小写:但是许多sql开发人员喜欢对sql关键字使用大写,而对列名和表名使用小写。
- 在处理sql语句时, 其中所有空格都会被忽略:多数sql开发人员认为,将sql语句分成多行更容易阅读和调试。
- 结束sql语句:多条sql语句必须以(;)分割。
- 在选择多个列时,一定要在列名之间加上(,),但在最后一列名后不加。eg:
SELECT
user_id,user_name,user_address
FROM
user;
- 使用通配符():一般而言,除非你确定需要表中的每一列,否则最好别使用通配符。因为检索不需要的列通常会降低检索和应用程序的性能。
- 使用distinct关键字,它指示数据库只返回不同的值
SELECT user_id
FROM user;
返回结果:
user_id
———————-
us01
us01
u02
u02
user03
user03
SELECT DISTINCT user_id
FROM user;
返回结果:
user_id
———————
us01
u02
user03
不能部分使用 DISTINCT : DISTINCT关键字作用于所有列,不仅仅是跟在其后的那一列。
SELECT DISTINCT user_id,user_name,user_address
FROM user;
不是仅仅是user_id而是后面所有的。
限制结果
1、sql server和Access:
SELECT TOP 5 user_name
FROM user;
SELECT TOP 5:只检索前5行数据。
2、oracle:
SELECT user_name
FROM user
WHERE ROWNUM <=5;
3、MySQL、MariaDB、PostgreSQL、SQLite:
SELECT user_name
FROM user
LIMIT 5;
LIMIT 5:指示mysql等BDBMS返回不超过5行的数据。
SELECT user_name
FROM user
LIMIT 5 OFFSET 5;
LIMIT 5 OFFSET 5:指示mysql等BDBMS返回从第5行起的5行数据。第一个数字是检索的行数,第二个数字是指从哪开始。
- 第0行:第一个被检索的行是第0行,而不是第一行。因此,LIMIT 1 OFFSET 1 - 会检索第2行而不是第
- MySQL、MariaDB、SQLite捷径:支持简化版的 LIMIT 4 OFFSET 3 语句,即LIMIT 3,4 。 使用这个语法,逗号之前的值对应offset,之后的值对应limit。(第3行起的4行数据)
- 并非所有的sql实现都一样。
- 注释
/
这是一条多行注释
/
第3课 排序检索数据
排序数据
注意:
order by 子句的位置:应该保证它是select语句中的最后一条子句,否则会出现错误信息。
SELECT user_name
FROM user
ORDER BY user_name;
按多个列排序
SELECT user_id,user_name,user_address
FROM user
ORDER BY user_name,user_address;
首先按照user_name排列然后按照user_address排序。
仅在多个行具有相同的user_name值时才对用户按照user_address进行排序。如果user_name列中所有的值都是唯一的,则不会按照user_address进行排序。
按列位置排序
SELECT user_id,user_name,user_address
FROM user
ORDER BY 2,3;
ORDER BY 2表示按照select 清单中的第二个列user_name进行排列。3表示先按照user_name排列再按照user_address排列。
显然,当根据不出现在select清单中的列进行排序时,不能使用这项技术。
指定排序方向
降序排列,默认为升序ASC
SELECT user_id,user_name,user_address
FROM user
ORDER BY user_id DESC;
按照 user_id降序排列
在多个列上降序排序:
必须对每一列指定DESC关键字
SELECT user_id,user_name,user_address
FROM user
ORDER BY user_id DESC,user_name;
按照 user_id降序排列,,user_name没指定默认按照升序排列。
第4课 过滤数据
使用where子句
注意:
在 同时使用order by 和where 子句的时候,应该让order by 位于where子句之后,否则会出错。
SELECT user_id,user_name,user_address
FROM user
WHERE user_id = 1;
where子句操作符号
where子句操作符
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
!< | 不小于 |
> | 大于 |
>= | 大于等于 |
!> | 不大于 |
BETWEEN | 在指定的两个值之间 |
IS NULL | 为NULL值 |
检查单个值
SELECT user_id,user_name,user_address
FROM user
WHERE user_id > 10;
不匹配检查
SELECT user_id,user_name,user_address
FROM user
WHERE user_id <> 'u01';
SELECT user_id,user_name,user_address
FROM user
WHERE user_id != 'u01';
注意:
<>和!= 通常是可以互换。但是,并非所有DBMS都支持这两种不等于操作符。eg:Microsoft Access支持 <>而不支持!= 。
范围值检查
SELECT user_id,user_name,user_address
FROM user
WHERE user_id BETWEEN 5 AND 10;
空值检查
SELECT user_id,user_name,user_address
FROM user
WHERE user_name IS NULL;
第5课 高级数据过滤
1
SELECT user_id,user_name,user_address
FROM user
WHERE user_name = 'zhangsan' AND user_id <= 5;
1
1
SELECT user_id,user_name,user_address
FROM user
WHERE user_id = 'u01' OR user_id = 'us01';
1
1
SELECT user_id,user_name,user_address
FROM user
WHERE (user_name = 'zhangsan' OR user_name = 'zhangsan' ) AND user_id <= 5;
1
1
SELECT user_id,user_name,user_address
FROM user
WHERE user_id IN ('u01','us01')
ORDER BY user_name;
等于:
SELECT user_id,user_name,user_address
FROM user
WHERE user_id = 'u01' OR user_id = 'us01'
ORDER BY user_name;
为什么要使用IN操作符:
- 在有很多合法选项时,IN操作符的语法更清楚、更直观。
- 在与其他AND和OR操作符组合使用IN时,求职顺序更容易管理。
- IN操作符一般比一组OR操作符执行的更快。
- IN的最大优点是可以包含其他select语句,能够更动态地建立where子句。
NOT:
where子句中用来否定其后条件的关键字
SELECT user_id,user_name,user_address
FROM user
WHERE NOT user_id = 'u01'
ORDER BY user_name;
等于:
SELECT user_id,user_name,user_address
FROM user
WHERE user_id <> 'u01'
ORDER BY user_name;
为什么要使用NOT操作符:
- 在复杂的子句中,NOT是非常有用的。eg:在与IN操作符联合使用的时候,NOT可以非常简单地找出与条件列表不匹配的行。
第6课 用通配符进行过滤
SELECT user_id,user_name,user_address
FROM user
WHERE user_id LIKE 'u01%';
SELECT user_id,user_name,user_address
FROM user
WHERE user_id LIKE '%u01%';
SELECT user_id,user_name,user_address
FROM user
WHERE user_id LIKE '_ u01';
SELECT user_id,user_name,user_address
FROM user
WHERE user_id LIKE '[u01]%';
。
SELECT user_id,user_name,user_address
FROM user
WHERE user_id LIKE '[^u01]%';
。
使用通配符的技巧
通配符搜索一般要耗费更长的处理时间。
- 不要过度使用通配符。如果其他操作符能达到相同的目的,应该使用其他操作符。
- 在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处。把通配符位置置于开始处,搜素起来是最慢的。
- 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
总之,通配符是一种极其重要和有用的搜索工具,以后我们经常会用它。
第7课 创建计算字段
MYSQL:
1、拼接字段
SELECT Concat(user_name,'(',user_address,')')
FROM user
ORDER BY user_name;
输出:
zhangsan (USA )
lisi (USA )
wangwu (China )
2、TRIM函数:
RTRIM()—去掉字符串右边的空格
LTRIM()—去掉字符串左边的空格
TRIM()—去掉字符串左右两边的空格
3、使用别名
SELECT Concat(user_name,'(',user_address,')')
AS user_title
FROM user
ORDER BY user_name;
输出:
user_title
—————————
zhangsan (USA)
lisi (USA)
wangwu (China)
执行算术计算
SELECT product_id,quantity,item_price
FROM order_item
WHERE order_num = 200008;
输出:
product_id quantity item_price
—————————————————-
pr01 5 2.2000
pro01 5 4.0000
pro02 10 5.8900
SELECT product_id,quantity,item_price,quantity*item_price AS ex_price
FROM order_item
WHERE order_num = 200008;
输出:
product_id quantity item_price ex_price
———————————————————
pr01 5 2.2000 12.5000
pro01 5 4.0000 20.0000
pro02 10 5.8900 58.9000
如何测试计算:
select 语句为测试、检验函数和计算提供了很好的方法。虽然select通常用于从表中检索数据,但是省略来from子句后就是简单地访问和处理表达式,比如:
select Trim(‘abc’); —-返回abc
select 3*2; —- 返回6
select now();—使用now函数返回当前日期和时间
第8课 使用函数处理数据
mysql:
提取字符串的组成:substring();
数据类型转换:convert();
取当前日期:curdate();
常用的文本处理函数
函数 | 说明 |
---|---|
left(); (或使用 字符串函数) |
返回字符串左边的字符 |
length(); (也使用 datalength()或len()) |
返回字符串的长度 |
lower(); (Access 使用 lcase()) |
将字符串转换为小写 |
ltrim(); | 去掉字符串左边的空格 |
right(); (或使用 字符串函数) |
返回字符串右边的空格 |
rtrim(); | 去掉字符串右边的空格 |
soundex(); | 返回字符串的soundex值 |
upper();(Access 使用 ucase()) | 将字符串转换为大写 |
输入:
SELECT user_name, UPPER(user_name) AS user_name_upcase
FROM user
ORDER BY user_name;
输出:
user_name user_name_upcase
————————————————
Zhangsan ZHANGSAN
lisi LISI
wang wu WANG WU
mysql日期处理函数:(没有datepart(),可以使用year())
SELECT order_num
FROM orders
WHERE YEAR(order_data) = 2012;
第9课 汇总数据
sql聚集函数
函数 | 说明 |
---|---|
AVG(); | 返回某列的平均值 |
COUNT(); | 返回某列的行数 |
MAX(); | 返回某列的最大值 |
MIN(); | 返回某列的最小值 |
SUM(); | 返回某值之和 |
eg:
SELECT MAX (prod_price) AS max_price
FROM products;
组合聚集函数:
SELECT COUNT(*) AS num_items,
MAX (prod_price) AS max_price,
MIN(prod_price) AS min_price,
AVG(prod_price) AS avg_price
FROM products;
注意:取别名
在指定别名以包含某个聚集函数的结果时,不应该使用表中实际的列名。虽然这样做也算合法,但许多sql实现不支持,可能会产生模糊但错误消息。
第10课 分组数据
创建分组 group by
过滤分组 having 与 where
where过滤行,having过滤分组。
分组和排序 group by 和 order by
输入:
SELECT user_id,COUNT(*) AS userNum
FROM user
GROUP BY user_id
HAVING COUNT(*) >= 3;
输出:
user_id userNum
——— ————-
10001 3
1002 5
20001 6
2002 5
输入:
SELECT user_id,COUNT(*) AS userNum
FROM user
GROUP BY user_id
HAVING COUNT(*) >= 3
ORDER BY userNum,user_id;
输出:
user_id userNum
——— ————-
10001 3
1002 5
2002 5
20001 6
select 子句顺序
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
第11课 使用子查询
利用子查询进行过滤
输入:
SELECT order_num
FROM order_items
WHERE prod_id = 'RGAN01';
输出:
order_num
—————
20007
20008
输入:
SELECT cust_id
FROM orders
WHERE order_num IN (20007,20008);
输出:
cust_id
————
1000000004
1000000005
输入:
SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM order_items
WHERE prod_id = 'RGAN01');
输出:
cust_id
————
1000000004
1000000005
输入:
SELECT cust_name,cust_contact
FROM Customers
WHERE cust_id IN (1000000004,1000000005);
输出:
cust_name cust_contact
———— —————-
Fun4All Denise L
The Toy Store KIM H
输入:
SELECT cust_name,cust_contact
FROM Customers
WHERE cust_id IN (SELECT cust_id
FROM orders
WHERE order_num IN (SELECT order_num
FROM order_items
WHERE prod_id = 'RGAN01'));
输出:
cust_name cust_contact
———— —————-
Fun4All Denise L
The Toy Store KIM H
注意:
- 只能是单列
作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误。
- 子查询和性能
作为计算字段使用子查询
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM orders
WHERE cust_id = cust_id) AS orders
FROM Customers
ORDER BY cust_name;
输出:
cust_name cust_state orders
———— —————- ———-
Fun4All IN 5
The Toy Store AZ 5
虽然子查询在构造这种select语句时极有用,但必须注意限制有歧义但列。
注意:
- 完全限定名
如果在select语句中操作多个表,就应使用完全限定列名来避免歧义。
- 不止一种解决方案
这并不是解决这种数据检索的最有效方法。还可以用join。
第12课 联结表
SELECT vend_name,prod_name,prod_price
FROM Vendors,Products
WHERE Vendors.vend_id = Products.vend_id;
SELECT vend_name,prod_name,prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
SELECT vend_name,prod_name,prod_price,quantity
FROM Vendors,Products,OrderItems
WHERE Vendors.vend_id = Products.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
第11课:
SELECT cust_name,cust_contact
FROM Customers
WHERE cust_id IN (SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01'));
第11课中的另一种写法:
执行复杂select语句操作最有效方法并不是子查询,使用联结也能得到相同查询
SELECT cust_name,cust_state
FROM Customers,Orders,OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'RGAN01';
第13课 创建高级联结
使用表别名
输入:
SELECT cust_name,cust_contact
FROM Customers AS C,Orders AS O,OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';
注:oracle中没有AS 可以直接把AS去掉,FROM Customers AS C,Orders AS O,OrderItems AS OI
可以直接写:FROM Customers C,Orders O,OrderItems OI
自联结
子查询:
输入:
SELECT cust_id,cust_name,cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
FROM Customers
WHERE cust_contact ='Jim Jones');
输出:
cust_id cust_name cust_contact
———— ————- ——————-
100003 Fun4All Jim Jones
100004 Fun4All Denise L. Stephens
联结:
输入:
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';
输出:
cust_id cust_name cust_contact
———— ————- ——————-
100003 Fun4All Jim Jones
100004 Fun4All Denise L. Stephens
提示:用自联结而不用子查询
自联结通常作为外部语句,用来替代从形同表中检索数据的使用子查询语句。虽然最终的结果是相同的,但许多dbms处理联结远比处理子查询快的多。
自然联结
输入:
SELECT C.*,
O.order_num,O.order_date,
OI.prod_id,OI.quantity,OI.item_price
FROM Customers AS C,Orders AS O,OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';
外联结
内联结
输入:
SELECT Customers.cust_id,Orders.order_num
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
外联结
输入:
SELECT Customers.cust_id,Orders.order_num
FROM Customers LEFT JOIN Orders
ON Customers.cust_id = Orders.cust_id;
输出:
cust_id order_num
———- —————
100001 20005
100001 20009
100002 NULL
100003 20006
100004 20007
100005 20008
输入:
SELECT Customers.cust_id,Orders.order_num
FROM Customers RIGHT JOIN Orders
ON Customers.cust_id = Orders.cust_id;
输入:
SELECT Customers.cust_id,Orders.order_num
FROM Customers FULL OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
注:Access 、 MariaDB 、 MySQL 、Open Office Base 和SQLite 不支持FULL OUTER JOIN 语法。
使用带聚集函数的联结
输入:
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
输出:
cust_id num_ord
———- —————
100001 2
100003 1
100004 1
100005 1
输入:
SELECT Customers.cust_id,
COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
输出:
cust_id num_ord
———- —————
100001 2
100002 0
100003 1
100004 1
100005 1
第14课 组合查询
使用union
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('LI','IN','MI');
输出:
cust_name cust_contact cust_email
———— —————— —————
Vi Toys Jon sm sale@vil.com
Full4All Jim Jon jjones@vil.com
The Toy Sto Kim How NULL
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Full4All';
输出:
cust_name cust_contact cust_email
———— —————— —————
Full4All Jim Jon jjones@vil.com
Full4All Denise L. S dstep@vil.com
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('LI','IN','MI')
UNION
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Full4All';
输出:
cust_name cust_contact cust_email
———— —————— —————
Full4All Denise L. S dstep@vil.com
Full4All Jim Jon jjones@vil.com
Vi Toys Jon sm sale@vil.com
The Toy Sto Kim How NULL
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('LI','IN','MI')
OR cust_name = 'Full4All';
输出:
cust_name cust_contact cust_email
———— —————— —————
Full4All Denise L. S dstep@vil.com
Full4All Jim Jon jjones@vil.com
Vi Toys Jon sm sale@vil.com
The Toy Sto Kim How NULL
UNION 规则
- union必须由两条或两条以上的select语句组成,语句之间用关键字union分割()
- union中每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列车)。
- 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型
包含或取消重复的行
union~~where
union all !~ where
union从查询结果集中自动去除了重复的行;如果想返回所有的匹配行,可使用UNION ALL
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('LI','IN','MI')
UNION ALL
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Full4All';
输出:
cust_name cust_contact cust_email
———— —————— —————
Vi Toys Jon sm sale@vil.com
Full4All Jim Jon jjones@vil.com
The Toy Sto Kim How NULL
Full4All Jim Jon jjones@vil.com
Full4All Denise L. S dstep@vil.com
对组合查询结果排序
输入:
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_state IN ('LI','IN','MI')
UNION
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Full4All'
ORDER BY cust_name,cust_contact;
输出:
cust_name cust_contact cust_email
———— —————— —————
Full4All Denise L. S dstep@vil.com
Full4All Jim Jon jjones@vil.com
The Toy Sto Kim How NULL
Vi Toys Jon sm sale@vil.com
第15课 插入数据
1,不安全
INSERT INTO Customers
VALUES('10001','Tom','pudong','shanghai');
2,看似繁琐,但安全
INSERT INTO Customers(cust_id,cust_name,cust_address,cust_city)
VALUES('10001','Tom','pudong','shanghai');
第16课 更新和删除数据
1,更新,不要省略where
UPDATE Customers
SET cust_name = 'wangwu',
cust_city = 'sichuan'
WHERE cust_id = '10001';
UPDATE Customers
SET cust_name = NULL,
cust_city = 'sichuan'
WHERE cust_id = '10001';
2,删除,不要省略where
DELETE FROM Customers
WHERE cust_id = '10001';
第17课 创建和操纵表
创建表
CREATE TABLE Products(
prod_id CHAR(10) NOT NULL,
prod_name CHAR(254) NOT NULL,
prod_desc VARCHAR(1000) NULL,
quantity INTEGER NOT NULL DEFAULT 1,
prod_price DECIMAL(8,2) NOT NULL
);
更新表
ALTER TABLE Vendors
ADD vend_phone CHAR(20);
ALTER TABLE Vendors
DROP COLUMN vend_phone;
删除表
DROP TABLE Vendors;
重命名表
第18课 使用视图
视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。
12课中的:
SELECT cust_name,cust_contact
FROM Customers,Orders,OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num
AND prod_id = 'RGAN01';
把整个查询包装成一个名为ProductCustomers的虚拟表,则可以如下轻松检索出相同的数据
SELECT cust_name,cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';
ProductCustomers是一个视图,它不包含任何列或数据,包含的是一个查询。
为什么要是使用视图
- 重用sql语句
- 简化复杂的sql操作。在编写查询后,可以方便地重用它而不必知道其基本查询细节。
- 使用表的一部分而不是整个表。
- 保护数据。可以授予用户访问表的特定部分的权限而不是整个表的访问权限。
- 更改数据格式和表示。视图可返回与地层表的表示和格式不同的数据。
创建视图
CREATE VIEW,与CREATE TABLE 一样,CREATE VIEW只能用于创建不存在的视图。
视图重命名:
删除视图,可以使用DROP语句,其语法为DROP VIEW viewname;
覆盖(或更新)视图,必须先删除它,然后再重新创建。