一、了解SQL

1.1 数据库基础

1.1.1 数据库

数据库是一个以某种有组织的方式存储的数据集合。
数据库软件应称为数据库管理系统(DBMS)。数据库是通过DBMS创建和操纵的容器。

1.1.2 表

表(table)是某种特定类型数据的结构化清单,存储在表中的数据是同一种类型的数据或清单。

1.1.3 列和数据类型

列(column):表中的一个字段。所有表都是由一个或多个列组成的。
数据类型限定了可存储在列中的数据种类,对于优化磁盘使用方面起到重要作用。

1.1.4 行

表中的数据总是按行存储的。

1.1.5 主键

唯一标识表中每行的这个列(或这几列)称为主键。
• 任意两行都不具有相同的主键值;
• 每一行都必须具有一个主键值(主键列不允许NULL值);
• 主键列中的值不允许修改或更新;
• 主键值不能重用(如果某行从表中删除,它的主键不能赋给以后的新行)。

1.2 什么是SQL

SQL是结构化查询语言(Structured Query Language)的缩写。SQL语句不区分大小写,但是建议SQL关键字大写,表名、列名等小写。

二、检索数据

2.1 检索单列数据

SELECT prod_name
FROM Products;

Products表中检索一个名为prod_name的列。

2.2 检索多个列

SELECT prod_id, prod_name, prod_price
FROM Products;

SELECT关键字后给出多个列名,列名之间必须以逗号分隔。

2.3 检索所有列

SELECT *
FROM Products;

2.4 检索不同的值

SELECT DISTINCT vend_id
FROM Products;

使用DISTINCT关键字,指示数据库只返回不同的值。

2.5 限制结果

SELECT TOP 5 prod_name
FROM Products;

只检索前5行数据

SELECT prod_name
FROM Products
LIMIT 5 OFFSET 5;

LIMIT 5 OFFSET 5指示MySQL等DBMS返回从第5行起的5行数据

第二个数字是指从哪儿开始,第一个数字是检索的行数。

提示:第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1会检索第2行,而不是第1行。

2.6 使用注释

注释使用--(两个连字符)嵌在行内。
在一行的开始处使用#,这一整行都将作为注释。
注释从\*开始,到*/结束,/*和*/之间的任何内容都是注释。

三、排序检索数据

3.1 排序数据

SELECT prod_name
FROM Products
ORDER BY prod_name;

注意:在指定一条ORDER BY子句时,应该保证它是SELECT语句中最后一条子句。

3.2 按多个列排序

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

3.3 按列位置排序

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;

DESC关键字只应用到直接位于其前面的列名,如果想在多个列上进行降序排序,必须对每一列指定DESC关键字

四、过滤数据

4.1 使用WHERE子句

SELECT prod_name, prod_price
FROM Products
WHERE prod_price = 3.49;

提示:在同时使用ORDER BYWHERE子句时,应该让ORDER BY位于WHERE之后。

4.2 WHERE子句操作符

4.2.1 检查单个值

SELECT prod_name, prod_price
FROM Products
WHERE prod_price < 10;

4.2.2 不匹配检查

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

4.2.3 范围值检查

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

4.2.4 空值检查

SELECT prod_name
FROM Products
WHERE prod_price IS NULL;

五、高级数据过滤

5.1 组合WHERE子句

5.1.1 AND操作符

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

5.1.2 OR操作符

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

5.1.3 求值顺序

SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符。可以使用圆括号对操作符进行明确分组。

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

5.2 IN操作符

IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。

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

检索由供应商DLL01和BRS01制造的所有产品。等同于OR操作符。

5.3 NOT操作符

用来否定其后条件的关键字。

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

六、用通配符进行过滤

6.1 LIKE操作符

解决之前过滤方法无法解决的问题,

6.1.1 百分号%通配符

%表示任何字符出现任意次数。

如:我们希望从 “Persons” 表中选取居住在包含 “lon” 的城市里的人:

