跳到主要内容

修改数据

本部分描述如何修改数据库中的数据。修改数据与查询数据有本质区别。查询数据涉及检查表的内容。修改数据涉及更改表的内容。

修改数据库中的数据

下列语句修改数据:

  • DELETE
  • INSERT
  • MERGE
  • UPDATE

当与更高级的 SELECT 语句相比时,虽然这些 SQL 语句相对简单,但由于它们更改数据库的内容,因此请小心使用它们。

如果在查询期间系统硬件或软件出现故障,请考虑会发生什么。即使对应用程序的影响是严重的,也不会破坏数据库自身。然而,如果正在进行修改时系统发生故障,则数据库的状态就不确定了。显然,处于不确定状态的数据库具有深远的影响。在数据库中删除、插入或更新行之前,请询问自己下列问题:

  • 用户对数据库及其表的访问是否安全。即,是否将有限的数据库和表级别权限授予特定用户?
  • 修改了的数据是否保持数据库现有的完整性?
  • 系统的状况是否使其对可能导致系统或硬件故障的外部事件具有相对较强的免疫力?

如果对这些问题不能都回答“是”,也不用担心。对所有这些问题的解决方案都内建在 GBase 8s 数据库服务器内。在对修改数据的语句进行描述之后,这部分讨论这些解决方案。

删除行

DELETE 从表中移除任何行或行的组合。在提交该事务之后,您不可恢复删除了的行。(在 中断了的修改 之下讨论事务。现在,请将事务与语句看做是一回事。)

当删除一行时,您还必须小心地删除其值依赖于该删除了的行的其他表的任何行。如果数据库强制执行引用约束,则您可使用 CREATE TABLE 或 ALTER TABLE 语句的 ON DELETE CASCADE 选项来允许从与另一表的关系中的一个表进行级联删除。要获取关于引用约束和 ON DELETE CASCADE 选项的更多信息,请参阅 引用完整性。

删除表的所有行

DELETE 语句指定表并通常包含 WHERE 子句,该子句指定要从表中移除的一行或多行。如果省略 WHERE 子句,则删除所有行。

重要: 请不要执行下列语句。

DELETE FROM customer;

您可编写带有或不带 FROM 关键字的 DELETE 语句。

DELETE customer;

由于这些 DELETE 语句不包含 WHERE 子句,因此从 customer 表删除所有行。如果您尝试使用 DB-Access 菜单选项来进行无条件的删除,则程序会警告您并要求确认。然而,从程序之内执行无条件的 DELETE 可在不发出警告的情况下发生。

如果想要从名为 from 的表中删除行,则您必须首先设置 DELIMIDENT 环境变量,或使用其所有者的名称来限定该表的名称:

DELETE legree.from;

要获取关于定界的标识符以及 DELIMIDENT 环境变量的更多信息,请参阅《GBase 8s SQL 指南:语法》 中对“带引号的字符串”表达式以及“标识符”段的描述。

使用 TRUNCATE 来删除所有行

您可使用 TRUNCATE 语句来快速地从表中移除所有行,同时还移除所有对应的索引数据。在提交该事务之后,您不可恢复删除了的行。您可对包含任何列类型(包括智能大对象)的表上使用 TRUNCATE 语句。

使用 TRUNCATE 语句来移除行的速度比使用 DELETE 语句来移除它们快。在 TRUNCATE 语句之后,不必立即运行 UPDATE STATISTICS 语句。成功地执行 TRUNCATE 之后,GBase 8s 自动地更新该表及其系统目录中的索引的统计信息和分布情况,以展示在该表中或在它的 dbspace 分区中没有任何行。

要了解日志记录的描述,请参阅 事务日志记录。

TRUNCATE 是数据定义语言语句,如果在该表上定义任何触发器,则该语句不激活 DELETE 触发器。要了解关于使用触发器的说明,请参阅 创建和使用触发器。

如果 TRUNCATE 语句指定的表是 typed 表,则成功的 TRUNCATE 操作从那个表中以及从该表层级结构内的所有子表中移除所有行和 B-tree 结构。TRUNCATE 不等同于 DELETE 语句的 ONLY 关键字,DELETE 语句将操作限制在 typed 表层级结构内的单个表。

GBase 8s 始终对 TRUNCATE 操作进行日志记录,即使对非日志记录的表也是如此。在支持事务日志记录的数据库中,在同一事务之内的 TRUNCATE 之后,仅 SQL 的 COMMIT WORK 或 ROLLBACK WORK 语句是有效的。要获取关于使用 TRUNCATE 语句对性能的影响的信息,请参阅《GBase 8s 性能指南》 。要了解完整的语法,请参阅《GBase 8s SQL 指南:语法》。

删除指定的行

DELETE 语句中的 WHERE 子句与 SELECT 语句中的 WHERE 子句的形式相同。您可使用它来准确地指定应删除哪一行或哪些行。您可删除带有特定客户编号的客户,如下例所示:

DELETE FROM customer WHERE customer_num = 175;

在此示例中,由于 customer_num 列有唯一约束,因此您可确保只删除一行。

删除选择了的行

您还可选取基于非索引列的行,如下例所示:

DELETE FROM customer WHERE company = 'Druid Cyclery';

由于被测试的列没有唯一约束,因此此语句可能删除多行。(Druid Cyclery 可能有两个商店,两个商店的名称相同但客户编号不一样。)

要了解 DELETE 语句影响多少行,请从 customer 表中为 Druid Cyclery 选择符合条件的行计数。

SELECT COUNT(*) FROM customer WHERE company = 'Druid Cyclery';

您还可选择这些行并显示它们,以确保它们是您想要删除的那些行。

然而,当数据库对于多个用户同时可用时,使用 SELECT 语句作为测试只是一种近似的方法。在您执行 SELECT 语句与后续的 DELETE 语句之间的时间内,其他用户可能已修改了该表并更改了结果。在此示例中,另一用户可能执行下列操作:

  • 为名为 Druid Cyclery 的另一客户插入新行
  • 在插入新行之前,删除一个或多个 Druid Cyclery 行
  • 更新 Druid Cyclery 行以具有新的公司名称,或更新某个其他客户以具有名称 Druid Cyclery。

在这短短的时间间隔内,虽然其他用户不太可能执行这些操作,但确实存在这种可能性。相同的问题也影响 UPDATE 语句。在 并发和锁定 之下讨论解决此问题的方法,且在 对多用户环境编程 中讨论得更详细。

您可能遇到的另一个问题是,在该语句完成之前出现硬件或软件故障。在此情况下,数据库可能还没删除行,可能已删除了一些行,或已经删除了所有指定的行。数据库的状态未知,这是我们不想看到的。要防止此情况,请使用事务日志记录,如 中断了的修改 讨论的那样。

删除包含 row 类型的行

当某行包含定义在 ROW 类型上的列时,您可使用点符号表示法来指定仅删除那些包含特定字段值的行。例如,下列语句仅从 employee 表中删除address 列中的 city 字段的值为 San Jose 的那些行:

DELETE FROM employee
WHERE address.city = 'San Jose';

在前面的语句中,address 列可能是命名的 ROW 类型或未命名的 ROW 类型。您用来指定 ROW 类型的字段值的语法是相同的。

删除包含集合类型的行

当某行包含定义在集合类型上的列时,您可在集合中搜索特定的元素,并删除在其中找到那个元素的一行或多行。例如,下列语句删除其中的direct_reports 列包含带有元素 Baker 的集合的那些行:

DELETE FROM manager
WHERE 'Baker' IN direct_reports;

从超级表中删除行

当您删除超级表的各行时,删除操作的作用域是超级表及其子表。假设您创建超级表 person,在其下定义两个子表 employee 和 sales_rep。下列对 person 表执行的 DELETE 语句可从 person、employee 和 sales_rep 全部三个表中删除行:

DELETE FROM person
WHERE name ='Walker';

要限制为仅删除超级表的行,您必须使用 DELETE 语句中的 ONLY 关键字。例如,下列语句仅删除 person 表的行:

DELETE FROM ONLY(person)
WHERE name ='Walker';
重要

当您从超级表中删除行时,请小心使用,因为对超级表的删除的作用域包括该超级表及其所有子表。

复杂的删除条件

DELECT 语句中的 WHERE 子句可与 SELECT 语句中的一样复杂。它可包含通过 AND 和 OR 连接的多个条件,且它可能包含子查询。

假设您发现 stock 表的某些行包含不正确的制造商代码。您不想更新它们,而是想要删除它们以便重新输入它们。您知道,与正确的那些行不一样,这些行在 manufact 表中没有相匹配的行。由于这些不正确的行在 manufact 表中没有相匹配的行,因此您可以编写下例中所示的 DELETE 语句:

DELETE FROM stock
WHERE 0 = (SELECT COUNT(*) FROM manufact
WHERE manufact.manu_code = stock.manu_code);

该子查询对匹配的 manufact 行数进行计数;对 stock 的正确的行计数为 1,对不正确的行计数为 0。选取不正确的行来删除。

提示

使用复杂的条件来开发 DELETE 语句的一种方法是,先开发精确地返回要删除的行的 SELECT 语句。将它编写为 SELECT *;当它返回所期望的行集时,将SELECT * 更改为读取 DELETE,并再执行它一次。

DELETE 语句的 WHERE 子句不可使用测试同一表的子查询。即,当您从 stock 进行删除时,您不可在也从 stock 中选择的 WHERE 子句中使用子查询。

此规则的关键在于 FROM 子句。如果在 DELETE 语句的 FROM 子句中命名表,则该表不可还出现在 DELECT 语句的子查询的 FROM 子句中。

MERGE 的 Delete 子句

不在 WHERE 子句中编写子查询,您可使用 MERGE 语句将来自源表和目标表的行连接在一起,然后从目标删除与连接条件相匹配的那些行。(Delete MERGE 中的源表还可为一个集合派生的表,它的行是查询的结果,该查询连接其他的表和视图,但是在下列的示例中,源是单个表。)

如在前面的示例中那样,假设您发现 stock 表的某些行包含不正确的制造商代码。您想要删除它们以便重新输入它们,而不是更新它们。您可使用 MERGE 语句,指定 stock 作为目标表,manufact 作为源表,ON 子句中的连接条件,并对于带有不正确的制造商代码的 stock 行使用 Delete 子句,如下例所示:

MERGE INTO stock USING manufact
ON stock.manu_code != manufact.manu_code
WHEN MATCHED THEN DELETE;

在此示例中,会从 stock 表中删除那些满足 ON 子句中的连接条件的所有行。在此,对于其中的 manu_code 列值不等于 manufact 中的任何manu_code 值的 stock 的那些行,连接条件中的不等谓词(stock.manu_code != manufact.manu_code)求值为真。

在 USING 子句中必须罗列正连接到目标表的源表。

MERGE 语句还可更新目标表的行,或将数据从源表插入到目标表,根据该行是否满足 ON 子句为连接目标表与源表而指定的条件。单个 MERGE 语句还可同时组合 DELETE 与 INSERT 操作,或者可同时组合 UPDATE 与 INSERT 操作而不删除任何行。MERGE 语句不更改源表。要获取关于 Delete 合并、Insert 合并和 Update 合并的语法与限制的更多信息,请参阅《GBase 8s SQL 指南:语法》 中 MERGE 语句的描述。

插入行

INSERT 将新的一行或多行添加到表。该语句有两个基本功能。它可使用您提供的列值创建单个的新行,或可使用从其他表选择的数据创建一组新的行。

单个行

在它的最简单形式中,INSERT 语句从一列值的列表创建一个新行,并将其放置在表中。下列语句展示如何将一行添加到 stock 表:

INSERT INTO stock
VALUES (115, 'PRC', 'tire pump', 108, 'box', '6/box');

stock 表有下列列:

stock_num

标识商品的种类的编号。

manu_code

manufact 表的外键。

description

该商品的描述。

unit_price

该商品的单价。

unit

计量的单位

unit_descr

说明计量单位的特征。

前一示例中 VALUES 子句中罗列的值与 stock 表的列有一一对应关系。要编写 VALUES 子句,您必须知道表的列以及它们的前后次序。

可能的列值

VALUES 子句仅接受常量值,不接受通用的 SQL 表达式。您可提供下列值:

  • 文字数值
  • 文字 DATETIME 值
  • 文字 INTERVAL 值
  • 带引号的字符串
  • 表示 NULL 的关键字 NULL
  • 表示当前日期的关键字 TODAY
  • 表示当前日期和时间的关键字 CURRENT(或 SYSDATE)
  • 表示您的授权标识符的关键字 USER
  • 表示正在运行数据库服务器的计算机名称的关键字 DBSERVERNAME(或 SITENAME)
注意

MERGE 语句可以替代 INSERT 语句,可使用与 INSERT 语句一样的 VALUES 子句语法来将行插入到表内。MERGE 语句执行源表与目标表的外部链接,然后将连接的结果集中的任何行插入到目标表内,这些行的连接谓词求值为 FALSE。MERGE 语句不更改源表。除了插入行之外,MERGE 语句可可选地同时组合 DELETE 与 INSERT 操作,或同时组合 UPDATE 与 INSERT 操作。要获取关于 Insert 合并、Delete 合并和 Update 合并的语法与限制的更多信息,请参阅《GBase 8s SQL 指南:语法》 中 MERGE 语句的描述。

对列值的限制

表的某些列可能不允许空值。如果您尝试向这样的列插入 NULL,则会拒绝该语句。表中的其他列可能不允许重复的值。如果您指定与这样的列中已经存在的值重复的值,则会拒绝该语句。有些列甚至可能限制允许的列值。请使用数据完整性约束来限制列。要获取更多信息,请参阅 数据完整性。

限制

请不要为包含货币值的列指定币种符号。请仅指定该金额的数值值。

数据库服务器可在数值与字符数据类型之间进行转换。您可将数值字符的字符串(例如,'-0075.6')作为数值列的值。数据库服务器将数值字符串转换为数值。仅当该字符串不表示数值时才会发生错误。

您可指定数值或日期作为字符列的值。数据库服务器将那个值转换为字符串。例如,如果您指定 TODAY 作为字符列的值,则使用表示当前日期的字符串。(DBDATE 环境变量指定所使用的格式。)

序列数据类型

表仅可有一个 SERIAL 数据类型的列。它还可有 SERIAL8 列或 BIGSERIAL 列。

当您插入值时,请为序列列指定值零。数据库服务器按次序生成下一个实际值。序列列不允许 NULL 值。

您可为序列列指定非零值(只要它不与那一列中任何现有的值重复),数据库服务器使用该值。那个非零值可能为数据库服务器生成的值设置新的起始点。(数据库服务器为您生成的下一个值是比该列中最大值大一的值。)

罗列特定的列名称

您不必为每列都指定值。相反,您可在表名称之后罗列列名称,然后仅为您命名了的那些列提供值。下列示例展示将新行插入到 stock 表内的语句:

INSERT INTO stock (stock_num, description, unit_price, manu_code)
VALUES (115, 'tyre pump ', 114, 'SHM');

仅提供库存编号、描述、单价和制造商代码的数据。数据库服务器为其余列提供下列值:

  • 它为未列出的序列列生成一个序列数值。
  • 它为与它相关联的有特定缺省值的列生成一个缺省值。
  • 它为任何允许空值的任何列生成 NULL 值,但它不为指定 NULL 作为缺省值的任何列指定缺省值。

您必须为未指定缺省值或不允许 NULL 值的所有列罗列并提供值。

您可以任何顺序罗列列,只要这些列的值也以相同的顺序罗列。

在执行前一示例中的 INSERT 语句之后,将下列新行插入到 stock 表内:

stock_num manu_code description unit_price unit unit_descr

115 SHM tyre pump 114

unit 和 unit_descr 都为空,表示在那两列中存在 NULL 值。由于 unit 列允许 NULL 值,因此不知道 114 美元可购买的轮胎充气泵(tire pump)的数目。当然,如果为这一列指定了 box 缺省值,则计量单位将为 box。在任何情况下,当您将值插入到表的特定的列内时,请注意那一行需要什么数据。

将行插入到类型的表中

您可使用与将行插入到不基于 ROW 类型的表内的相同方法,将行插入到类型的表内。

当类型的表包含一 row 类型列(定义该类型的表的命名了的 ROW 类型包含嵌套的 ROW 类型)时,您插入到 row 类型列的方法,与为不基于 ROW 类型的表插入 row 类型列的方法相同。下列部分,在列上插入的语法规则,描述如何执行插入到 row 类型列内。

此部分为示例使用 row 类型 zip_t、address_t 和 employee_t,以及 类型的表 employee。下图展示创建 row 类型和表的 SQL 语法。

图: 创建 row 类型和表的 SQL 语法。

CREATE ROW TYPE zip_t
(
z_code CHAR(5),
z_suffix CHAR(4)
);

CREATE ROW TYPE address_t
(
street VARCHAR(20),
city VARCHAR(20),
state CHAR(2),
zip zip_t
);

CREATE ROW TYPE employee_t
(
name VARCHAR(30),
address address_t,
salary INTEGER
);

CREATE TABLE employee OF TYPE employee_t;

在列上插入的语法规则

下列语法规则适用于那些定义在命名了的 ROW 类型或未命名的 ROW 类型上的列上的插入:

在要插入字段值之前,指定 ROW 构造函数。

将 ROW 类型的字段值括在圆括号中。

将 ROW 表达式强制转型为适当的命名了的 ROW 类型(对于命名了的 ROW 类型)。

包含命名了的 row 类型的行

下列语句展示您如何将一行插入到在 图 1 中的 employee 内:

INSERT INTO employee
VALUES ('Poole, John',
ROW('402 High St', 'Willits', 'CA',
ROW(69055,1450))::address_t, 35000 );

由于 employee 表的 address 列是命名了的 ROW 类型,因此您必须使用强制转型运算符和 ROW 类型的名称(address_t)来插入类型 address_t的值。

包含未命名的 row 类型的行

假设您创建下图所示的表。student 表定义 s_address 列为一未命名的 row 类型。

图: 创建 student 表。

CREATE TABLE student
(
s_name VARCHAR(30),
s_address ROW(street VARCHAR (20), city VARCHAR(20),
state CHAR(2), zip VARCHAR(9)),
grade_point_avg DECIMAL(3,2)
);

下列语句展示您如何向 student 表添加一行。要插入到未命名的 row 类型列 s_address 内,请使用 ROW 构造函数,但不要对该 row 类型值进行强制转型。

INSERT INTO student
VALUES ('Keene, Terry',
ROW('53 Terra Villa', 'Wheeling', 'IL', '45052'),
3.75);

为 row 类型指定 NULL 值

row 类型列的字段可包含 NULL 值。您可在列级别或在字段级别指定 NULL 值。

下列语句在列级别指定 NULL 值,来为 s_address 列的所有字段插入 NULL 值。当您在列级别插入 NULL 值时,请不要包括 ROW 构造函数。

INSERT INTO student VALUES ('Brauer, Howie', NULL, 3.75);

当您为 ROW 类型的特定字段插入 NULL 值时,必须包括 ROW 构造函数。下列 INSERT 语句展示您可以如何将 NULL 值插入到 employee 表的address 列的特定字段内。(address 列被定义为命名了的 ROW 类型。)

INSERT INTO employee
VALUES (
'Singer, John',
ROW(NULL, 'Davis', 'CA',
ROW(97000, 2000))::address_t, 67000
);

当您为 ROW 类型的字段指定 NULL 值时,当该 ROW 类型出现在 INSERT 语句、UPDATE 语句或程序变量赋值中时,无需显式地强制转型该 NULL 值。

下列 INSERT 语句展示您如何为 student 表的 s_address 列的 street 和 zip 字段插入 NULL 值:

INSERT INTO student
VALUES(
'Henry, John',
ROW(NULL, 'Seattle', 'WA', NULL), 3.82
);

将行插入到超级表内

当您将行插入到超级表内时,不存在特殊的注意事项。INSERT 语句仅适用于在该语句中指定的表。例如,下列语句将值插入到超级表内,但不将值插入到任何子表内:

INSERT INTO person
VALUES (
'Poole, John',
ROW('402 Saphire St.', 'Elmondo', 'CA', '69055'),
345605900
);

将集合值插入到列内

