MySQL

第1课 了解SQL

1.数据库(database):

数据库是以某种组织的方式存储的数据集合,就相当于一个文件柜。

2.数据库软件(DBMS):数据库管理系统

数据库是通过DBMS创建和操纵的容器

3.表:可以用来存储某种特定类型的数据。存储在表中的数据是同一种类型的数据或清单。

表中的名字是唯一的。

4.模式:关于数据库和表的布局及特性的信息。

5.表由列组成

6.数据类型的作用:

(1)数据类型限定了可存储在列中的数据种类

(2)数据类型还帮助正确的分类数据,并在优化磁盘使用方面起重要作用。

7.表中的任何列都可以作为主键,需要满足的条件:
(1)任意两行都不具有相同的主键值

(2)每一行都必选具有一个主键值(主键列不允许为控制NULL)

(3)主键列中的值不允许修改或更新

(4)主键值不能重复使用(如果某行从表中删除,他的主键不能赋给以后的新行)

最好的方式就是动手实践。

第2课 检索数据

Select语句:

它的用途是从一个或多个表中检索信息。

2.1检索单个列:

SELECT prod_name FROM Products;

2.2检索多个列:

就是在检索多个列的基础上,在列名之间加上逗号。

2.3检索所有列:

select * from 表名。

2.4检索不同的值:

关键字distinct 不同的 Select distinct 列名 from 表名

Distinct 关键字作用于所有列,不仅仅是跟在其后的那一列。

2.5限制结果:

MySQL:需要使用limit子句。

Select prod_name from products limit 5;
Select prod_name from products limit 5 offset 5;

Limit指定返会的行数 Offset指定从哪开始

第1个被检索的行是第0行,而不是第一行。

注释:注释使用–(两个连字符)嵌在行内

