10.1 DELETE 语句
要从表删除行,程序执行DELETE语句。DELETE语句可以常规方式以WHERE子句指 定行,或它可引用单个行,通过指定的游标访存最后一行。
每当您删除行时,您必须考虑其他表中的行是否依赖于删除了的行。在修改数据中论述 协调删除的这个问题。当从程序内删除时,问题是一样的。
10. 1. 1直接删除
您可在程序中嵌入DELETE语句。下列示例使用GBase 8s ESQL/C:
EXEC SQL delete from items
WHERE order_num = :onum;
您还可动态地准备和执行同样形式的语句。在任一情况下,该语句直接作用于数据库来影 响一行或多行。
示例中的WHERE子句使用名为onum的主变量的值。通常在该操作之后,将结果发布在 SQLSTATE中以及在sqlca结构中。即使发生错误,SQLERRD数组的第三个元素也包含 删除的行的计数。SQLCODE中的值展示操作的完全成功。如果该值不是负的,则未发生 错误且SQLERRD的第三个元素是满足了 WHERE子句并被删除了的所有行的计数。
当发生错误时,该语句提前终止oSQLSTATE中的值和SQLCODE中的值以及SQLERRD 的第二个元素说明它的原因,且行的计数显示删除了的行数。对于许多错误,由于错误阻 止数据库服务器开始操作,因此那个计数为零。例如,如果不存在命名的表,或如果重命 名WHERE子句中测试的列,则不会尝试任何删除。
然而,在操作开始且处理某些行之后,可发现某些错误。这些错误中最常见的是锁冲突。 在数据库服务器可删除那行之前,它必须获取行上的排他锁。其他程序可能正在使用来自 该表的行,阻止该数据库服务器锁定行。由于锁定的问题影响所有类型的修改,因此在对 多用户环境编程中讨论它。
另外,在删除开始之后,可出现较少见的错误类型。例如,在更新数据库时发生硬件错误。
在修改期间为任何种类的错误做准备的最好方式是使用事务日志记录。万一发生错误,您 可告诉数据库服务器将数据库恢复原样。下列示例是基于直接删除部分中的示例的,将其 扩展为使用事务:
EXEC SQL begin work; |
/*开启事务*/ |
EXEC SQL delete from items |
|
where ordejnum = :onum; |
|
del_result = sqlca.sqlcode; |
/*保存两个错误*/ |
del_isamno = sqlca.sqlerrd[1]; |
/*代码编号*/ |
del_rowcnt = sqlca.sqlerrd[2]; |
/*以及行的计数*/ |
if (del_result < 0) |
/*发现的问题:*/ |
EXEC SQL rollback work; |
/*恢复一切*/ |
else |
/* 一切正常:*/ |
EXEC SQL commit work; |
/*结束事务*/ |
此示例中的关键在于,在程序结束该事务之前,它将重要的返回值保存在sqlca结构中。像 其他SQL语句一样,ROLLBACK WORK和COMMIT WORK语句都在sqlca结构中设 置返回代码。然而,如果您想要报告错误生成的代码,则必须在执行ROLLBACK WORK之 前保存它们。ROLLBACK WORK语句移除所有暂挂的事务,包括它的错误代码。
使用事务的优势在于,不管发生什么错误,数据库都处于已知的、可预测的状态。不存在 修改完成了多少的问题;要不就是都完成了,要不就是都没完成。
在带有日志记录的数据库中,如果用户未启动一个显式的事务,则数据库服务器在语句执 行之前初始化一个内部的事务,并在执行完成或失败后终止该事务。如果语句执行成功, 则提交该内部的事务。如果语句失败,则回滚该内部的事务。
当您必须修改多个表时,事务日志记录的用处特别明显。例如,考虑从演示数据库删除一 个订单的问题。在该问题的最简单的形式中,您必须从两个表orders和items同时删除行, 如下列GBase 8 s ESQL/C的示例所示:
EXEC SQL BEGIN WORK;
EXEC SQL DELETE FROM items
WHERE order_num = :o_num;
if (SQLCODE >= 0)
{
EXEC SQL DELETE FROM orders
WHERE order_num == :o_num;
{
if (SQLCODE >= 0)
EXEC SQL COMMIT WORK;
{
else
{
printf("Error %d on DELETE", SQLCODE);
EXEC SQL ROLLBACK WORK;
}