此部分描述如何使用 DB-Access 将集合值插入到列内。它未讨论如何将个别元素插入到集合列内。要访问或修改集合的个别元素,请使用 GBase 8s ESQL/C 程序或 SPL 例程。要获取关于如何创建 GBase 8s ESQL/C 程序来插入到集合内的信息,请参阅《GBase 8s ESQL/C 程序员手册》。要获取关于如何创建 SPL 例程来插入到集合内的信息,请参阅 创建和使用 SPL 例程。

本部分提供的这些示例是基于下图中的 manager 表。manager 表同时包含简单的和嵌套的集合类型。

图: 创建 manager 表。

CREATE TABLE manager
(
mgr_name VARCHAR(30),
department VARCHAR(12),
direct_reports SET(VARCHAR(30) NOT NULL),
projects LIST(ROW(pro_name VARCHAR(15),
pro_members SET(VARCHAR(20) NOT NULL))
NOT NULL)
);

将值插入到简单的集合和嵌套的集合内

当您将值插入包含集合列的行内时,您插入集合列包含的所有元素的值以及其他列的值。 例如,下列语句将单个行插入到 manager 表内,该表同时包括简单的集合和嵌套的集合列:

INSERT INTO manager(mgr_name, department,
direct_reports, projects)
VALUES
(
'Sayles', 'marketing',
"SET{'Simonian', 'Waters', 'Adams', 'Davis', 'Jones'}",
LIST{
ROW('voyager_project', SET{'Simonian', 'Waters',
'Adams', 'Davis'}),
ROW ('horizon_project', SET{'Freeman', 'Jacobs',
'Walker', 'Smith', 'Cannan'}),
ROW ('saphire_project', SET{'Villers', 'Reeves',
'Doyle', 'Strongin'})
}
);

将 NULL 值插入到包含 row 类型的集合内

要将值插入到 ROW 类型的集合内,您必须为 ROW 类型中的每一字段指定值。

通常,在集合中不允许 NULL 值。然而,如果集合的元素类型为 ROW 类型,则您可将 NULL 值插入到 row 类型的个别字段内。

您还可指定空集合。空集合是不包含任何元素的集合。要指定空集合,请使用大括号({})。例如,下列语句将数据插入到 manager 表中的行内,但指定 direct_reports 和 projects 列为空集合:

INSERT INTO manager
VALUES ('Sayles', 'marketing', "SET{}",
"LIST{ROW(NULL, SET{})}"
);

集合列不可包含 NULL 元素。由于指定 NULL 值作为集合的元素,因此下列语句返回一个错误:

INSERT INTO manager
VALUES ('Cole', 'accounting', "SET{NULL}",
"LIST{ROW(NULL, ""SET{NULL}"")}"

下列语法规则适用于对集合类型执行插入和更新:

使用大括号({})来划分每一集合包含的元素。

如果该集合为嵌套的集合,则使用大括号({})来同时划分内部集合和外部集合的元素。

插入智能大对象

当您使用 INSERT 语句来将对象插入到 BLOB 或 CLOB 列时,数据库服务器在 sbspace 中,而不是在表中,存储该对象。数据库服务器提供您可从 INSERT 语句之内调用的 SQL 函数来导入和导出 BLOB 或 CLOB 数据,这些数据又称为智能大对象。要了解这些函数的描述,请参阅 智能大对象函数。

下列 INSERT 语句使用 filetoblob() 和 filetoclob() 函数来插入 inmate 表的行。(图 1 定义 inmate 表。)

INSERT INTO inmate
VALUES (437, FILETOBLOB('datafile', 'client'),
FILETOCLOB('tmp/text', 'server'));

在前一示例中,FILETOBLOB() 和 FILETOCLOB() 函数的第一个参数分别指定要复制到 inmate 表的 BLOB 和 CLOB 列内的源文件的路径。每一函数的第二个参数指定该源文件是位于客户机计算机('client'),还是位于服务器计算机('server')。要在该函数参数中指定文件名称的路径,请应用下列规则:

  1. 如果源文件驻留在服务器计算机上,则您必须指定该文件的完全路径名称(不是相对于当前工作目录的路径名称)。
  2. 如果源文件驻留在客户机计算机上,则您可指定该文件的完全路径或相对路径。

多个行和表达式

INSERT 语句的其他主要形式以 SELECT 语句替代 VALUES 子句。此特性允许您插入下列数据:

仅用一条语句处理多个行(每当 SELECT 语句返回一行,就插入一行)

计算值(VALUES 子句仅允许常量),由于 projection 列表可包含表达式

例如,假设对于已付款但尚未装运的每个订单都需要电话跟进。下列示例中的 INSERT 语句找到那些订单并为每一订单在 cust_calls 中插入一行:

INSERT INTO cust_calls (customer_num, call_descr)
SELECT customer_num, order_num FROM orders
WHERE paid_date IS NOT NULL
AND ship_date IS NULL;

此 SELECT 语句返回两列。将来自这些列的数据(在每一选择了的行中)插入到 cust_calls 表的命名了的列内。然后,将(来自 SERIAL 列order_num 的)订单编号插入到呼叫描述,这是一个字符列。请记住,数据库服务器允许您将整数值插入到字符列内。它自动地将序列编号转换为十进制数字的字符串。

使用 INSERT 语句插入多行数据,支持在VALUES子句中使用多个逗号分隔的列值列表,其中列表括在括号内,并用逗号分隔,列表数为插入数据行数。插入值的个数、类型和顺序要与表字段对应;如果某一<列名>未在 INTO 子句后面出现,则新插入的行在这些列上将取空值或缺省值,如该列在基表定义时说明为 NOT NULL 时数据库返回语法错误信息;

如果<基表名>后没指定任何<列名>,则隐含指定该表的所有列,这时,VALUES 中列值必须在每个列上均有<插入值>;对于存在行触发器的表,每一行都会触发相关的触发器;同样如果目标表具有约束,那么每一行都会进行相应的约束检查,只要有一行不满足约束,所有的值都不能插入成功。

例如,在user_info表中新增两条数据,可以使用如下语法格式:

INSERT INTO user_info (user_account,user_name,user_age,user_class) VALUES
(00001, '张三 ',20,‘计算机系’),
(00002,‘李四’,19,‘计算机系’);

对插入选择的限制

下列列表包含对插入行的 SELECT 语句的限制:

  • 它不可包含 INTO 子句。
  • 它不可包含 INTO TEMP 子句。
  • 它不可包含 ORDER BY 子句。
  • 它不可引用您正在向其内插入行的表。

INTO、INTO TEMP 和 ORDER BY 子句限制较小。在此上下文中,INTO 子句没什么用处。(要获取更多信息,请参阅 SQL 编程。)要绕开 INTO TEMP 子句限制,请先选择您想要插入到临时表内的数据,然后使用 INSERT 语句从临时表插入该数据。同样,缺少 ORDER BY 子句也无关紧要。如果您需要确保这些新行在表中物理地排序,则您可首先将它们选择到临时表内并对它排序,然后从该临时表插入。在所有插入完成之后,您还可使用集群的索引来对该表进行物理排序。

重要

最后一个限制更为严重,因为它同时阻止在 INSERT 语句的 INTO 子句与 SELECT 语句的 FROM 子句中命名同一个表。同时在 INSERT 语句的 INTO 子句与 SELECT 语句的 FROM 子句中命名同一个表,导致数据库服务器进入无限循环,每一插入了的行都会在其中被重新选择和重新插入。

然而,在某些情况下,您可能想要从您必须向其内插入数据的同一个表进行选择。例如,假设您已了解 Nikolus 公司与 Anza 公司供应相同的产品,但仅以一半的价格供应。您想要向 stock 表添加一些行来反映两家公司之间的差异。理想情况下,您想要从所有 Anza 库存行选择数据,并使用 Nikolus 制造商代码重新插入它。然而,您不可从您正在向其内插入的同一个表进行选择。

要避开此限制,请选择您想要插入到临时表内的数据。然后在 INSERT 语句中从那个临时表进行选择,如下例所示:

SELECT stock_num, 'NIK' temp_manu, description, unit_price/2
half_price, unit, unit_descr FROM stock
WHERE manu_code = 'ANZ'
AND stock_num < 110
INTO TEMP anzrows;

INSERT INTO stock SELECT * FROM anzrows;

DROP TABLE anzrows;

此 SELECT 语句从 stock 得到现有的行,并替换制造商代码的文字值以及单价的计算得到的值。然后将这些行保存在临时表 anzrows 中,立即将该表插入到 stock 表内。

当您插入多个行时,存在一种风险:其中一行包含无效的数据,就可能导致数据库服务器报告一个错误。当发生这样一个错误时,该语句提早终止。即使未发生错误,也存在一个小风险:在执行该语句时可能发生硬件或软件故障(例如,磁盘可能写满)。

在任何一种事件中,您都不可轻易地知道插入了多少新行。如果全部重复该语句,则您可能创建重复的行,也可能不会。由于数据库处于未知状态,因此您无所适从。解决方案在于使用事务,如 中断了的修改 讨论的那样。

更新行

根据 SET 子句的规范,使用 UPDATE 语句来更改表的一个或多个现有行的内容。此语句采用两种根本不同的形式。一种允许您按名称将特定的值指定给列;另一种允许您将(可能是通过 SELECT 语句返回的)值的列表指定给列的列表。在任一情况下,如果您正在更新行,且某些列有数据完整性约束,则您更改的数据必须符合对那些列的限制。要获取更多信息,请参考 数据完整性。

注意

MERGE 语句是 UPDATE 语句的一种替代,可使用与 UPDATE 语句一样的 SET 子句语法来修改表的现有行中的一个或多个值。MERGE 语句执行源表与目标表的外部连接,然后以来自于连接的结果集的值更新目标表中的行,其连接谓词求值为 TRUE。MERGE 语句不更改源表中的值。除了更新行之外,MERGE 语句可可选地同时组合 UPDATE 与 INSERT 操作,或可同时组合 DELETE 与 INSERT 操作而不更新任何行。要获取关于 Update 合并、Delete 合并和 Insert 合并的语法和限制的更多信息,请参阅《GBase 8s SQL 指南:语法》 中 MERGE 语句的描述。

选择要更新的行

UPDATE 语句的任一形式都可以确定修改那些行的 WHERE 子句结尾。如果您省略 WHERE 子句,则修改所有行。要选择在 WHERE 子句中需要更改的精确行集可能非常复杂。对 WHERE 子句的唯一限制是,不可在子查询的 FROM 子句中命名您更新的表。

UPDATE 语句的第一种形式是,使用一系列赋值子句来指定新的列值,如下例所示:

UPDATE customer
SET fname = 'Barnaby', lname = 'Dorfler'
WHERE customer_num = 103;

WHERE 子句选择您想要更新的行。在演示数据库中,customer.customer_num 列是那个表的主键,因此,此语句最多可更新一行。

您还可在 WHERE 子句中使用子查询。假设 Anza 公司对他们的网球发出安全召回。结果是,包括来自制造商 ANZ 的库存编号 6 的任何未装运的订单都必须设定为延期交货,如下例所示:

UPDATE orders
SET backlog = 'y'
WHERE ship_date IS NULL
AND order_num IN
(SELECT DISTINCT items.order_num FROM items
WHERE items.stock_num = 6
AND items.manu_code = 'ANZ');

此子查询返回一订单编号(零个或多个)的列。然后,该 UPDATE 操作针对该列表测试 orders 的每一行,如果那一行相匹配,则执行更新。

以统一值进行更新

关键字 SET 之后的每一赋值都为列指定新的值。那个值统一地应用于您更新的每一行。在前面部分中的示例中,新的值为常量,但您可指定任意表达式,包括基于列值本身的表达式。假设制造商 HRO 已将所有价格提高百分之五,且您必须更新 stock 表来反映此提价。请使用下列语句:

UPDATE stock
SET unit_price = unit_price * 1.05
WHERE manu_code = 'HRO';

您还可使用子查询作为指定的值的一部分。当使用子查询作为表达式的元素时,它必须恰好返回一个值(一列和一行)。对于任何库存编号,或许您决定必须收取比那种产品的任何制造商都更高的价格。您需要更新所有未装运的订单的价格。下列示例中的 SELECT 语句指定该标准:

UPDATE items
SET total_price = quantity *
(SELECT MAX (unit_price) FROM stock
WHERE stock.stock_num = items.stock_num)
WHERE items.order_num IN
(SELECT order_num FROM orders
WHERE ship_date IS NULL);

第一个 SELECT 语句返回单个值:在 stock 表中,对于某个特定的产品的最高价格。第一个 SELECT 语句是一个相关联的子查询,因为当来自items 的值出现在第一个 SELECT 语句的 WHERE 子句中时,您必须为您更新的每一行都执行该查询。

第二个 SELECT 语句产生未装运的订单的订单编号的一个列表。它是一个执行一次的非相关的子查询。

对更新的限制

当您修改数据时,对子查询的使用存在限制。特别是,您不可查询正在修改的表。您可在表达式中引用列的当前值,如同在 unit_price 列增大百分之五的示例中那样。您还可引用在子查询中的 WHERE 子句中的列的值,如同在更新了 stock 表的示例中那样,其中,更新 items 表,且在连接表达式中使用 items.stock_num。

在设计良好的数据库中,不会经常发生同时更新与查询一个表的需要。然而,当您首次开发数据库时,尚未认真全面地考虑它的设计之前,您可能想要同时更新和查询。当无意中且错误地在表的应为唯一的列中包含了带有重复值的几行时,会发生一个典型的问题。您可能想要删除重复的行,或仅更新重复的行。不论是哪种方式,不可避免地需要对您想要修改的同一表上的子查询进行重复的行的测试。这在 UPDATE 语句或 DELETE 语句中是不允许的。通过 SQL 程序修改数据 讨论如何使用更新游标来执行此种修改。

用选择了的值更新

第二种形式的 UPDATE 语句以单个批量赋值替代赋值列表,其中设置列的列表与值的列表相同。当这些值是简单的常量时,这种形式与前面的示例的形式没什么不同,只是重新安排它的某些部分,如下例所示:

UPDATE customer
SET (fname, lname) = ('Barnaby', 'Dorfler')
WHERE customer_num = 103;

以此方式编写该语句,不存在任何优势。实际上,它更难于阅读,因为将哪些值指定给哪些列并不明显。

然而,当这些要指定的值来自单个 SELECT 语句时,这种形式就很合理。假设要将地址的更改应用于几个客户。不是每次更新 customer 表都报告更改,而是在名为 newaddr 的单个临时表中收集新的地址。现在,一次性应用所有新地址的时候到了。

UPDATE customer
SET (address1, address2, city, state, zipcode) =
((SELECT address1, address2, city, state, zipcode
FROM newaddr
WHERE newaddr.customer_num=customer.customer_num))
WHERE customer_num IN (SELECT customer_num FROM newaddr);

单个 SELECT 语句产生多个列的值。如果您以其他形式重新编写此示例,为每一更新了的列进行赋值,您必须编写五个 SELECT 语句,为每个要更新的列编写一个。这样的语句不仅更难编写,而且它会花费更长的执行时间。

提示

在 SQL API 程序中,您可使用记录或主变量来更新值。要获取更多信息,请参考 SQL 编程

更新 row 类型

依赖于该列是命名了的 ROW 类型还是未命名的 ROW 类型,您用于更新 row 类型值的语法会不同。本部分描述那些差异,还描述如何为 ROW 类型的字段指定 NULL 值。

更新包含命名了的 row 类型的行

要更新在命名了的 ROW 类型上定义的列,您必须指定所有 ROW 类型的字段。例如,下列语句仅更新 employee 表中 address 列的 street 和 city字段,但每一 ROW 类型的字段必须包含一个值(允许 NULL 值):

UPDATE employee
SET address = ROW('103 California St',
'San Francisco', address.state, address.zip)::address_t
WHERE name = 'zawinul, joe';

在此示例中,从该行中读取 state 和 zip 字段的值,然后立即重新插入到该行内。仅更新 address 列的 street 和 city 字段。

当您更新在命名了的 ROW 类型上定义的列的字段时,您必须使用 ROW 构造函数,并将该行值强制转型为适当的命名了的 ROW 类型。

更新包含未命名的 row 类型的行

要更新在未命名的 ROW 类型上定义的列,您必须指定该 ROW 类型的所有字段。例如,下列语句仅更新 student 表中 address 列的 street 和 city字段,但 ROW 类型的每一字段都必须包含一个值(允许 NULL 值):

UPDATE student
SET s_address = ROW('13 Sunset', 'Fresno',
s_address.state, s_address.zip)
WHERE s_name = 'henry, john';

要更新在未命名的 ROW 类型上定义的列的字段,请始终在插入该字段值之前,指定 ROW 构造函数。

为 row 类型的字段指定 Null 值

row 类型列的字段可包含 NULL 值。当您以 NULL 值插入到 row 类型字段内或更新 row 类型字段时,您必须将该值强制转型为那个字段的数据类型。

下列 UPDATE 语句展示您可以如何为命名了的 row 类型列的特定字段指定 NULL 值:

UPDATE employee
SET address = ROW(NULL::VARCHAR(20), 'Davis', 'CA',
ROW(NULL::CHAR(5), NULL::CHAR(4)))::address_t)
WHERE name = 'henry, john';