SELECT * FROM Persons
WHERE City LIKE '%lon%'

再如:我们希望从 “Persons” 表中选取居住在以 “g” 结尾的城市里的人:

SELECT * FROM Persons
WHERE City LIKE '%g'

6.1.2 下划线_通配符

下划线的用途与%一样,但它只匹配单个字符,而不是多个字符。

6.1.3 方括号[ ]通配符

方括号[]通配符用来指定一个字符集,它必须匹配指定位置(通配符的位置)的一个字符

SELECT cust_contactFROM Customers
WHERE cust_contact LIKE '[JM]%'
ORDER BY cust_contact;

此通配符可以用前缀字符^(脱字号)来否定

6.2 使用通配符的技巧

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

七、创建计算字段

7.1 计算字段

计算字段并不实际存在于数据库表中。计算字段是运行时在SELECT语句内创建的。

7.2 拼接字段

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

去掉空格,使用SQL的RTRIM()函数来完成。

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

大多数DBMS都支持RTRIM()(正如刚才所见,它去掉字符串右边的空格)、LTRIM()(去掉字符串左边的空格)以及TRIM()(去掉字符串左右两边的空格)。

别名用AS关键字赋予

SELECT 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.1 函数

SQL函数不是可移植的。这表示为特定SQL实现编写的代码在其他实现中可能不正常。

8.2 使用函数

8.2.1 文本处理函数

UPPER()将文本转换为大写

SELECT vend_name, UPPER(vend_name) AS vend_name_upcase
FROM Vendors
ORDER BY vend_name;

image-20210530154929203

使用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) = 2012;

8.2.3 数值处理函数

image-20210530154906313

九、汇总数据

9.1 聚集函数

image-20210530155025376

SELECT AVG(prod_price) AS avg_price
FROM Products;

说明:这些函数都忽略列值为NULL的行。

使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。

使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值

9.2 聚集不同值

使用AVG()函数返回特定供应商提供的产品的平均价格,但使用了DISTINCT参数,因此平均值只考虑各个不同的价格。

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

9.3 组合聚集函数

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.1 数据分组

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

10.2 创建分组

GROUP BY子句指示DBMS分组数据,然后对每个组而不是整个结果集进行聚集。

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

GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。

10.3 过滤分组

WHERE过滤行,而HAVING过滤分组。

提示:HAVING支持所有WHERE操作符

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

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

10.4 分组和排序

image-20210530155211680

10.5 SELECT子句顺序

表10-2 SELECT子句及其顺序

image-20210530155228648

十一、使用子查询

11.1 利用子查询进行过滤

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

作为子查询的SELECT语句只能查询单个列。

11.2 作为计算字段使用子查询

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

十二、联结表

12.1 联结

12.1.1 关系表

关系表的设计就是要把信息分解成多个表,一类数据一个表。各表通过某些共同的值互相关联(所以才叫关系数据库)。

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子句的重要性

没有WHERE子句,第一个表中的每一行将与第二个表中的每一行配对,而不管它们逻辑上是否能配在一起。

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 联结多个表

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;

十三、创建高级联结

13.1 使用表别名

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

13.2 使用不同类型的联结

13.2.1 自联结

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

13.2.2 自然联结

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

13.2.3 外联结

联结包含了那些在相关表中没有关联行的行。这种联结称为外联结。

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

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

左外联接的结果集包括LEFT OUTER子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。