也可以使用(#)但这种形式有些DBMS不支持

第3课 排序检索数据

3.1排序数据

子句:

SQL语句由子句构成,有些子句时必须的,有些子句是可选的,一个子句通常由一个关键字加上所提供的数据组成

order by 子句的位置:

在指定一条order by子句时,应该保证它是select语句中最后一条子句。如果不是最后的子句,将会出错

3.2按多个列排序

要按多个列排血,只须指定这些列名,列名之间用逗号分开即可

select prod_id,prod_price,price_name 
from Products
ORDER BY prod_price,prod_name;

3.3 按照位置排序

order by不仅支持多个列排序,还支持按相对位置进行排序

select prod_id,prod_price,prod_name 
from products
order by 2,3;

3.4指定排序方向

数据排序默认为升序,如果要进行降序排序,必须指定DESC关键字

SELECT prod_id,prod_price,prod_name
FROM Products
ORDER BY prod_price DESC;

打算用多个列排序

SELECT prod_id, prod_price, prod_name 
FROM Products
ORDER BY prod_price DESC, prod_name;

DESC关键字只对应到直接位于其前面的列名

如果想在多个列上进行降序排序,必须对每一列指定DCES关键字

第4课 过滤数据

4.1 使用where子句

在select语句中,数据根据where子句中指定的搜索条件进行过滤

SELECT prod_name, prod_price 
FROM Products 
WHERE prod_price = 3.49;

where子句的位置

在同时使用ORDER BY 和WHERE子句的同时,应让ORDER BY 子句位于WHERE子句之后。

4.2 WHERE 子句操作符

操作符 说明

= 等于 > 大于

<> 不等于 >= 大于等于

!= 不等于 !> 不大于

< 小于 <= 小于等于

!< 不小于 BETWEEN 指定的两个值之间

IS NULL 为NULL值

4.2.1 检查单个值

SELECT prod_name,prod_price 
FROM Products
WHERE prod_pirce < 10;
SELECT prod_name,prod_price 
FROM Products
WHERE prod_pirce <= 10;

4.2.2不匹配检查

SELECT vend_id,prod_name
FROM Products 
WHERE vend_id <> 'DLL01';

单引号用来限定字符串

如果将值与字符串类型的列进行比较,就需要限定引号

用来与数值列进行比较就不需要使用引号

SELECT vend_id,prod_name
FROM Products 
WHERE vend_id != 'DLL01';

4.2.3范围值检查

要检查某个范围的值,可以使用BETWEEN操作符

SELECT prod_name, prod_pirce
FROM Products
WHERE prod_price BETWEEN 5 AND 10;

BETWEEN 匹配范围中所有的值,包括指定的开始值和结束值。

4.2.4 空值检查

NULL:无值,它与字段包含0、空字符串或仅仅包含空格不同

WHERE子句中的IS NULL子句可用来检查具有NULL值的列。

SELECT prod_name 
FROM Procducts
WHERE prod_price IS NULL;

第5课 高级数据过滤

5.1组合WHERE子句

SQL允许使用多个WHERE子句,即以AND子句或者OR子句的方式使用

操作符:
用来联结或改变WHERE子句中的子句的关键字,也称为逻辑操作符

5.1.1 AND操作符

and操作符通过不止一个列进行过滤

SELECT prod_id,prod_price,prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;

AND:用在WHERE子句中的关键字,用来指示检索满足所有给定条件的行

可以增加多个过滤条件,每个条件间都要使用AND关键字

5.1.2 OR操作符

它指示DBMS检索匹配任一条件的行

在第一个条件满足时,不管第二个条件是否满足,相应的行都将被检索出来

SELECT prod_id,prod_price,prod_name
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01';

OR: WHERE子句中使用的关键字,用来表示检索匹配任一给定条件的行。

5.1.3求值顺序

由于AND在求值过程中优先级更高

此问题的解决方法是使用圆括号对操作符进行明确的分组

SELECT prod_name, prod_price
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BR001')
	AND prod_price >= 10;

5.2 IN操作符

IN操作符用来指定条件范围,范围中的每一个条件都可以进行匹配。IN取一组由逗号分隔、括在圆括号中的合法值.

SELECT prod_name, prod_price 
FROM Products
WHERE vend_id IN('DLL01','BRS01')
ORDER BY prod_name;

IN: WHERE子句中用来指定要匹配值的清单的关键字,功能与OR相当

IN操作符的优点:

  1. IN操作符的语法更清楚,更直观
  2. 在与其他AND和OR操作符组合使用IN时,求值顺序更容易管理
  3. IN操作符一般比一组操作符执行得更快
  4. IN的最大的优点是可以包含其他SELECT语句,能够更动态的建立WHERE子句

5.3 NOT操作符

WHERE子句中的NOT操作符有且只有一个功能,那就是否定其后所跟的任何条件。

SELECT prod_name 
FROM Products
WHERE NOT vend_id = 'DLL01'
ORDER BY prod_name;

第6课 用通配符进行过滤

6.1 LIKE操作符

利用通配符,可以创建比较特定数据的搜索模式

通配符:用来匹配值的一部分的特殊字符。

搜索模式:由字面值、通配符或者两者组合构成的搜索条件

为了在搜索子句中使用通配符,必须使用LIKE操作符。

LIKE操作符知识DBMS,后跟的搜索模式利用通配符匹配而不是简单的相等匹配进行比较

谓词:操作符何时不为操作符? 它作为谓词的时候,LIKE是谓词而不是操作符

***通配符搜索只能用在文本字段(字符串), 非文本数据类型字符不能使用通配符搜索。

6.1.1 百分号(%)通配符

在搜索串中,%表示任何字符出现任意次数。

SELECT prod_id, prod_name 
FROM Products
WHERE prod_name LIKE 'Fish%';

区分大小写

通配符可在搜索模式中的任意位置使用,并且可以使用多个通配符。

SELECT prod_id, prod_name 
FROM Products
WHERE prod_name LIKE '%bean bag%';

'%'除了能够匹配一个或多个字符串,还能匹配0个字符,但不能匹配NULL。

6.1.2 下划线(_)通配符

下划线(_)只匹配单个字符

SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '% inch teddy bear';

6.2 使用通配符的技巧

  1. 不要过度使用通配符。如果其他操作符能达到相同的目的,用该使用其他操作符。
  2. 在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处。这样搜索起来是最慢的
  3. 仔细注意通配符的位置,如果放错地方,可能不会返回想要的数据。

第7课 创建计算字段

7.1计算字段

存储在表中的数据都不是应用程序所需要的。

我们需要直接从数据库中检索出转换、计算或格式化过的数据,而不是检索出数据,然后再在客户端应用程序中重新格式化

计算字段是运行时在SELECT语句内创建的

7.2拼接字段

此操作符可以用(+)或者两个竖杠(||)表示

在MySQL和MariaDB中,必须使用特殊的函数

SQL Sever 使用+号

DB2、Oracle、PostgreSQL和SQLite使用||

使用加号

SELECT vend_name + '(' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;

使用MySQL或MariaDB是使用的语句

SELECT CONCAT(vend_name, '(',vend_country,')')
FROM Vendors
ORDER BY vend_name;

去除空格RTRIM()函数

TRIM函数

RTRIM函数:去除字符串右边的空格

LTRIM函数:去除字符串左边的空格

TRIM函数:去除左右两边的空格

SELECT CONCAT(RTRIM(vend_name),'(',RTRIM(vend_country),')') 
FROM Vendors
ORDER BY vend_name;

使用别名

别名是一个字段或值的替换名。别名用AS关键字赋予

SELECT CONCAT(RTRIM(vend_name),'(',RTRIM(vend_country),')') 
AS vend_title
FROM Vendors
ORDER BY vend_name;

7.3执行算术计算

计算字段的另一常见的用途就是对检索出的数据进行算术计算

SELECT prod_id,
	quantity,
    item_price,
    quantity * item_price AS expanded_price
FROM ORDERITEMS
WHERE order_num = 20008;

第8课 使用函数处理数据

8.1 函数

SQL函数不是可移植的。

特定的SQL实现编写的代码在其他实现中可能不能用

8.2使用函数

大多数SQL实现支持以下类型的函数

  1. 用于处理文本字符串(如删除或填充值,转换值为大写或小写)的文本函数
  2. 用于在数值数据上进行算术操作(如返回绝对值,进行代数运算)的数值函数
  3. 用于处理日期和时间值并从这些值中提取特定成分(如返回两个日期之差,检查日期有效性)的日期和时间函数
  4. 用于生成美观好懂的输出内容的格式化函数(如用语言形式表达出日期,用货币符号和千分位表示金额)
  5. 返回DBMS正使用的特殊信息(如返回用户登录信息)的系统函数

8.2.1 文本处理函数

常用的文本处理函数:

函数 说明

**upper()**函数 转化为大写

left() 返回字符串左边的字符

length() 返回字符串的长度

lower() 将字符串转为小写

LTRIM() 去掉字符串左边的空格

RIGHT() 返回字符串右边的字符

RTRIM() 去掉字符串右边的空格

SUBSTR()或SUBSTRING() 提取字符串的组成部分

SOUNDEX() 返回字符串的SOUNDEX值

SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法

SELECT cust_name, cust_contact
FROM Customers
WHERE SOUNDEX(cust_contact) = SOUNDEX('Michael Green')

发音类似的单词也会被检索

8.2.2 日期和时间处理函数

MySQL可以使用名为YEAR()的函数从日期中提取年份

SELECT order_num 
FROM Orders
WHERE YEAR(order_date) = 2020;

8.2.3 数值处理函数

函数 说明

ABS() 返回一个数的绝对值

COS() 返回一个角度的余弦

EXP() 返回一个数的指数值

PI() 返回圆周率的值

SIN() 返回角度的正弦

SQRT() 返回一个数的平方根

TAN() 返回一个数的正切

第9课 汇总数据

9.1 聚集函数

使用聚集函数,SQL查询可以用于检索数据,比如:

  1. 确定表中行数
  2. 获得表中某些行的和
  3. 找出表列的最大值、最小值、平均值

函数 说明

AVG() 返回某列的平均值

COUNT() 返回某列的行数

MAX() 返回某列的最大值

MIN() 返回某列的最小值

SUM() 返回某列值之和

9.1.1 AVG()函数

AVG()通过对表中行数计数并计算其列值之和,求得该列的平均值

SELECT AVG(prod_price) AS avg_price
FROM Products;
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01'; 

AVG()函数忽略列值为NULL的行

9.1.2 COUNT()函数

count()函数有两种使用方法:

  1. 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值
  2. 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值
SELECT COUNT(*) AS num_cust
FROM Customers;
SELECT COUNT(cust_email) AS num_cust
FROM Customers;

9.1.3 MAX()函数

MAX()返回指定列中的最大值。MAX()要求指定列名

SELECT MAX(prod_price) AS max_price
FROM Products;

MAX()函数忽略列值为NULL的行

9.1.4 MIN()函数

MIN()函数返回指定列的最小值

SELECT MIN(prod_price) AS min_price 
FROM Products;

MIN()函数忽略列值为NULL的行

9.1.5 SUM()函数

SUM()函数用来返回指定列值的和(总计)

SELECT SUM(quantity) AS item_ordered
FROM OrderItems
WHERE order_num = 20005;
SELECT SUM(item_price * quantity) AS total_price
FROM ORDERItems
WHERE order_num = 20005;

SUM()函数忽略列值为NULL的行

9.2 聚集不同值

  1. 对所有行执行计算,指定ALL参数或不指定参数(因为ALL是默认行)
  2. 只包含不同的值,指定DISTINCT参数
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';

9.3组合聚集函数

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;

第10课 分组数据

10.1数据分组

使用分组可以将数据分为多个逻辑组。对每个组进行聚集计算。

10.2创建分组

分组是使用SELECT语句的GROUP BY子句建立的

SELECT vend_id,COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;

使用GROUP BY子句的一些规定:

  1. GROUP BY 子句可以含任意数目的列,因而可以对分组进行嵌套,更细致的进行数据分组
  2. 如果在GROUP BY子句中嵌套了分组,数据将在最后指定的分组上进行汇总。在建立分组时,指定的所有列都一起计算。
  3. GROUP BY 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式。不能别名。
  4. 大多数SQL实现不允许GROUP BY列带有长度可变的数据类型(如文本或备注型字段)
  5. 除聚集计算语句外,SELECT语句中的每一列都必须在GROUP BY子句中给出。
  6. 如果分组列中包含具有NULL值的行,则NULL将作为一个分组返回。如果列中有多行NULL值,他们将分为一组
  7. GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句中给出

10.3 过滤分组

WHERE过滤行,而HAVING过滤分组

SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;

过滤是基于分组聚集值,而不是特定行的值

WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。

WHERE 和HAVING子句可以同时使用

SELECT vend_id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id
HAVING COUNT(*) >= 2;

使用HAVING时应该结合GROUP BY子句,而WHERE子句用于标准的行级过滤。

10.4 分组和排序

order by GROUP BY

对产生的输出排序 对行分组,但输出可能不是分组的顺序

任意列都可以使用(甚至非选择的列也可以使用) 只能够使用选择列或者表达式列,而且必须使用每个选择列表达式

不一定需要 如果与聚集函数一起使用列(或表达式),则必须使用

提示:不要忘记ORDER BY

一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。千万不要依赖GROUP BY排序数据。

SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3;
SELECT order_num, count(*) AS items
FROM OrderItems
GROUP BY order_num
HAVING COUNT(*) >= 3
ORDER BY items, order_num;

10.5 SELECT 子句顺序

子句 说明 是否必须使用

SELECT 要返回的列或表达式 是

FROM 从中检索数据的表 仅在从表中选择数据时使用

WHERE 行级过滤 否

GROUP BY 分组说明 仅在按组计算聚集时使用

HAVING 组级过滤 否

ORDER BY 输出排序顺序 否

第11课 使用子查询

11.1子查询

SQL还允许创建子查询(subquery),即嵌套在其他查询中的查询。

11.2利用子查询进行过滤

SELECT order_num 
FROM OrderItems
WHERE prod_id = 'RGAN01';
SELECT cust_id 
FROM Orders
WHERE order_num IN(20007,20008)
SELECT cust_id 
FROM Orders
WHERE order_num IN (SELECT order_num
                   FROM OrderItems
                   WHERE prod_id = 'RGAN01');

在SELECT语句中,子查询总是从内先外处理。

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'));

作为子查询的SELECT语句只能查询单个列。企图检索多个列将返回错误

11.3 作为计算字段使用查询

SELECT cust_name, 
		cust_state,
		(SELECT COUNT(*) 
         FROM Orders
         WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

order是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每一个顾客执行一次。

比较Order表中cust_id和当前正从Customers表中检索的cust_id

第12课 联结表

12.1 联结

SQL最强大的功能之一就是能在数据查询的执行中联结。

联结是利用SQL的SELECT能执行的最中重要的操作

12.1.1关系表

关系表的设计就是要把信息分解成多个表,一类数据一个表。

各个表通过某些共同的值相关联(关系数据库)

关系数据库的好处:

  1. 信息不重复,不会浪费时间和空间
  2. 如果供应商信息变动,可以只更新Vendors表中的单个记录,相关表中的数据不用改动
  3. 由于数据不重复,数据显然是一致的,使得处理数据和生成报表更简单。

12.1.2 为什么使用联结

将数据分解为多个表能够更有效的存储,更方便的处理,并且可伸缩性更好。

如果数据存储在多个表中,如何检索数据

答案是: 使用联结 联结是一种机制,用来在一条SELECT语句中关联表,因此称为联结

12.2 创建联结

创建联结只需要指定要联结的所有表以及关联他们的方式即可

SELECT vend_name, prod_name, prod_price
FROM Vendors,Products
WHERE Vendors.vend_id = Products.vend_id;

12.2.1 WHERE 子句的重要性

在一条SELECT语句中联结几个表时,相应的关系是在运行中构造的。

笛卡儿积:由没有联结条件的表关系返回的结果为笛卡尔积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。

12.2.2 内联结

目前为止使用的联结称为等值联结,它基于两个表之间的相等测试。这种联结也称为内联结。

SELECT vend_name, prod_name, prod_price
FROM Vendors
INNER JOIN Products ON Vendors.vend_id = Products.vend_id;

12.2.3 联结多个表

SQL不限制一条SELECT语句中可以联结的表的数目。

创建联结的基本规则也相同。首先列出所有表,然后定义表之间的关系。

SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;

使用联结可以有效的替代子查询

子查询:

SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN(SELECT cust_id
                FROM Orders
                WHERE order_num IN(SELECT ordre_num 
                                  FROM OrderItems
                                  WHERE prod_id = 'RGAN01'));

联结:

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';

第13课 创建高级联结

13.1 使用表别名

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';

需要注意的是,表别名只在查询执行中使用。与列别名不一样,表别名不返回到客户端。

13.2 使用不同类型的联结

其他三种联结:自联结、自然联结、外联结

13.2.1 自联结

假如要给与Jim Jones同一公司的所有顾客发一封邮件。

方法1:子查询

SELECT cust_id, cust_name, cust_contact
FROM Customers
WHERE cust_name = (SELECT cust_name
                  FROM Customers
                  WHERE cust_contact = 'Jim Jones');

使用联结进行查询:

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';

此查询中需要的两个表实际上是相同的表,因此Customer表在FROM子句中出现了两次。

where首先联结两个表,然后按第二个表中的cust_contact过滤数据,返回所需要的数据。

用自联结而不用子查询

自联结通常作为外部语句,用来替代从相同表中检索数据的子查询语句。虽然最终的结果是相同的,但是许多DBMS处理联结远比处理子查询快得多。

13.2.2 自然联结

标准的联结返回所有数据,相同的列甚至多次出现。

自然联结排除多次出现,使每一个列只返回一次

自然联结要求你只能选择那些唯一的列,一般通过对一个表使用通配符(SELECT *),而对其他表的列使用明确的子集完成。

SELECT C.*,O.order_num,O.order_date,
		OI.prod_id,OI.quantity,OI.item_price
FROM Customer 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';

13.2.3 外联结

外联结:许多联结将一个表中的行与另一个表中的行相关联,但有时候需要包含没有关联行的那些行。

使用内联结检索所有顾客及其订单:

SELECT Customers.cust_id, Order.order_num
FROM Customers
INNER JOIN Orders ON Customers.cust_id = Orders.cust_id;

使用外联结检索没有订单顾客在内的所有顾客

SELECT Customer.cust_id, Orders.order_num
FROM Customers
LEFT OUTER JOIN Orders ON Customer.cust_id = Orders.cust_id;

与内联结关联两个表中的行不同的是,外联结还包括没有关联行的行。

在使用OUTER JOIN语法时,必须使用RIGHT或LEFT关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表)

SELECT Customer.cust_id,Orders.order_num
FROM Customers
RIGHT OUTER JOIN Order ON Customers.cust_id = Orders.cust_id;

左联结和右联结的唯一的差别就是所关联的表的顺序。

13.3使用带聚集函数的联结

要检索所有顾客及每个顾客所下的订单数

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;

聚集函数也可以方便地与其他联结一起使用

SELECT Customers.cust_id 
COUNT(Order.order_num) AS num_id
FROM Customers
LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;

13.4 使用联结和联结条件

  1. 注意所使用的联结类型,一般我们使用内联结,但使用外联结也有效
  2. 关于确切的联结语法,应该查看相关文档
  3. 保证使用正确的联结条件,否则会返回不正确的数据
  4. 应该总是提供联结条件,否则会得出笛卡尔积
  5. 在一个联结中可以使用多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试他们前分别测试每个联结。这会使故障排除更为简单。

第14课 组合查询

14.1 组合查询

多数SQL查询只包含从一个或多个表中返回数据的单挑SELECT语句

但是,SQL也允许执行多个查询(多个select语句),并将结果作为一个查询结果集返回。

这些组合查询通常称为并(union)或复合查询

主要有两种情况需要使用组合查询:

  1. 在一个查询中从不同的表中返回结构数据
  2. 对一个表执行多个查询,按一个查询返回数据

组合查询等价于多个WHERE条件

14.2 创建组合查询

可用UNION操作符来组合数条SQL查询。利用UNION,可以给出多条SELECT语句,将他们的结果组合成一个结果集。

14.2.1 使用UNION

使用UNION很简单,所要做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION

例:获取IL,In,Mi等州的所有顾客的报表,还想包括不管位于哪个州的所有的Fun4ALL

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN('IL','IN','MI');
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All'

组合:

SELECT cust_name, cust_contct, cust_email
FROM Customers
WHERE cust_state IN('IL','IN','MI')
UNION
SELECT cust_name, cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Fun4ALL';

14.2.2 UNION规则

  1. UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔
  2. UNION中的每一个查询必须包含相同的列、表达式或聚集函数(不过,没个列不需要以相同的次序列列出)
  3. 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含的转换的类型

14.2.3 包含或取消重复的行

使用UNION,重复的行会被自动取消,如果想返回所有的匹配行,可以使用UNION ALL而不是UNION

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN('IL','IN','MI')
UNION ALL
SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_name = 'Fun4All';

14.2.4 对组合查询结果排序

在用UNION组合查询时,只能使用一条ORDER BY子句,他必须位于最后一条SELECT语句之后

SELECT cust_name, cust_contact, cust_email
FROM Customers
WHERE cust_state IN('IL','IN','MI')
UNION
SELECT cust_name,cust_contact,cust_email
FROM Customers
WHERE cust_name = 'Fun4All'
ORDER BY cust_name,cust_contact;

ORDER BY子句似乎只是最后一条SELECT语句的组成部分,但实际上DBMS将用它来排序所有SELECT语句返回的所有结果。

第15课 插入数据

15.1数据的插入

INSERT用来将行插入到数据库表,插入的方式有几种形式:

  1. 插入完整的行
  2. 插入行的一部分
  3. 插入某些查询的结果

15.1.1 插入完整的行

使用INSERT语法,指定表名和插入到新行中的值

INSERT INTO Customers
VALUES(1000000006,'Toy Land','123 Any Street','New York','NY','11111','USA',NULL,NULL);

各列必须以他们在表定义中出现的次序填充

这种方式虽然简单,但并不安全,高度依赖于表中列的定义次序,还依赖于其容易获取的次序信息。即使可以得到这种次序信息,也不能保证各列在下一次表结构变动后保持完全相同的次序。

编写INSERT语句更安全的方法:

INSERT INTO Customers(cust_id,cust_name,cust_address,cust_city,cust_state,
                     cust_zip,cust_country,cust_contact,cust_email)
VALUES(100000006,'toy land','123 any street','new york','ny','11111','USA',NULL,NULL);

这种写法的有点在于即使表结构改变,这条INSERT语句任然能正确工作。

15.1.2 插入部分行

使用这种语法还可以省略列,这表示可以只给某些列提供值,给其他列不提供值。

INSERT INTO Customers(cust_id,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
VALUES(1000000006,'toy land','123 Any Street','New York','NY','1111','USA');

省略列:如果表的定义允许,则可以在INSERT操作中省略某些列。如果表中不允许有NULL值或者默认值,这时却省略了表中的值,DBMS就会产生错误信息,相应的行不能成功插入,省略的列必须满足以下条件:

  1. 该列定义为允许NULL值
  2. 在表定义中给出默认值。这表示如果不给出值,将使用默认值。

15.1.3 插入检索出的数据

INSERT还存在另一种形式,可以利用它将SELECT语句的结果插入表中,这就是所谓的INSERT SELECT

INSERT INTO Customers(cust_id,cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
SELECT cust_id,cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country
FROM CustNew;

INSERT SELECT 中SELECT语句可以包含WHERE子句,以过滤插入的数据。

插入多行

INSERT通常只插入一行。要插入多行,必须执行多个INSERT语句

INSERT SELECT 是一个例外,他可以用一条INSERT插入多行,不管SELECT语句返回多少行,都将被INSERT插入。

15.2 从一个表复制到另一个表

要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用CREATE SELECT语句(有的DBMS可以覆盖已存在的表,这依赖于所使用的具体的DBMS)

CREATE TABLE CustCopy AS SELECT * FROM Customers;

SQL Sever可以这样写:

SELECT * INTO CustCopy FROM Customers;

在使用SELECT INTO时,需要知道一些事情:

  1. 任何SELECT选项和子句都可以使用,包括WHERE 和GROUP BY
  2. 可利用联结从多个表插入数据
  3. 不管从多少个表中检索数据,数据都只能插入到一个表中

第16课 更新和删除数据

16.1 更新数据

更新数据可以使用UPDATE语句,有两种方式使用UPDATE:

  1. 更新表中的特定行
  2. 更新表中的所有行

基本的UPDATE语句的三部分组成:

  1. 要更新的表
  2. 列名和他们的新值
  3. 确定要更新哪些行的过滤条件
UPDATE Customers
SET cust_email = ‘kim@thetoystore.com’
WHERE cust_id = 10000000005;

更新多个列

UPDATE Customers
SET cust_contact = 'Sam Roberts',
    cust_email = 'sam@toyland.com'
WHERE cust_id = 1000000006;

在UPDATE语句中使用子查询

UPDATE语句中可以使用子查询,使得能用SELECT语句检索出的数据更新列数据

要删除某列的值,可以设置它为NULL

UPDATE Customers
SET cust_email = NULL
WHERE cust_id = 100000000005;

16.2 删除数据

要删除表中的数据,可以使用DELETE语句,有两种使用DELETE的方式:

  1. 从表中删除特定的行
  2. 从表中删除所有行
DELETE FROM Customers
WHERE cust_id = 1000000006;

如果省略WHERE子句,那么它将删除表中的每一个顾客。

DELETE不需要列名或通配符。DELETE删除整行而不是删除列。要删除指定的列,可以使用UPDATE语句

删除表的内容而不是表

DELETE语句从表中删除行,甚至是删除表中所有行。但是,DELETE不删除表本身

更快的删除

如果想从表中删除所有行,不要使用DELETE.可以使用TRUNCATE TABLE语句,他完成相同的工作,而速度更快。

16.3 更新和删除的指导原则

  1. 除非确实打算更新和删除每一行,否则绝对不要使用不带WHERE子句的UPDATE或DELETE语句
  2. 保证每一个表都有主键,尽可能像WHERE子句那样使用它
  3. 在UPDATE或DELETE语句使用WHERE子句前,应该先用SELECT进行测试,保证它过滤的是正确的记录,以防编写的WHERE子句不正确。

第17课 创建和操纵表

17.1 创建表

创建表的两种方式:

  1. 多数DBMS都具有交互式创建和管理数据库表的工具
  2. 表也可以直接用SQL语句操纵

用程序创建表,可以使用SQL的CREATE TABLE语句

17.1.1 表创建基础

利用CREATE TABLE 创建表,必须给出下列信息:

  1. 新表的名字,在关键字CREATE TABLE之后给出
  2. 表的列名和定义,用逗号分隔
  3. 有的DBMS还要求指定表的位置
CREATE TABLE Products
(
    prod_id          CHAR(10)           NOT NULL,
    vend_id          CHAR(10)           NOT NULL,
    prod_name        CHAR(254)          NOT NULL,
    prod_price       DECIMAL(8,2)       NOT NULL,
    prod_desc        VARCHAR(1000)      NULL
);

17.1.2 使用NULL值

NULL值就是没有值或缺值,允许NULL值的列也允许在插入行时不给出该列的值

CREATE TABLR Orders
(
    order_num   INTEGER  NOT NULL,
    order_date  DATETIME NOT NULL,
    cust_id    CHAR(10)  NOT NULL 
);
CREATE TABLE Vendors
(
    vend_id   CHAR(10)  NOT NULL,
    vend_name CHAR(50)  NOT NULL,
    vend_address CHAR(50) ,
    vend_city   CHAR(50)  ,
    vend_state   CHAR(50) ,
    vend_zip     CHAR(50) ,
    vend_country  CHAR(50) 
);

17.1.3 指定默认值

默认值在CREATE TABLE 语句中的定义中用关键字DEFAULT指定

CREATE TABLE OrderItems
(
    order_num    INTEGER  NOT NULL,
    order_item    INTEGER NOT NULL,
    prod_id     CHAR(10)   NOT NULL,
    quantity  INTEGER  NOT NULL DEFAULT 1,
    item_price DECIMAL(8,2)  NOT NULL
);

默认值经常用于日期或时间戳列

MySQL用户指定DEFAULT CURRENT_DATE()

17.2 更新表

更新表定义可以使用ALTER TABLE语句

使用ALTER时需要考虑的事情:

  1. 理想情况不要在表中包含数据时对其进行更新。应该在表的设计过程充分考虑未来的可能的需求,避免今后对表的结构做大改动
  2. 所有的DBMS都允许给现有的表增加列,不过对所有增加列的数据类型(以及NULL和DEFAULT的使用)有所限制
  3. 许多DBMS不允许删除或更改表中的列
  4. 多数DBMS允许重新命名表中的列
  5. 许多DBMS限制对已经填有数据的列进行更改,对未填有数据的列几乎没有限制

ALTER语法:

  1. 在ALTER TABLE 之后要更改的表名
  2. 列出要做哪些更改
ALTER TABLE Vendors
ADD vend_phone CHAR(10)
ALTER TABLE Vendors
DROP COLUMN vend_phone;

复杂的表结构更改一般需要手动删除过程,他涉及一下步骤

  1. 用新的列布局删除创建一个新表
  2. 使用insert into 语句从旧表中复制数据到新表
  3. 检验包含所需数据的新表
  4. 重命名旧表
  5. 用旧表原来的名字重命名新表
  6. 根据需要,重新创建触发器、存储过程、索引和外键

17.3 删除表

删除表(删除整个表而不是其内容)非常简单 使用DROP TABLE语句即可:

DROP TABLE CustCopy;

17.4 重命名表

每一个DBMS对表重命名的支持有所不同。

MYSQL用户使用RENAME语句

第18课 视图

18.1 视图

视图只包含使用时动态检索数据的查询

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是一个视图,作为视图,他不包含任何列或数据,包含的是一个查询

18.1.1为什么使用视图

  1. 重用SQL语句
  2. 简化复杂的SQL语句
  3. 使用表的一部分而不是整个表
  4. 保护数据
  5. 更改数据格式和表示

18.1.2 视图的规则和限制

常见的限制与规范:

  1. 视图必须唯一命名
  2. 对于可以创建的视图数目没有限制
  3. 创建视图,必须具有足够的访问权限
  4. 视图可以嵌套
  5. 许多DBMS禁止在视图查询中使用ORDER语句
  6. 有些DBMS要求对返回的所有列进行命名,如果列是计算字段,则需要使用别名
  7. 视图不能索引,也不能有关联的触发器或默认值
  8. 有些DBMS把视图作为只读查询,这表示可以从视图检索数据,但不能将数据写回底层表
  9. 有些DBMS允许创建这样的视图,他不能进行导致行不再属于视图的插入或更新

18.2创建视图

CREATE VIEW语句

18.2.1利用视图简化复杂的联结

CREATE VIEW ProductCustomers AS
SELECT cust_name, cust_contact, prod_id
FROM Customers, Orders, OrderItems
WHERE Customers.cust_id = Orders.cust_id
AND OrderItems.order_num = Orders.order_num;
SELECT cust_name. cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

18.2.2 用视图重新格式化检索出的数据

SELECT RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')'
AS vend_title
FROM Vendors
ORDER BY vend_name;
CREATE VIEW VendorLocations AS
SELECT RTRIM(vend_name) + '(' + RTRIM(vend_country) + ')'
AS vend_title
FROM Vendors;

18.2.3 用视图过滤不想要的数据

CREATE VIEW CustomerEMailList AS
SELECT cust_id, cust_name,cust_email
FROM Customers
WHERE cust_email IS NOT NULL;

18.2.4使用视图与计算字段

SELECT prod_id,
	quantity,
	item_price,
	quantity * item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;

转换为视图

CREATE VIEW OrderItemsExpanded AS
SELECT order_num,
prod_id,
quantity,
item_price,
quantity * item_price AS expanded_price
FROM OrderItems
SELECT * 
FROM OrderItemsExpanded
WHERE order_num = 20008;

第19课 使用存储过程

19.1 存储过程

存储过程就是为以后使用而保存的一条或多条SQL语句

19.2 为什么要使用存储过程

  1. 通过把处理封装在一个易用的单元中,可以简化复杂的操作
  2. 由于不要求反复建立一系列处理步骤,因而保证了数据的一致性
  3. 上一点的延伸就是防止错误。需要执行的步骤越多,出错的肯能性就越大。防止错误保证了数据的一致性
  4. 简化对变动的管理。如果表名、列名或业务逻辑有变化,那么只需要更改存储过程的代码。
  5. 上一点的延伸就是安全性。通过存储限制对基础数据的访问,减少了数据讹误的机会。
  6. 因为存储过程通常以编译过的形式存储,所以DBMS处理命令所需的工作量少,提高了性能。
  7. 存在一些只能用在单个请求中的SQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。

存储过程:简单、安全、高性能

19.3 执行存储过程

执行存储过程的SQL语句很简单,即EXECUTE.

EXECUTE接受存储过程名和需要传递给它的任何参数

EXECUTE AddNewProduct('JTSO1',
                      'Stuffed Eiffel Tower',
                      6.49,
                      'Plush stuffed toy with the text La Tour Eiffel in red white and blue'
);

存储过程所完成的工作:

  1. 验证传递的数据,保证所有4个参数都有值
  2. 生成用作主键的唯一ID
  3. 将新产品插入Products表,在合适的列中存储生成的主键和传递的数据

19.4 创建存储过程

sql sever版本

CREATE PROCEDURE MailingListCount
AS
DECLARE @cnt INTEGER
SELECT @cnt = COUNT(*)
FROM Customers
WHERE NOT cust_email IS NULL;
RETURN @cnt;

DECLARE语句声明了一个名为@cnt的局部变量,然后在SELECT语句中使用这个变量,让它包含COUNT()函数返回的值

最后,用RETURN @cnt语句将计数返回给调用程序

调用SQL Sever的例子

DECLARE @ReturnValue INT
EXECUTE @ReturnValue = MailingListCount;
SELECT @ReturnValue;

声明了一个变量来保存存储过程返回的任何值,然后执行存储过程,再使用SELECT语句显示返回的值。

第20课 管理事务处理

如何使用COMMIT和ROLLBACK语句管理事务处理

20.1 事务处理

事务处理是一种机制,用来管理必须成批执行的SQL操作,保证数据库不包括不完整的操作结果。

关于事务处理的几个术语:

  1. 事务(transaction)指一组SQL语句
  2. 回退(rollback)指撤销指定SQL语句的过程
  3. 提交(commit)指将未存储的SQL语句结果写入数据库表
  4. 保留点(savepoint)指事务处理中设置的临时占位符,可以对它发布回退

可以回退哪些语句

事务处理用来管理INSERT UPDATE DELETE语句。不能回退SELECT CREATE DROP 语句

20.2 控制事务处理

管理事务的关键在于将SQL语句组分解为逻辑块,并明确规定数据何时该回退,何时不该回退。

Mysql代码

START TRANSACTION
COMMIT TRANSACTION

通常COMMIT用于保存更改,ROLLBACK用于撤销

20.2.1 使用ROLLBACK

DELETE FROM Orders;
ROLLBACK;

sql的ROLLBACK命令用来回退SQL语句

20.2.2 使用COMMIT

一般的SQL语句都是针对数据库表直接执行和编写的。这就是所谓的隐式提交,即提交操作是自动进行的。

BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION

20.2.3 使用保留点

要支持回退部分事务,必须在事务处理块中的合适位置放置占位符,如果需要回退,就可以回退到某个占位符。这个占位符被称为保留点

创建保留点,MYSQL可以使用SAVEPOINT语句

SAVEPOINT delete1;

每个保留点都要去能够标识它的唯一名字,以便在回退的时候,DBMS知道回退到何处。

ROLLBACK TO delete1;

第21课 使用游标

21.1 游标

需要在检索出来的行中前进或后退一行或多行,这就是游标的用途所在。

游标是一个存储在DBMS服务器上的数据库查询,他不是一条SELECT语句,而是被该语句检索出来的结果集。

游标常见的选项和特征:

  1. 能够标记游标为只读,使数据能读取,但不能更新和删除
  2. 能控制可以执行的定向操作
  3. 能标记某些列为可编辑的,某些列位不可编辑的
  4. 规定范围。使游标对创建它的特定请求或对所有请求可访问
  5. 指示DBMS对检索出的数据进行复制,使数据在游标打开和访问期间不变化

21.2 使用游标

21.2.1创建游标

使用DECLARE语句创建表,DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子句。

mysql版本

DECLARE CustCursor CURSOR
FOR
SELECT * FROM Customers
WHERE cust_email IS NULL;

21.2.2 使用游标

使用OPEN CURSOR 语句打开游标

OPEN CURSOR CustCursor

用FETCH语句访问游标数据

DECLARE TYPE CustCursor IS REF CURSOR
RETURN Customers % ROWTYPE;
DECLARE CustRecord Customers%ROWTYPE
BEGIN 
OPEN CustCursor;
FETCH CustCursor INTO CustRecord;
CLOSE CustCursor;
END;

21.2.3 关闭游标

CLOSE CustCursor

第22课 高级SQL特性

22.1 约束

管理如何插入或处理数据库的规则

22.1.1 主键

表中任意列只要满足以下条件,都可以用于主键:

  1. 任意两行的主键值都不相同
  2. 每行都具有一个主键值(即列中不允许NULL值)
  3. 包含主键值的列从不修改或更新
  4. 主键值不能重用。如果从表中删除某行,其主键值不分配给新行。
CREATE TABLE Vendors(
vend_id   CHAR(10)    NOT NULL PRIMARY KEY,
vend_name  CHAR(10)    NOT NULL,
vend_address CHAR(10)    NULL
);
ALTER TABLE Vendors
ADD CONSTRAINT PRIMARY KEY(vend_id);

这里定义相同的列为主键,但使用的是CONSTRAINT语法。

22.1.2 外键

外键是表中的一列,其值必须列在另一表的主键中

CREATE TABLE Orders
(
    order_num   INTEGER NOT NULL PRIMARY KEY,
    order_date DATETIME NOT NULL,
    cust_id  ChAR(10)    NOT NULL REFERENCES Customers(cust_id)
);

其中的表定义使用了REFERENCES关键字,他表示cust_id中任何值都必须是Customers表中的cust_id中的值

相同的工作也可以在ALTER TABLE语句中用CONSTRAINT语法来完成:

ALTER TABLE Orders
ADD CONSTRAINT
FOREIGN KEY(cust_id) REFERENCES Customers(cust_id);

外键有助于防止意外删除

21.1.3 唯一约束

唯一约束用来保证一列中的数据是唯一的

与主键的区别:

  1. 表可以包含多个唯一约束,但每个表只允许一个主键
  2. 唯一约束列可以包含NULL值
  3. 唯一约束列可以修改或更新
  4. 唯一约束列的值可以重复使用
  5. 与主键不一样,唯一约束不能用来定义外键

使用UNIQUE关键字

22.1.4 检查约束

  1. 检查最小值或最大值
  2. 指定范围
  3. 只允许特定的值
CREATE TABLE OrderItems(
order_num  INTEGER NOT NULL,
    order_item INTEGER NOT NULL,
    prod_id  CHAR(10)   NOT NULL,
    quantity  INTEGER   NOT NULL CHECK(quantity > 0),
    item_price  MONEY NOT NULL
);

检查名为gender的列只包含M或F

ADD CONSTRAINT CHECK(gender LIKE '[MF]');

22.2 索引

索引用CREATE INDEX语句创建

CREATE INDEX prod_name_ind
ON Products(prod_name);

索引必须唯一命名

索引名为prod_name_ind ON用来指定被索引的表,而索引中包含的列在表名后的圆括号中给出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值