下列 UPDATE 语句展示您如何为 student 表的 address 列的 street 和 zip 字段指定 NULL 值。

UPDATE student
SET address = ROW(NULL::VARCHAR(20), address.city,
address.state, NULL::VARCHAR(9))
WHERE s_name = 'henry, john';
重要

您不可为 row 类型列指定 NULL 值。您仅可为 row 类型的个别的字段指定 NULL 值。

更新集合类型

当您使用 DB-Access 来 更新集合类型时,您必须更新整个集合。下列语句展示如何更新 projects 列。 要定位需要更新的行,请使用 IN 关键字在direct_reports 列上执行搜索。

UPDATE manager
SET projects = "LIST
{
ROW('brazil_project', SET{'Pryor', 'Murphy', 'Kinsley',
'Bryant'}),
ROW ('cuba_project', SET{'Forester', 'Barth', 'Lewis',
'Leonard'})
}"
WHERE 'Williams' IN direct_reports;

在前一语句中第一次出现的 SET 关键字是 UPDATE 语句语法的一部分。

重要

请不要将 UPDATE 语句的 SET 关键字与表明集合为 SET 数据类型的 SET 构造函数相混淆。

虽然您可使用 IN 关键字来定位简单集合的特定元素,但您不可从 DB-Access 更新集合列的个别元素。然而,您可创建 GBase 8s ESQL/C 程序和 SPL 例程来更新集合内的元素。要获取关于如何创建 GBase 8s ESQL/C 程序来更新集合的信息,请参阅《GBase 8s ESQL/C 程序员手册》。要获取关于如何创建 SPL 例程来更新集合的信息,请参阅 处理集合 部分。

更新超级表的行

当您更新超级表的行时,更新的作用域是超级表及其子表。

当您对超级表构造 UPDATE 语句时,您可更新该超级表中的所有列,以及从该超级表继承的子表的列。例如,下列语句更新来自 employee 和sales_rep 表的行,它们是超级表 person 的子表:

UPDATE person
SET salary=65000
WHERE address.state = 'CA';

然而,对超级表的更新不允许您更新不在该超级表内的子表的列。例如,在前面的更新语句中,您不可更新 sales_rep 表的 region_num 列,因为region_num 列未出现在 employee 表中。

当您对超级表执行更新时,请注意该更新的作用域。例如,对 person 表的 UPDATE 语句未包括 WHERE 子句来限定要更新的行,该语句修改person、employee 和 sales_rep 表的所有行。

要限定为仅对超级表的行更新,您必须在 UPDATE 语句中使用 ONLY 关键字。例如,下列语句仅更新 person 表的行:

UPDATE ONLY(person)
SET address = ROW('14 Jackson St', 'Berkeley',
address.state, address.zip)
WHERE name = 'Sallie, A.';
重要

当您更新超级表的行时,请小心使用,因为对超级表的更新的作用域包括该超级表及其所有子表。

更新列的 CASE 表达式

CASE 表达式允许语句返回几个可能的结果之一,这依赖于若干条件测试中哪个求值为 TRUE。