全外联结(full outer join),它检索两个表中的所有行并关联那些可以关联的行。(MySQl不支持

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;

13.4 使用联结和联结条件

在总结讨论联结的这两课前,有必要汇总一下联结及其使用的要点。
• 注意所使用的联结类型。一般我们使用内联结,但使用外联结也有效。
• 关于确切的联结语法,应该查看具体的文档,看相应的DBMS支持何种语法(大多数DBMS使用这两课中描述的某种语法)。
• 保证使用正确的联结条件(不管采用哪种语法),否则会返回不正确的数据。
• 应该总是提供联结条件,否则会得出笛卡儿积。
• 在一个联结中可以包含多个表,甚至可以对每个联结采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前分别测试每个联结。这会使故障排除更为简单。

十四、 组合查询

14.1 组合查询

主要有两种情况需要使用组合查询:
1. 在一个查询中从不同的表返回结构数据;
2. 对一个表执行多个查询,按一个查询返回数据。

14.2 创建组合查询

可用UNION操作符来组合数条SQL查询。

14.2.1 使用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
FROM Customers
WHERE cust_name = 'Fun4All';

14.2.2 UNION规则

  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔(因此,如果组合四条SELECT语句,将要使用三个UNION关键字)。
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过,各个列不需要以相同的次序列出)。
  • 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含转换的类型(例如,不同的数值类型或不同的日期类型)。

14.2.3 包含或取消重复的行

如果想返回所有的匹配行,可使用UNION ALL而不是UNION。

14.2.4 对组合查询结果排序

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;

实际上DBMS将用它来排序所有SELECT语句返回的所有结果

十五、插入数据

15.1 数据插入

使用INSERT语句可能需要客户端/服务器DBMS中的特定安全权限。在你试图使用INSERT前,应该保证自己有足够的安全权限。

15.1.1 插入完整的行

INSERT INTO Customers(cust_id,
	cust_name,
	cust_address,
	cust_city,
	cust_state,
	cust_zip,
	cust_country,
	cust_contact,
	cust_email)
VALUES('1000000006',
	'Toy Land',
	'123 Any Street',
	'New York',
	'NY',
	'11111',
	'USA',
	NULL,
	NULL);

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',
	'11111',
	'USA');

15.1.3 插入检索出的数据

利用它将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;

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

要将一个表的内容复制到一个全新的表(运行中创建的表),可以使用SELECT INTO语句。

SELECT *
INTO CustCopy
FROM Customers;

十六、更新和删除数据

16.1 更新数据

UPDATE Customers
SET cust_email = 'kim@thetoystore.com'
WHERE cust_id = '1000000005';

更新多列:

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

16.2 删除数据

DELETE FROM Customers
WHERE cust_id = '1000000006';

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

十七、创建和操纵表

17.1 创建表

17.1.1 表创建基础

利用CREATE TABLE创建表,必须给出下列信息:
• 新表的名字,在关键字CREATE TABLE之后给出;
• 表列的名字和定义,用逗号分隔;
• 有的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值

CREATE TABLE Orders
(
	order_num INTEGER NOT NULL,
	order_date DATETIME NOT NULL,
	cust_id CHAR(10) NOT NULL
);

只有不允许NULL值的列可作为主键,允许NULL值的列不能作为唯一标识。

17.1.3 指定默认值

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

17.2 更新表

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

ALTER TABLE Vendors
ADD vend_phone CHAR(20);

17.3 删除表

DROP TABLE CustCopy;

删除表没有确认,也不能撤销,执行这条语句将永久删除该表。

17.4 重命名表

RENAME

十八、使用视图

18.1 视图

视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。

18.1.1 为什么使用视图

视图的一些常见应用。
• 重用SQL语句。
• 简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必知道其基本查询细节。
• 使用表的一部分而不是整个表。
• 保护数据。可以授予用户访问表的特定部分的权限,而不是整个表的访问权限。
• 更改数据格式和表示。视图可返回与底层表的表示和格式不同的数据

18.2 创建视图

视图用CREATE VIEW语句来创建。REATE VIEW只能用于创建不存在的视图。

删除视图,可以使用DROP语句,其语法为

DROP VIEW viewname

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;

检索订购了产品RGAN01的顾客,可如下进行:

SELECT cust_name, cust_contact
FROM ProductCustomers
WHERE prod_id = 'RGAN01';

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

