还要注意您在 key_dbt 中设置了 DB_DBT_USERMEM 标记,指示检索的键应置于用户指定的内存中。这可以让您使用 ekey 变量来检查该键。
在选择部分内容的最后,我们来讨论一个查询,该查询将返回一个或多个其评判标准不是带键的字段的项。假设
SELECT * FROM employees WHERE state=ca
因为 state 字段上没有键,您只能遍历整个数据库。即意味着执行一个简单的游标遍历循环。
|
DBC *dbc; DBT key_dbt, data_dbt; emp_data *edata; dbp->cursor(dbp, &key_dbt, &data_dbt, &dbc, 0); memset(&key_dbt, 0, sizeof(key_dbt)); memset(&data_dbt, 0, sizeof(data_dbt)); for (ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_FIRST); ret == 0; ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_NEXT)) { /* See if the state field is "ca". */ edata = data_dbt->data; if (strcmp(edata->state, "ca") == 0) /* Keep this record. */ } |
这似乎没有效率,如果没有字段的索引,您别无它法,而且,实际上这就是当您指定一个匹配不带键的字段的查询时 SQL 数据库内部的工作过程。
移除数据
您现在已经了解了如何插入和更改数据以及如何对其进行检索。最后要讨论的是如何移除数据。基本上,有两种不同的方法可用于从数据中删除字节组:如果您知道要移除的项的键(且该项不是该键的众多重复项之一),则可以使用键式删除。如果不知道键,您可以遍历和使用游标删除。从简单情形开始,解雇 Mickey Mouse。
DELETE FROM employees WHERE id= 0010002
这看起来像检索,但我们将使用 del 方法。
|
DBT key_dbt; emp_key ekey; ekey = 0010002; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = &ekey; key_dbt.size = sizeof(ekey); dbp->del(dbp, NULL, &key_dbt, 0); |
您可以选择解雇所有姓 Mouse 的员工。可以用同一方法,因为您有姓的次索引:
|
DELETE FROM employees WHERE last_name = "Mouse" DBT key_dbt; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = "Mouse"; key_dbt.size = strlen(key_dbt.data); dbp->del(dbp, NULL, &key_dbt, 0); |
但这有点过于无情。假如您不想解雇 Mickey,而只想解雇 Minnie Mouse。有可以方便地解雇 Minnie 的方法吗?换言之,如何完成这一任务:
DELETE FROM employees where last_name = "Mouse" AND first_name = "Minnie"
毫不为奇,您要使用游标来遍历数据项目,然后选择要删除的项目。
|
DBT key_dbt, data_dbt; DBC *sdbc; sdbp->cursor(sdbp, NULL, &sdbc, 0); memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = "Mouse"; key_dbt.size = strlen(key_dbt.data); for (ret = sdbc->get(sdbc, &key_dbt, &data_dbt, DB_SET); ret == 0; ret = sdbc->get(sdbc, &key_dbt, &data_dbt, DB_NEXT_DUP)) { edata = data_dbt->data; if (strcmp(edata->first_name, "Minnie") == 0) { /* OK, this is a record we want to delete. */ sdbc->del(sdbc, 0); } } dbp->del(dbp, NULL, &key_dbt, 0); |
现在您应该已经大致了解了如何创建 Oracle Berkeley DB 函数来执行基本的 SQL 命令。Berkeley DB 也有众多的选项和配置,可以为您提供更为精妙的功能。此处只探讨一个话题:如何将数据库操作纳入事务中。
管理事务
先了解事务在(多数) SQL 实施中是如何工作的。当在 SQL 中执行 DML 语句时,它将成为当前事务的一部分。随后的每个语句都将作为该事务的一部分运行。当 SQL 会话结束或应用程序执行 COMMIT 语句时,当前事务将进行提交操作。任何事后时候都可同工作执行 ROLLBACK 语句中断事务。
许多 SQL 实施还包括 AUTOCOMMIT 特性,该特性将每个 DML 语句都视为其自己的事务。当启用了 AUTOCOMMIT 模式后,以下次序
statement 1
COMMIT
statement 2
COMMIT
statement 3
COMMIT
等同于
statement 1
statement 2
statement 3
Berkeley DB 也允许您将数据库操作封装入事务中。与 SQL 不同,您可以不用事务运行 Berkeley DB。事实上,除非显示请求事务,否则运行时不会用到它们。因此,如何告知 Berkeley DB 您要使用事务呢?
请记住,在打开环境时您可以指定一些标记:
|
DB_ASSERT(dbenv->open(dbenv, "my_databases/personnel", DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0644); |
那些标记将针对您的应用程序配置 Berkeley DB。在本例中,您通过指定 DB_INIT_TXN 标记启用了事务。如果省略标记,则应用程序将无需通过事务运行。
Berkeley DB 提供了与 SQL 的 AUTOCOMMIT 类似的特性。 您可以配置整个数据库 (环境) 始终使用环境句柄的 set_flags 方法自动提交:
dbenv->set_flags(dbenv, DB_AUTOCOMMIT, 1);
或者,也可以在一个打开的数据库上指定 DB_AUTOCOMMIT,致使您不会向其显式传递事务的所有后续操作将在事务中运行。
假如您不想自动提交,希望应用程序可以将操作组合成为一个逻辑事务。例如,您想添加 Mickey Mouse 并指定他为经理。
|
INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00, "Main Street", "Disney Land", "CA", 98765); INSERT INTO manages(00000001, 000100002) COMMIT |
(上面表明 id=00000001 的员工将管理 Mickey。)
假设您知道如何执行数据操作,因此我们集中讨论如何指定事务。
首先,必须显式开始一个事务 (与在 SQL 中不同)。创建事务是一个环境操作,因此它是环境句柄的一个方法。该方法将创建一个事务句柄 (DB_TXN)
|
DB_TXN *txn; dbenv->txn_begin(dbenv, NULL, &txn, 0); |
具备事务句柄后,将其传入任何您希望加入事务中的数据库操作:
|
emp_dbp->put(emp_dbp, txn, &key, &data, 0); man_dbp->put(man_dbp, txn, &key, &data, 0); |
然后,您可以调用事务句柄的适当方法来提交或中断事务。
提交事务:
txn->commit(txn, 0);
中断事务:
txn->abort(txn);
两个方法都为破坏性方法,使得事务句柄不可用。
与 SQL 不同, Berkeley DB 也以通过事务来保护 DDL 操作。因此,您可以将 DB_TXN 句柄传送到操作,如 dbenv->dbremove、dbenv->dbrename 和 dbp->open(¡ DB_CREATE ¡)。在这些情形中,DDL 操作将在指定事务的环境中执行,这意味着与其它事务一样可以对其进行提交或中止。
结论
Oracle Berkeley DB 提供的功能与 SQL 数据库的相同,但以完全不同的程序包提供。您可编写程序调用 API,并且整个数据库被直接“嵌入”到应用程序中;即它们运行于同一地址空间。这通常会将性能提升一个数量级。为取得这一成绩将增加应用程序的负担。当应用程序要求非常高的性能或应用程序操纵的数据本身不是关系型时,这通常是最有用的。