外键
确保在
cities
表中有相应项之前任何人都不能在
weather
表中插入行,叫做维持数据的引用完整性。
在过分简化的数据库系统中,可以通过检查
cities
表中是否有匹配的记录存在,然后决定应该接受还是
拒绝即将插入
weather
表的行。这种方法存在一些问题且并不方便,于是
KingbaseES
提出新的解决方案:
新的表定义如下:
CREATE TABLE cities (
city varchar(
80
) primary key,
location point
);
CREATE TABLE weather (
city
varchar(
80
) references cities(city),
temp_lo
int
,
temp_hi
int
,
prcp
real,
date
date
);
尝试插入一个非法的记录:
INSERT INTO weather VALUES (
'Berkeley'
,
45
,
53
,
0.0
,
'1994-11-28'
);
ERROR: insert or update on table "weather" violates foreign key constraint
"weather_city_fkey"
DETAIL: Key (city)=(Berkeley) is not present in table "cities".
外键的行为可以很好地根据应用来调整。可参考
数据定义
中相关信息。正确使用外键将提高数据库的应用
质量,建议用户熟练掌握外键的使用。
事务
事务是指访问并可能更新数据库中各种数据项的一个程序执行单元。在关系数据库中,一个事务可以是一条
SQL
语句,一组
SQL
语句或整个程序。它将多个步骤捆绑成了一个单一的、要么全完成要么全不完成的操
作。步骤之间的中间状态对于其他并发事务是不可见的,如果产生错误导致事务不能完成,则其中任何一个
步骤都不会对数据库造成实质影响。
在一个保存着多个客户账户余额和支行总存款额的银行数据库中。如需记录一笔从
Alice
的账户到
Bob
的账
户的额度为
100.00
美元的转账,
SQL
命令为:
UPDATE accounts SET balance
=
balance
-
100.00
WHERE name
=
'Alice'
;
UPDATE branches SET balance
=
balance
-
100.00
(continues on next page)
(continued from previous page)
WHERE name
=
(SELECT branch_name FROM accounts WHERE name
=
'Alice'
);
UPDATE accounts SET balance
=
balance
+
100.00
WHERE name
=
'Bob'
;
UPDATE branches SET balance
=
balance
+
100.00
WHERE name
=
(SELECT branch_name FROM accounts WHERE name
=
'Bob'
);
在上述操作涉及到对数据库的多个独立更新,银行职员希望确保这些更新要么全部发生,或者全部不发生。
不能出现由于系统错误导致
Bob
收到
100
美元而
Alice
并未被扣款的情况,或者
Alice
被扣款而
Bob
未收到
转账的情况。因此,需要保证在操作中途发生错误时,已经执行的步骤不会产生效果。这就是事务的原子性
(一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做)。
银行职员同样希望能保证一旦一个事务被数据库系统完成并认可,它就被永久地记录下来且即便其后发生崩
溃也不会被丢失。例如,需要永久保留
Bob
的现金提款记录。
一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。当
多个事务并发运行时,每一个事务都不能看到其他事务未完成的修改。例如,如果一个事务为统计所有支行
的余额,它不会只包括
Alice
的支行的扣款而不包括
Bob
的支行的存款,或者反之。所以事务的全做或全不
做并不只体现在它们对数据库的持久影响,也体现在它们发生时的可见性。一个事务所做的更新在它完成之
前对于其他事务是不可见的,而之后所有的更新将同时变得可见。
在
KingbaseES
中,使用
SQL
命令
BEGIN
和
COMMIT
开启一个事务:
BEGIN;
UPDATE accounts SET balance
=
balance
-
100.00
WHERE name
=
'Alice'
;
--
etc etc
COMMIT;
在事务执行中若不想提交(若
Alice
的余额不足),可以以
ROLLBACK
命令代替
COMMIT
命令,当前的更新
即被取消。
实际上,
KingbaseES
将每一个
SQL
语句都作为一个事务来执行。如果未发出
BEGIN
命令,则每个独立的语
句都会被加上一个隐式的
BEGIN
以及
COMMIT
。一组被
BEGIN
和
COMMIT
包围的语句也被称为一个事
务块。
Note:
由于某些客户端库会自动发出
BEGIN
和
COMMIT
命令,因此可能会在不被告知的情况下得到事务
块的效果。具体情况请查看对应接口文档。
可以利用保存点来以更细的粒度控制事务中的语句。保存点允许有选择性地放弃事务的一部分而提交剩下的
部分。使用
SAVEPOINT
定义一个保存点后,可以在必要时利用
ROLLBACK TO
回滚到该保存点。该事务
中位于保存点和回滚点之间的数据库修改都会被放弃,但是早于该保存点的修改则会被保存。
在回滚到保存点之后,它的定义依然存在,因此可以多次回滚到它。反之,如果确定不再需要回滚到特定的
保存点,它可以被释放以便系统释放一些资源。不管是释放保存点还是回滚到保存点都会释放定义在该保存
点之前的所有其他保存点。
由于上述操作发生在一个事务块内,因此这些操作对于其他数据库会话来说都不可见。当提交整个事务块
时,被提交的动作将作为一个单元变得对其他会话可见,而被回滚的动作则永远不会变得可见。
在银行数据库中,假设从
Alice
的账户扣款
100
美元,然后存款到
Bob
的账户,但直至最终才发现应该存在
Wally
的账户中。可以通过使用保存点来做这件事:
BEGIN;
UPDATE accounts SET balance
=
balance
-
100.00
WHERE name
=
'Alice'
;
SAVEPOINT my_savepoint;
(continues on next page)
(continued from previous page)
UPDATE accounts SET balance
=
balance
+
100.00
WHERE name
=
'Bob'
;
--
oops
...
forget that
and
use Wally
's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance
=
balance
+
100.00
WHERE name
=
'Wally'
;
COMMIT;
ROLLBACK TO
是重新控制一个由于错误被系统置为中断状态的事务块的唯一的途径,而不是完全回滚它并
重新启动