假设经常需要这个格式的结果。我们不必在每次需要时执行这种拼接,而是创建一个视图,使用它即可。

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 使用视图与计算字段

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

十九、使用存储过程

19.1 存储过程

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

19.2 为什么要使用存储过程

  • 通过把处理封装在一个易用的单元中,可以简化复杂的操作(如前面例子所述)。
  • 由于不要求反复建立一系列处理步骤,因而保证了数据的一致性。如果所有开发人员和应用程序都使用同一存储过程,则所使用的代码都是相同的。
  • 这一点的延伸就是防止错误。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。
  • 简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,那么只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。
  • 这一点的延伸就是安全性。通过存储过程限制对基础数据的访问,减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。
  • 因为存储过程通常以编译过的形式存储,所以DBMS处理命令的工作较少,提高了性能。
  • 存在一些只能用在单个请求中的SQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。

19.3 执行存储过程

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

分析▼
这里执行一个名为AddNewProduct的存储过程,将一个新产品添加到Products表中。AddNewProduct有四个参数,分别是:供应商
ID(Vendors表的主键)、产品名、价格和描述。这4个参数匹配存储过程中4个预期变量(定义为存储过程自身的组成部分)。此存储过程将新行添加到Products表,并将传入的属性赋给相应的列。

19.4 创建存储过程

CREATE PROCEDURE MailingListCount (
	ListCount OUT INTEGER
) 
IS
v_rows INTEGER;
BEGIN
	SELECT COUNT(*) INTO v_rows
	FROM Customers
	WHERE NOT cust_email IS NULL;
	ListCount := v_rows;
END;

二十、管理事务处理

20.1 事务处理

确保成批的SQL操作要么完全执行,要么完全不执行,来维护数据库的完整性。
在使用事务处理时,有几个反复出现的关键词。下面是关于事务处理需要知道的几个术语:

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

20.2 控制事务处理

START TRANSACTION

20.2.1 使用ROLLBACK

SQL的ROLLBACK命令用来回退(撤销)SQL语句

20.2.2 使用COMMIT

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

20.2.3 使用保留点

设置保留点:

SAVEPOINT delete1;

回滚到保留点:

ROLLBACK TO delete1;

完整示例:

BEGIN TRANSACTION
INSERT INTO Customers(cust_id, cust_name)
VALUES('1000000010', 'Toys Emporium');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20100,'2001/12/1','1000000010');
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 1, 'BR01', 100, 5.49);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20100, 2, 'BR03', 100, 10.99);
IF @@ERROR <> 0 ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION

二十一、使用游标

21.1 游标

游标(cursor)是一个存储在DBMS服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。
常见的一些选项和特性如下。

  • 能够标记游标为只读,使数据能读取,但不能更新和删除。
  • 能控制可以执行的定向操作(向前、向后、第一、最后、绝对位置、相对位置等)。
  • 能标记某些列为可编辑的,某些列为不可编辑的。
  • 规定范围,使游标对创建它的特定请求(如存储过程)或对所有请求可访问。
  • 指示DBMS对检索出的数据(而不是指出表中活动数据)进行复制,使数据在游标打开和访问期间不变化。
  • 游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。

21.2 使用游标

21.2.1 创建游标

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

二十二、高级SQL特性

22.1 约束

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

22.1.1 主键

表中任意列只要满足以下条件,都可以用于主键:
• 任意两行的主键值都不相同。
• 每行都具有一个主键值(即列中不允许NULL值)。
• 包含主键值的列从不修改或更新。(大多数DBMS不允许这么做,但如果你使用的DBMS允许这样做,好吧,千万别!)
• 主键值不能重用。如果从表中删除某一行,其主键值不分配给新行。

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)

在定义外键后,DBMS不允许删除在另一个表中具有关联行的行。例如,不能删除关联订单的顾客。删除该顾客的唯一方法是首先删除相关的订单。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Spring中用到的设计模式 Previous
Git常用指令 Next