下列示例展示如何在 UPDATE 语句中使用 CASE 表达式来增加 stock 表中某些商品的单价:

UPDATE stock
SET unit_price = CASE
WHEN stock_num = 1
AND manu_code = "HRO"
THEN unit_price * 1.2
WHEN stock_num = 1
AND manu_code = "SMT"
THEN unit_price * 1.1
ELSE 0
END

您必须在 CASE 表达式内包括至少一个 WHEN 子句;后续的 WHEN 子句和 ELSE 子句是可选的。如果无 WHEN 条件求值为真,则结果值为空。

更新智能大对象的 SQL 函数

您可使用可从 UPDATE 语句之内调用的 SQL 函数来导入和导出智能大对象。要了解这些函数的描述,请参阅 智能大对象函数 页。

下列 UPDATE 语句使用 LOCOPY() 函数来将 BLOB 数据从 fbi_list 表的 mugshot 列复制到 inmate 表的 picture 列内。(图 1 定义 inmate 和fbi_list 表。)

UPDATE inmate (picture)
SET picture = (SELECT LOCOPY(mugshot, 'inmate', 'picture')
FROM fbi_list WHERE fbi_list.id = 669)
WHERE inmate.id_num = 437;

LOCOPY() 的第一个参数指定从其导出该对象的列(mugshot)。第二个和第三个参数指定该新创建的对象将使用其存储特性的表(inmate)和列(picture)的名称。该 UPDATE 语句执行之后,picture 列包含来自 mugshot 列的数据。

当您在该函数参数中指定文件名称的路径时,请应用下列规则:

  • 如果源文件驻留在服务器计算机上,则您必须指定该文件的完全路径名称(而不是相对于当前工作目录的路径名称)。
  • 如果源文件驻留在客户机计算机上,则您可指定该文件的完全路径名称或相对路径名称。

更新表的 MERGE 语句

MERGE 语句允许您对源表与目标表的一个外部连接的结果应用布尔条件。如果 MERGE 语句包括 Update 子句,则对目标在 UPDATE 操作中使用那些满足您在 ON 关键字之后指定的连接条件的行。MERGE 语句的 SET 子句支持与 UPDATE 语句的 SET 子句相同的语法,并指定要更新的目标表的哪些列。

下例示例展示您可如何使用 MERGE 语句的 Update 子句来更新目标表:

MERGE INTO t_target AS t USING t_source AS s ON t.col_a = s.col_a
WHEN MATCHED THEN UPDATE
SET t.col_b = t.col_b + s.col_b ;

在前一示例中,目标表的名称为 t_target,源表的名称为 t_source。对于在源表与目标表中其 col_a 都有相同的值的连接结果的行,MERGE 语句通过将源表中 col_b 列的值添加到 t_target 表中 col_b 列的当前值来更新 t_target 表。

MERGE 语句的 UPDATE 操作不修改源表,且不可更新目标表中的任何行超过一次。

单个 MERGE 语句可同时组合 UPDATE 与 INSERT 操作,或可同时组合 DELETE 与 INSERT 操作而不需要删除子句。要了解不包括 Update 子句的 MERGE 的不同的示例,请参阅主题 MERGE 的 Delete 子句

对数据库级对其对象的权限

您可使用下列数据库权限来控制谁可访问数据库:

  • 数据库级别权限
  • 表级别权限
  • 例程级别权限
  • 语言级别权限
  • 类型级别权限
  • 序列级别权限
  • 分片级别权限

本部分简要地描述数据库级别和表级别权限。要了解权限的列表以及 GRANT 和 REVOKE 语句的描述,请参阅《GBase 8s SQL 指南:语法》。

数据库级别权限

当您创建数据库时,您是唯一可访问它的人,直到您作为该数据库的所有者或数据库管理员(DBA),将数据库级别权限授予其他人。下表展示数据库级别权限。

权限影响
Connect允许您打开数据库、发出查询以及在临时表上创建和放置索引。
Resource允许您创建永久表。
DBA允许您作为 DBA 执行若干附加的函数。

表级别权限

当您在不符合 ANSI 的数据库中创建表时,所有用户都有访问该表的权限,直到您作为该表的所有者取消特定用户的表级别权限为止。下表介绍控制用户可如何访问表的四种权限。

权限用途
Select逐表授予权限,并允许您从表选择行。(此权限可限定于表中的特定列。)
Delete允许您删除行。
Insert允许您插入行。
Update允许您更新现有的行(即,更改其内容)。

创建数据库和表的人们经常将 Connect 和 Select 权限授予 public,以便所有用户都拥有它们。如果您可查询表,则您至少具有对那个数据库和表的 Connect 和 Select 权限。

您需要其他的表级别权限来修改数据。表的所有者经常保留这些权限,或仅将它们授予特定的用户。因此,您可能无法修改您可自由地查询的一些表。

例如,由于这些权限都是逐表授予的,因此您仅可拥有对一个表的 Insert 权限,以及仅拥有对另一表的 Update 权限。甚至可进一步将 Update 权限限定于表中的特定列。

显示表权限

如果您是表的所有者(即,如果您创建了它),则您拥有对那个表的所有权限。否则,您可通过查询系统目录来确定您对于某个表拥有的权限。系统目录由描述数据库结构的系统表构成。对每一表所授予的权限都记录在 systabauth 系统表中。要显示这些权限,您还必须知道该表的唯一标识符编号。在 systables 系统表中指定此编号。要显示对 orders 表授予的权限,您可输入下列 SELECT 语句:

SELECT * FROM systabauth
WHERE tabid = (SELECT tabid FROM systables
WHERE tabname = 'orders');

该查询的输出类似于下列示例:

grantorgrantee tabid               tabauth

tfecitmutator 101 su-i-x--
tfecitprocrustes101 s--idx--
tfecitpublic 101 s--i-x--

授权者是授予权限的用户。授权者通常是表的所有者,但所有者可为授权者授权了的另一用户。被授权者是将权限授予其的用户,被授权者 public意味着有 Connect 权限的任何用户。如果您的用户名未出现,则您仅拥有授予给了 public 的那些权限。

tabauth 列指定授予的权限。此列的每一行中的字母是权限名称的首字母,除了 i 表示 Insert 以及 x 表示 Index 之外。在此示例中,public 具有 Select、Insert 和 Index 权限。仅用户 mutator 具有 Update 权限,仅用户 procrustes 具有 Delete 权限。

在数据库服务器为您执行任何操作(例如,执行 DELETE 语句)之前,它都执行类似于前一查询的查询。如果您不是该表的所有者,且如果数据库服务器找不到您的用户名或 public 对该表的必要权限,则它拒绝执行该操作。

将权限授予角色

作为 DBA,您可创建角色来使得给予一类用户的权限标准化。当您将权限指定给那个角色时,那个角色的每个用户都拥有那些权限。用于定义和操纵角色的 SQL 语句包括:CREATE ROLE、DROP ROLE、GRANT、REVOKE 和 SET ROLE。要获取关于定义和操纵角色的 SQL 语句的语法的更多信息,请参阅《GBase 8s SQL 指南:语法》。

在连接到数据库时,缺省的角色自动地应用于特定的用户和组,而不要求该用户发出 SET ROLE 语句。例如:

GRANT DEFAULT ROLE manager TO larry;

要获取关于角色与缺省角色的更多信息,请参阅 控制数据库使用 或参阅《GBase 8s 管理员指南》。

要获取关于授予和撤销权限的更多信息,请参阅 授予和撤销应用程序中的权限。

数据完整性

INSERT、UPDATE 和 DELETE 语句修改现有的数据库中的数据。每当您修改现有的数据时,就可影响数据的完整性。例如,可能会将不存在的产品的订单输入到 orders 表内,可能从 customer 表中删除一个有未完成订单的客户,或者可能在 orders 表中更新订单编号,但未在 items 表中更新。在每一这些情况下,都会失去存储的数据的完整性。

数据完整性实际由下列部分组成:

实体完整性

表的每一行都有唯一的标识符。

语义完整性

列中的数据正确地反映设计了该列来保存的信息的类型。

引用完整性

强制执行表之间的关系。

设计良好的数据库体现了这些原则,因此当您修改数据时,数据库本身防止您执行可能损坏数据完整性的任何操作。

实体完整性

实体是要记录在数据库中的任何人、位置或事物。每一表都表示一个实体,且表的每一行都表示那个实体的一个实例。例如,如果 order 是一个实体,则 orders 表表示订单的概念,表中的每一行表示一特定的订单。

要标识表中的每一行,该表必须有一主键。主键是标识每一行的一个唯一值。此要求称为实体完整性约束。

例如,orders 表的主键是 order_num。order_num 列为表中的每一行保存一个唯一的系统生成的订单编号。要访问 orders 表中的一行数据,请使用下列 SELECT 语句:

SELECT * FROM orders WHERE order_num = 1001;

在此语句的 WHERE 子句中使用该订单编号使得您能够容易地访问行,因为该订单编号唯一地标识那一行。如果该表允许重复的订单编号,则它几乎不可能访问单个一行,因为此表的所有其他列都允许重复的值。

语义完整性

语义完整性确保输入到行内的数据反映那一行的允许的值。该值必须在那一行的域或允许的值集之内。例如,items 表的 quantity 列仅允许数值。如果可将该域之外的值输入到列内,则违反该数据的语义完整性。

下列约束强制语义完整性:

数据类型

数据类型定义您可存储在列中的值的类型。例如,数据类型 SMALLINT 允许您将从 -32,767 至 32,767 的值输入到列内。

缺省值

缺省值是当未指定显式的值时插入到该列内的值。例如,如果未输入名称,则 cust_calls 表的 user_id 列的缺省值为该用户的登录名称。

检查约束

检查约束指定对插入到列内的数据的条件。输入到表内的每一行都必须满足这些条件。例如,items 表的 quantity 列可能检查大于或等于 1 的数量。

引用完整性

引用完整性指的是表之间的关系。由于数据库中每一表都必须具有主键,因此此主键可出现在其他的表中,这是因为它与那些表内数据的关系。当来自一个表的主键出现在另一表中时,将它称之为外键。

