一、了解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 BY
和WHERE子
句时,应该让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;
使用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 数值处理函数
九、汇总数据
9.1 聚集函数
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 分组和排序
10.5 SELECT子句顺序
表10-2 SELECT子句及其顺序
十一、使用子查询
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
语法时,必须使用RIGHT
或LEFT
关键字指定包括其所有行的表(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 协议 ,转载请注明出处!