外键连接表并在表之间创建依赖。若干表可形成依赖的层级结构,这样,如果您更改或删除一个表中的行,则您破坏在其他表中行的含义。例如,下图展示 customer 表的 customer_num 列是那个表的主键,以及 orders 和 cust_call 表中的外键。在 orders 和 cust_calls 表中都引用客户编号 106,George Watson™。如果从 customer 表删除客户 106,则破坏三个表以及此特定的客户之间的链接。

图: 演示数据库中的引用完整性

当您删除包含主键的行,或以不同的主键更新它时,您破坏了包含那个值作为外键的任何行的含义。引用完整性是外键对主键的逻辑依赖。包含外键的行的完整性依赖于它引用的那行的完整性—包含相匹配的主键的行。

在缺省情况下,数据库服务器不允许您违反引用完整性,且如果在您从子表删除行之前,您尝试从父表删除行,则向您提示错误消息。然而,您可使用 ON DELETE CASCADE 选项来在从父表删除的同时对相应的子表进行删除。请参阅 ON DELETE CASCADE 选项。

要定义主键和外键以及它们之间的关系,请使用 CREATE TABLE 和 ALTER TABLE 语句。要获取关于这些语句的更多信息,请参阅《GBase 8s SQL 指南:语法》。

ON DELETE CASCADE 选项

当您从主键为表删除行时,要保持引用完整性,请使用 CREATE TABLE 和 ALTER TABLE 语句的 REFERENCES 子句中的 ON DELETE CASCADE 选项。此选项允许您使用单个删除命令从父表删除一行以及在相匹配的子表中它的对应行。

在级联删除期间锁定

在删除期间,保持父表和子表上的所有符合条件的行上的锁定。当您指定删除时,在执行任何引用操作之前,执行从父表请求的删除。

多个子表的情况

如果您具有带有两个子约束的父表,一个子表指定了级联删除,另一个子表没有级联删除,且您尝试从同时应用于两个子表的父表删除一行,则 DELETE 语句失败,且从父表和子表都不删除行。

必须打开日志记录

为了使级联删除起作用,您必须在您的当前数据库中打开日志记录。在 事务日志记录 中讨论日志记录和级联删除。

级联删除的示例

假设您有应用了引用完整性规则的两个表,父表 accounts,以及子表 sub_accounts。下列 CREATE TABLE 语句定义引用约束:

CREATE TABLE accounts (
acc_num SERIAL primary key,
acc_type INT,
acc_descr CHAR(20));

CREATE TABLE sub_accounts (
sub_acc INTEGER primary key,
ref_num INTEGER REFERENCES accounts (acc_num)
ON DELETE CASCADE,
sub_descr CHAR(20));

accounts 表的主键,acc_num 列,使用 SERIAL 数据类型,sub_accounts 表的外键,ref_num 列,使用 INTEGER 数据类型。允许组合主键上的 SERIAL 与外键上的 INTEGER 数据类型。仅在此条件下,您可混合并匹配数据类型。SERIAL 数据类型是 INTEGER,且数据库自动地为该列生成值。所有其他主键与外键组合都必须显式地相匹配。例如,定义为 CHAR 的主键必须与定义为 CHAR 的外键相匹配。

sub_accounts 表的外键的定义,ref_num 列,包括 ON DELETE CASCADE 选项。此选项指定在父表 accounts 中任何行的删除都将自动地导致删除子表 sub_accounts 的对应行。

要从将级联删除 sub_accounts 表的 accounts 表删除一行,您必须打开日志记录。打开日志记录之后,您可从两个表都删除账户编号 2,如下例所示:

DELETE FROM accounts WHERE acc_num = 2;

对级联删除的限制

对于大多数删除,包括自引用的删除和循环查询的删除,您可使用级联删除。唯一的例外是相关的子查询,相关的子查询是嵌套的 SELECT 语句,子查询(或内部 SELECT)在其中产生的值依赖于包含它的外部 SELECT 语句所产生的值。如果您已实施了级联删除,则您不可在相关的子查询中编写使用子表的删除。当您尝试从相关的子查询删除时,您会收到错误。

限制

如果表使用 ON DELETE CASCADE 定义引用约束,则您不可在该表上定义 DELETE 触发器事件。

对象模式和违反检测

数据库的对象模式和违反检测可帮助您监视数据完整性。在模式更改期间,或当对于短期内大批量数据执行插入、删除和更新操作时组合这些特性,这些特性特别有效。

在对象模式特性的讨论的上下文之内,数据库对象是约束、索引和触发器,且它们中的每一个都有不同的模式。请不要将与对象模式特性相关的数据库对象与一般的数据库对象相混淆。一般的数据库对象是诸如表和同义词之类的对象。

对象模式的定义

您可为约束或唯一索引设置禁用、启用或过滤模式。您可为触发器或重复索引设置启用或禁用模式。您可使用数据库对象模式来控制 INSERT、DELETE 和 UPDATE 语句的效果。

启用模式

在缺省情况下,约束、索引和触发器是启用的。

当数据库对象是启用的时,数据库服务器识别该数据库对象的存在,并在它执行 INSERT、DELETE 或 UPDATE 语句时考虑该数据库对象。因此,当触发器事件发生时,强制执行启用的约束,更新启用的索引,并执行启用的触发器。

当您启用约束和唯一索引时,如果存在违反的行,则该数据处理语句失败(即,不更改行)且数据库服务器返回错误消息。

当您分析违反表和诊断表中的信息时,您可标识该失败的原因。然后,您可采取更正活动或回滚该操作。

禁用模式

当数据库对象是禁用的时,在执行 INSERT、DELETE 或 UPDATE 语句时,数据库服务器不考虑它。当触发器事件发生时,不强制执行禁用的约束,不更新禁用的索引,也不执行禁用的触发器。当您禁用约束和唯一索引时,违反该约束或唯一索引的限制的任何数据操纵语句都成功,(即,更改目标行),且数据库服务器不返回错误消息。

过滤模式

当约束或唯一索引处于过滤模式时,该语句成功,且在 INSERT、DELETE 或 UPDATE 语句期间,通过将失败了的行写到与该目标表相关联的违反表,数据库服务器强制满足约束或唯一索引需求。将关于该约束违反的诊断信息写到与目标表相关联的诊断表。

使用数据操纵语句的模式的示例

一个使用 INSERT 语句的示例可说明启用模式、禁用模式与过滤模式之间的差异。请考虑这样一条 INSERT 语句,其中一个用户试图在表上添加不满足完整性约束的一行。例如,假设用户 joe 创建了名为 cust_subset 的表,且此表由下列列构成:ssn(客户的社会保险编号)、fname(客户的名)、lname(客户的姓)以及 city(客户生活的城市)。ssn 列具有 INT 数据类型。其他三列有 CHAR 数据类型。

假设用户 joe 定义了 lname 列为非空,但尚未将名称指定给非空约束,于是,数据库服务器已隐式地将名称 n104_7 指定给此约束。最后,假设用户 joe 在 ssn 列上创建了名为 unq_ssn 的唯一索引。

现在,对 cust_subset 有 Insert 权限的用户 linda 在此表上输入下列 INSERT 语句:

INSERT INTO cust_subset (ssn, fname, city)
VALUES (973824499, "jane", "los altos");

要更好地理解启用模式、禁用模式与过滤模式之间的区别,您可在下面三个部分中查看前面的 INSERT 语句的结果。

当约束为启用的时,插入操作的结果

如果在 cust_subset 表上的 NOT NULL 约束是启用的,则 INSERT 语句不能在此表中插入新行。而当用户 linda 输入该 INSERT 语句时,她收到下列错误消息:

-292 An implied insert column lname does not accept NULLs.
当约束为禁用时,插入操作的结果

如果在 cust_subset 表上 NOT NULL 约束是禁用的,则用户 linda 发出的 INSERT 操作成功地在此表中插入新行。cust_subset 表的新行有下列列值。

ssnfnamelnamecity
973824499janeNULLlos altos
当约束处于过滤模式时,插入的结果

如果将 cust_subset 表上的 NOT NULL 约束设置为过滤模式,则用户 linda 发出的 INSERT 命令不能在此表中插入新行。而将该新行插入到违反表内,并将描述完整性违反的诊断行添加到诊断表。

假设用户 joe 已为 cust_subset 表启动了违反表和诊断表。违反表名为 cust_subset_vio,诊断表名为 cust_subset_dia。当用户 linda 在cust_subset 目标表上发出 INSERT 语句时,添加到 cust_subset_vio 违反表的新行有下列列值。

ssnfnamelnamecitygbasedbt_tupleidgbasedbt_optypegbasedbt_recowner
973824499janeNULLlos altos1Ilinda

cust_subset_vio 违反表中的此新行有下列特征:

违反表的前四列恰好与目标表的列相匹配。这四列与目标表的对应列有相同的名称和相同的数据类型,它们具有用户 linda 输入的 INSERT 语句提供了的列值。

gbasedbt_tupleid 列中的值 1 是分配给不符合的行的唯一序列标识符。

gbasedbt_optype 列中的值 I 表示操作类型的代码,该操作已导致了创建此不符合的行。特别地,I 代表 INSERT 操作。

gbasedbt_recowner 列中的值 linda 标识发出了导致创建此不符合行的用户。

用户 linda 在 cust_subset 目标表上发出的 INSERT 语句还导致将诊断行添加到 cust_subset_dia 诊断表。添加到诊断表的诊断行有下列列值。

gbasedbt_tupleidobjtypeobjownerobjname
1Cjoen104_7

cust_subset_dia 诊断表中的此新诊断行有下列特征:

通过同时出现在两表中的 gbasedbt_tupleid 列,将此诊断表的行连接到违反表的对应行。值 1 同时出现在两表中的此列。

objtype 列中的值 C 标识违反表中对应行导致的完整性违反的类型。特别地,值 C 代表约束违反。

objowner 列中的值 joe 表示检测到违反完整性的约束的所有者。

objname 列中的值 n104_7 给出对其检测到了完整性违反的约束的名称。

通过连接违反表与诊断表,用户 joe(其拥有 cust_subset 目标表及其相关联的特殊表)或 DBA 可在违反表中发现在 INSERT 语句之后创建了的其 gbasedbt_tupleid 值为 1 的行,且此行违反约束。表的所有者或 DBA 可查询 sysconstraints 系统目录表来确定此约束为 NOT NULL 约束。既然知道该 INSERT 语句失败的原因,用户 joe 或 DBA 便可采取更正行动。

对于一个违反行的多个诊断行

在前面的示例中,仅诊断表中的一行对应于违反表中的新行。然而,当将单个新行添加到违反表时,可将多个诊断行添加到诊断表。例如,如果用户linda 在 INSERT 语句中输入了的 ssn 值(973824499)与 cust_subset 目标表的 ssn 列中现有的值相同,则在违反表中仅会出现一个新行,但会在cust_subset_dia 诊断表中出现下列两个诊断行。

gbasedbt_tupleidobjtypeobjownerobjname
1Cjoen104_7
1Ijoeunq_ssn

诊断表中的两行同时对应于违反表的同一行,因为这两行在 gbasedbt_tupleid 列中都有值 1。然而,第一个诊断行标识用户 linda 发出了的 INSERT 语句导致约束违反,而第二个诊断行标识同一 INSERT 语句导致唯一索引违反。在此第二个诊断行中,objtype 中的值 I 代表唯一索引违反,且objname 列中的值 unq_ssn 给出检测出了完整性违反的索引的名称。

要获取关于如何设置数据库对象模式的更多信息,请参阅《GBase 8s SQL 指南:语法》 中的 SET Database Object Mode 语句。

违反表和诊断表

当您为目标表启动违反表时,在对目标表的 INSERT、UPDATE 和 DELETE 操作期间,违反约束和唯一索引的任何行都不会导致整个操作失败,但会被过滤到违反表。诊断表包含关于由违反表中每一行导致的完整性违反的信息。通过检查这些表,您可标识失败的原因,并通过修正违反或回滚操作来采取更正行动。

在您为目标表创建违反表之后,您不可改变基础表或违反表的列或分片。在您已启动了违反表之后,如果您改变目标表上的约束,则将不符合的行过滤到违反表。

要获取关于如何启动和停止违反表的信息,请参阅《GBase 8s SQL 指南:语法》中的 START VIOLATIONS TABLE 和 STOP VIOLATIONS TABLE 语句。

违反表与数据库对象模式的关系

如果您将定义在表上的约束和唯一索引设置为过滤模式,但您未为此目标表创建违反表和诊断表,则在插入、更新或删除操作期间,违反约束或唯一索引要求的任何行都不过滤到违反表。相反,您会收到错误消息,指示您必须为目标表启动违反表。

类似地,如果您将禁用的约束或禁用的唯一索引设置为启用模式或过滤模式,且您想要能够标识不满足约束或唯一索引要求的现有的行,则在您发出 SET Database Object Mode 语句之前必须创建违反表。

START VIOLATIONS TABLE 语句的示例

下列示例展示执行 START VIOLATIONS TABLE 语句的不同方式。

启动违反表和诊断表而不指定它们的名称

要为演示数据库中名为 customer 的目标表启动违反表和诊断表,请输入下列语句:

START VIOLATIONS TABLE FOR customer;

由于您的 START VIOLATIONS TABLE 语句不包括 USING 子句,因此违反表的缺省名称为 customer_vio,诊断表的缺省名称为customer_dia。customer_vio 表包括下列列:

customer_num
fname
lname
company
address1
address2
city
state
zipcode
phone
gbasedbt_tupleid
gbasedbt_optype
gbasedbt_recowner

customer_vio 表与 customer 表有相同的表定义,除了 customer_vio 表有包含关于导致了坏行的操作的信息的三个附加列之外。

customer_dia 表包括下列列:

gbasedbt_tupleid
objtype
objowner
objname

对于目标表,此列的列表展示诊断表与违反表之间的重要差异。尽管对于目标表中的每列,违约表都有相匹配的列,但诊断表的列不依赖于目标表的模式。由任何 START VIOLATIONS TABLE 语句创建的诊断表始终有以上列表中的四列,带有相同的列名称和数据类型。

启动违反表和诊断表并指定它们的名称

下列语句为名为 items 的目标表启动违反表和诊断表。USING 子句为违反表和诊断表声明显式的名称。违反表将命名为 exceptions,诊断表将命名为 reasons。

START VIOLATIONS TABLE FOR items
USING exceptions, reasons;

指定诊断表中的最大行数

下列语句为名为 orders 的目标表启动违反表和诊断表。当在目标表上执行诸如 INSERT、MERGE 或 SET Database Object Mode 之类的单个语句时,MAX ROWS 子句指定可插入到 orders_dia 诊断表内的最大行数。

START VIOLATIONS TABLE FOR orders MAX ROWS 50000;

如果您未为 START VIOLATIONS TABLE 语句中的 MAX ROWS 指定值,则对诊断表中的行数没有缺省的限制,除了可用的磁盘空间之外。

MAX ROWS 子句仅对于表函数在其中作为诊断表的操作限定行的数目。

对违反表的权限的示例

下列示例说明如何从对目标表的权限的当前集派生对违反表的权限的初始集。

例如,假设我们创建了名为 cust_subset 的表,且此表由下列列组成:ssn(客户的社会保险号码)、fname(客户的名)、lname(客户的姓)以及 city(客户生活的城市)。

在 cust_subset 表上存在下列权限集:

  • 用户 alvin 是该表的所有者。
  • 用户 barbara 具有对该表的 Insert 和 Index 权限。她还具有对 ssn 和 lname 列的 Select 权限。
  • 用户 carrie 具有对 city 列的 Update 权限。她还具有对 ssn 列的 Select 权限。
  • 用户 danny 具有对该表的 Alter 权限。

现在,用户 alvin 为 cust_subset 表启动名为 cust_subset_viols 的违反表和名为 cust_subset_diags 的诊断表,如下:

START VIOLATIONS TABLE FOR cust_subset
USING cust_subset_viols, cust_subset_diags;

数据库服务器授予对 cust_subset_viols 违反表的下列初始权限集:

  • 用户 alvin 是该违反表的所有者,因此他具有对该表的所有表级别权限。
  • 用户 barbara 具有对违反表的 Insert、Delete 和 Index 权限。她还具有对违反表的下列列的 Select 权限:ssn 列、lname列、gbasedbt_tupleid 列、gbasedbt_optype 列和 gbasedbt_recowner 列。
  • 用户 carrie 具有对违反表的 Insert 和 Delete 权限。她还具有对违反表的下列列的 Update 权限:city 列、gbasedbt_tupleid列、gbasedbt_optype 列和 gbasedbt_recowner 列。她具有对违反表的下列列的 Select 权限:ssn 列、gbasedbt_tupleid列、gbasedbt_optype 列和 gbasedbt_recowner 列。
  • 用户 danny 不具有对违反表的任何权限。
对诊断表的权限的示例

下列示例说明如何从对目标表的当前权限集派生对诊断表的初始权限集。

例如,假设名为 cust_subset 的表由下列列组成:ssn(客户的社会保险编号)、fname(客户的名)、lname(客户的姓)以及 city(客户生活的城市)。

对 cust_subset 表存在下列权限集:

  • 用户 alvin 为该表的所有者。
  • 用户 barbara 具有对该表的 Insert 和 Index 权限。她还具有对 ssn 和 lname 列的 Select 权限。
  • 用户 carrie 具有对 city 列的 Update 权限。她还具有对 ssn 列的 Select 权限。
  • 用户 danny 具有对该表的 Alter 权限。

现在,用户 alvin 为 cust_subset 表启动名为 cust_subset_viols 的违反表和名为 cust_subset_diags 的诊断表,如下:

START VIOLATIONS TABLE FOR cust_subset
USING cust_subset_viols, cust_subset_diags;

数据库服务器对于 cust_subset_diags 诊断表授予下列初始的权限集:

  • 用户 alvin 为诊断表的所有者,因此他具有对该表的所有表级别权限。
  • 用户 barbara 具有对诊断表的 Insert、Delete、Select 和 Index 权限。
  • 用户 carrie 具有对诊断表的 Insert、Delete、Select 和 Update 权限。
  • 用户 danny 对诊断表没有权限。

中断了的修改

即使所有软件都没有错误且所有硬件都完全可靠,计算机外部的世界也可干扰它。闪电可能击中建筑物,中断供电并在您的 UPDATE 语句运行期间停止计算机。当磁盘已满或用户提供不正确的数据时,更可能发生的情景是,导致您的多行插入过早停止并产生错误。在任何情况下,每当您修改数据,您必须假设某种不可预测的事件可中断该修改。

当外部原因导致修改中断时,您不可确定该操作完成了多少。即使在单行操作中,您也不可知道是否正确地更新了到达了磁盘的数据或索引。

如果多行修改是一个问题,则多语句修改就更糟。通常在程序中嵌入它们,因此您看不到正在执行的个别 SQL 语句。例如,要在演示数据库中输入新的订单,请执行下列步骤:

  1. 在 orders 表中插入一行。(此插入生成一个订单编号。)
  2. 对于订购的每一商品,在 items 表中插入一行。

存在两种编制订单输入应用程序的方法。一种方法是使它完全是交互的,以便程序立即插入第一行,然后在用户输入时插入每一商品。但这种方法使得操作可能遭遇许多更不可预测的事件:客户的电话电线,用户按错键,用户的终端或计算机断电,等等。

下列列表描述构建订单输入应用程序的正确方法:

  • 以交互方式接受所有数据。
  • 验证数据并展开它(例如,在 stock 和 manufact 中查找代码)。
  • 在屏幕上显示信息以进行检查。
  • 等待操作人员进行最终的提交。
  • 快速地执行插入。

即使使用这些步骤,不可预测的情况还可在它插入该订单之后,但在它完成插入商品之前停止该程序。如果发生那种情况,则数据库处于不可预测的状态:它的数据完整性受到损害。

事务

对所有这些潜在问题的解决方案称为事务。事务是必须或者全部完成,或者根本就不执行的修改的序列。数据库服务器保证在事务的范围内执行的操作或者完整并正确地提交到磁盘,或者将数据库恢复到事务开始之前的同一状态。

事务不仅是对不可预测的故障的保护;当程序检测到逻辑错误时,它还为程序提供一种规避的方法。

事务日志记录

数据库服务器可保持对在事务期间数据库服务器对数据库进行的每一更改的记录。如果发生了取消该事务的情况,则数据库服务器自动地使用这些记录来撤销更改。许多原因可导致事务失败。例如,发出 SQL 语句的程序可失败或被终止。数据库服务器一发现事务失败,失败可能就在重新启动计算机和数据库服务器之后发生,它就使用来自该事务的记录来将数据库返回到之前的同一状态。

保存事务的记录的过程称为事务日志记录,或简称为日志记录。事务的记录,称为日志记录,保存在与数据库分开的磁盘空间部分中。此空间称为逻辑日志,因为该日志记录表示事务的逻辑单元。

GBase 8s 提供下列支持:

  • 在日志记录数据库中创建无日志记录(raw)或日志记录(standard)的表。
  • 使用 ALTER TABLE 语句将表从无日志记录改变为日志记录,或相反。

为了快速加载非常大的表,GBase 8s 支持无日志记录的表。建议您在事务内不使用无日志记录的表。要避免并发问题,在您在事务中使用表之前,请使用 ALTER TABLE 语句来使该表成为 standard(即,日志记录)。

要获取关于 GBase 8s 的无日志记录的表的更多信息,请参阅《GBase 8s 管理员指南》。要了解无日志记录的表的性能优势,请参阅《GBase 8s 性能指南》。要获取关于 ALTER TABLE 语句的信息,请参阅 GBase 8s SQL 指南:语法。

大多数 GBase 8s 数据库不会自动地生成事务记录。DBA 决定数据库是否使用事务日志记录。没有事务日志记录,您就不可回滚事务。

日志记录和级联删除

为了使级联删除起作用,必须在您的数据库中打开日志记录,因为当您指定级联删除时,首先在父表的主键上执行删除。如果在执行父表的主键的行删除之后,但在删除子表的外键的行之前,系统出现故障,则违反引用完整性。如果关闭日志记录,即使是临时地关闭,也不会级联删除。然而,在重新打开日志记录之后,又可级联删除。

GBase 8s 允许您使用 CREATE DATABASE 语句中的 WITH LOG 子句来打开日志记录。

指定事务

您可使用两种方法来用 SQL 语句指定事务的边界。在最常用的方法中,通过执行 BEGIN WORK 语句指定多语句事务的开始。在以 MODE ANSI 选项创建的数据库中,不存在标记事务的开始的需要。总会有一个起作用;您只要指明每一事务的结束。

在两种方法中,要指定成功的事务的结束,请执行 COMMIT WORK 语句。此语句告诉数据库服务器您达到了必须一起成功完成的一系列语句的结束。数据库服务器执行任何必要的操作来确保正确地完成了所有修改并提交到了磁盘。

程序还可通过执行 ROLLBACK WORK 语句来有意地取消事务。此语句请求数据库服务器取消当前事务并撤销任何更改。

当订单输入应用程序创建新订单时,它可以下列方式使用事务:

  • 交互地接受所有数据。
  • 验证并展开它。
  • 等候操作人员进行最终的提交
  • 执行 BEGIN WORK
  • 在 orders 和 items 表中插入行,检查数据库服务器返回的错误代码
  • 如果未发生错误,则执行 COMMIT WORK;否则,执行 ROLLBACK WORK

如果任何外部故障阻止事务的完成,在当系统重启时,部分事务回滚。在所有情况下,该数据库处于不可预测的状态。要么完全地输入新订单,要么根本未输入它。

使用 GBase 8s 数据库服务器来备份和记录日志

通过使用事务,您可确保数据库始终处于一致的状态,且将您的修改正确地记录在磁盘上。但磁盘本身不十分安全。对于机械故障以及洪水、火灾和地震,它都是脆弱的。唯一的保护措施是保存数据的多个副本。这些冗余的副本称为备份副本。

事务日志(也称为逻辑日志)补充数据库的备份副本。它的内容是自从上一次备份数据库以来发生了的所有修改的历史。如果您曾经需要从备份副本恢复数据库,则您可使用事务日志来将数据库向前滚到它最近的状态。

数据库服务器包含完善的特性来支持备份和日志记录。您的数据库服务器归档和备份指南描述这些特性。

数据库服务器对性能和可靠性有严格的要求(例如,它支持在正在使用数据库时制作备份副本。)

数据库服务器管理它自己的磁盘空间,这些空间专用于日志记录。

数据库服务器使用有限的日志文件集来并发地执行所有数据库的日志记录。在事务为活动的时,可将日志文件复制到另一介质(备份)。

数据库服务器从不需要考虑这些设置,因为 DBA 总是从中央位置管理它们。

GBase 8s 支持 onload 和 onunload 实用程序。使用 onunload 实用程序来制作单个数据库或表的个人备份副本。此程序将表或数据库复制到磁带。它的输出由磁盘页的二进制映像组成,如同在数据库服务器中存储了它们。因此,可快速地制作副本,且相应的 onload 程序可快速地恢复该文件。然而,对于任何其他程序,该数据格式没有意义。要获取关于如何使用 onload 和 onunload 实用程序的信息,请参阅《GBase 8s 迁移指南》。

如果您的 DBA 使用 ON-Bar 来创建备份并备份逻辑日志,则您还可能能够使用 ON-Bar 来创建您自己的备份副本。要获取更多信息,请参阅您的《GBase 8s 备份与恢复指南》。

并发和锁定

如果在单个用户工作站中包含您的数据库,而没有网络将它连接到其他计算机,则并发并不重要。在所有其他情况下,您必须允许这样的可能性:当您的程序正在修改数据时,另一程序也正在读取或修改同一数据。并发涉及在同一时间对同一数据的两个或多个独立使用。

在多用户数据库系统中,高级别的并发对良好的性能至关重要。然而,除非在数据的使用上存在控制,否则并发可导致各种负面的影响。程序可能读取过时的数据;即使表面上似乎成功地输入了修改,但修改可能丢失。

要防止此类错误,数据库服务器强加一个锁定系统。锁定是程序可在一块数据上放置的声明或保留。只要数据被锁定,数据库服务器就保证其他程序不可修改它。当另一程序请求该数据时,数据库服务器或者让该程序等待,或者向它返回错误。

要控制锁定对您的数据访问的影响,请使用 SQL 语句的组合:SET LOCK MODE 与或者 SET ISOLATION 或者 SET TRANSACTION。在从程序内阅读关于游标的使用的讨论之后,您可了解这些语句的详细信息。在 SQL 编程 和 通过 SQL 程序修改数据 中讨论游标。要获取关于锁定和并发的更多信息,请参阅 对多用户环境编程。

GBase 8s 数据复制

从更广义上讲,术语数据复制意味着在多个不同的站点,数据库对象多次出现。例如,有一种复制数据的方式是将数据库复制到不同的计算机上的数据库服务器,这样,报告可针对该数据运行,而不干扰正在使用原始数据库的客户机应用程序。

下列列表描述数据复制的优势:

  • 与未复制的远程数据相对,在本地访问复制了的数据的客户机的性能提高,因为它们无需使用网络服务。
  • 使用复制了的数据,提高所有站点的客户机的可用性,因为如果本地的复制了的数据不可用时,尽管是远程地,该数据的一个副本仍可用。

获得这些优势不是没有代价的。与非复制的数据相比,对于复制了的数据,数据复制显然需要更多的存储,且更新复制了的数据可比更新单个对象要花费更多处理时间。

通过显式地指定应发现和更新数据的位置,可在客户机应用程序的逻辑中实际地实现数据复制。然而,归档数据复制的这种方式成本高、容易出错且难以维护。相反,数据复制的概念常常伴随着复制透明。复制透明是在数据库服务器内自动地处理定位和维护数据副本的详细信息的内建功能。

在数据复制的大框架内,GBase 8s 数据库服务器几乎实现整个数据库服务器的透明的数据复制。复制一个数据库服务器管理的所有数据,并动态地在另一数据库服务器上更新,通常位于一远程站点。GBase 8s 数据库服务器的数据复制有时称为热站点备份,因为它提供一种维护整个数据库服务器的备份副本的方法,在发生灾难性故障时,可快速地使用它。

由于数据库服务器提供复制透明,因此您通常不需要关注或注意到数据复制;DBA 会处理它。然而,如果您的机构决定使用数据复制,则您应注意到,在数据复制环境中,存在对于客户机应用程序的特殊连接性事项。在 GBase 8s 管理员指南 中描述这些事项。

总结

通过数据库所有者授予您的权限来控制数据库访问。通常自动地授予您查询数据的权限,但通过特定的 Insert、Delete 和 Update 权限来控制修改数据的能力,以逐个表的方式授予这些权限。

如果对数据库施加数据完整性约束,则您的修改数据的能力受到那些约束的限制。您的数据库级别权限和表级别权限以及任何数据约束控制您可如何以及何时修改数据。此外,数据库的对象模式和违反检测特性也影响您可修改数据的方式,并有助于保持您的数据的完整性。

您可使用 DELETE 语句从表删除一行或多行。它的 WHERE 子句选择这些行;使用带有相同子句的 SELECT 语句来预览这些删除。

TRUNCATE 语句删除表的所有行。

使用 INSERT 语句将行添加到表。您可插入包含特定的列值的单个行,或可插入 SELECT 语句生成的一批行。

使用 UPDATE 语句来修改现有的行的内容。您使用可包括子查询的表达式来指定新的内容,以便您可使用基于其他表或更新了的表自身的数据。该语句有两种形式。在第一种形式中,您逐列地指定新值。在第二种形式中,SELECT 语句或记录变量生成一组新值。

使用 CREATE TABLE 和 ALTER TABLE 语句的 REFERENCES 子句来创建表之间的关系。REFERENCES 子句的 ON DELETE CASCADE 选项允许您使用一个 DELETE 语句来从父表和相关联的子表删除行。

使用事务来防止在修改过程中不可预测的中断,防止数据库处于不确定的状态。当在一事务内执行修改时,会在发生错误之后回滚它们。事务日志还扩展数据库的定期制作的备份副本。如果必须恢复数据库,则它可将数据库返回到最近的状态。

对用户为透明的数据复制提供另一种针对灾难性故障的保护。