常常有人問 Oracle Berkeley DB “我如何在 Berkeley DB 中進(jìn)行 ?"因此,我們此處將介紹如何在 Oracle Berkeley DB 中實(shí)施眾多您喜愛的 SQL 功能。不是所有的 SQL 應(yīng)用程序都應(yīng)該在 Oracle Berkeley DB 實(shí)施( Oracle Berkeley DB 是一個(gè)開放源的嵌入式數(shù)據(jù)庫引擎,提供了快速、可靠、本地的持久性,無需管理),但如果您有一系列相對(duì)固定的查詢且很關(guān)心性能,Berkeley DB 將是很好的選擇。
讓我們從頭開始 (很好的起步點(diǎn))。我們從 ABC 開始學(xué)閱讀,在 Berkeley DB 中我們從術(shù)語開始。
這里為堅(jiān)定的 SQL 程序員提供了一個(gè)小型“翻譯指南”:
SQL 術(shù)語 | Oracle Berkeley DB 對(duì)應(yīng)詞匯 |
數(shù)據(jù)庫 | 環(huán)境 |
表 | 數(shù)據(jù)庫 |
字節(jié)組/行 | 鍵/數(shù)據(jù)對(duì) |
主索引 | 鍵 |
次索引 | 次數(shù)據(jù)庫 |
選擇一個(gè)應(yīng)用程序域 — 傳統(tǒng)員工數(shù)據(jù)庫,經(jīng)過一定的簡化。我們進(jìn)一步假定您需要所有 Berkeley DB 的全部額外特性:并發(fā)、事務(wù)、可恢復(fù)性等。
創(chuàng)建數(shù)據(jù)庫
在 SQL 中,您可以執(zhí)行以下命令
CREATE DATABASE personnel
在 Berkeley DB 中, 您想要?jiǎng)?chuàng)建一個(gè)放置所有應(yīng)用程序數(shù)據(jù)的環(huán)境。在代碼中,您將通過一個(gè)環(huán)境句柄來引用環(huán)境,該句柄類型為 DB_ENV。您將使用這一句柄來操作此環(huán)境。現(xiàn)在,將一些精妙的錯(cuò)誤處理過程放在一邊,來集中討論 API。
DB_ENV *dbenv; int ret; /* Create the handle. */ DB_ASSERT(db_env_create(&dbenv, 0) == 0); /* * If you wanted to configure the environment, you would do that here. * Configuraition might include things like setting a cache size, * specifying error handling functions, specifying (different) * directories in which to place your log and/or data files, setting * parameters to describe how many locks you'd need, etc. */ /* Now, open the handle. */ DB_ASSERT(dbenv->open(dbenv, "my_databases/personnel", DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0644); |
您現(xiàn)在創(chuàng)建和打開了一個(gè)環(huán)境。需要注意幾項(xiàng)事情:
· 開始前必須有 my_databases/personnel 目錄。
· open 調(diào)用的最后一個(gè)參數(shù)就是作為此環(huán)境的一部分為您創(chuàng)建的文件的模式。
· 此處指定的標(biāo)記將允許您創(chuàng)建環(huán)境
(DB_CREATE),使用鎖定 (DB_INIT_LOCK);有一個(gè)共享的內(nèi)存緩存池 (DB_INIT_MPOOL);使用事務(wù) (DB_INIT_TXN);并同時(shí)在不同的控制線程中使用得到的環(huán)境句柄 (DB_THREAD)。
在 SQL 中,查詢通常由單獨(dú)的服務(wù)器處理,該服務(wù)器由數(shù)據(jù)庫管理員配置以在您的系統(tǒng)上正常(或不正常)工作。因?yàn)?span lang="EN-US"> Berkeley DB 嵌入到了您的應(yīng)用程序中,因此該應(yīng)用程序可以執(zhí)行許多配置工作。但這確實(shí)與數(shù)據(jù)庫調(diào)優(yōu)有關(guān),我們將另文詳述。
現(xiàn)在創(chuàng)建了數(shù)據(jù)庫,接下來創(chuàng)建一些表。在 Berkeley DB 中, 表由類型為 DB * 的句柄引用 。對(duì)于應(yīng)用程序中的每個(gè)表,通常會(huì)打開一個(gè)句柄,然后在一或多個(gè)線程中使用該句柄。
因此,在 SQL 中可能是
CREATE TABLE employee (primary key empid int(8), last_name varchar(20), first_name varchar(15), salary numeric(10, 2) salary, street varchar (20), city varchar (15), state char(2), zip int(5)) |
在我們討論實(shí)施這一過程的 Berkeley DB 代碼前,要記住在 SQL 中,數(shù)據(jù)庫負(fù)責(zé)實(shí)施和解釋數(shù)據(jù)模式,這很重要。在 Berkeley DB 中,這一解釋由應(yīng)用程序完成。在分析數(shù)據(jù)操作語言 (DML) 時(shí)這將變得更加有趣,但現(xiàn)在其很明顯,因?yàn)樵趧?chuàng)建員工表時(shí),Berkeley DB 只知道主鍵,不知道數(shù)據(jù)庫中的不同域。
首先,您需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫句柄來代表創(chuàng)建的的表。(我們?cè)俅温赃^錯(cuò)誤處理。)
DB *dbp; DB_ENV *dbenv; /* Let's assume we've used the code from above to set dbenv. */ ASSERT(db_create(&dbp, dbenv, 0) == 0); /* * Like with the environment, tables can also be configured. You * can specify things like comparison functions, page-size, etc. * That would all go here. */ /* Now, we'll actually open/create the primary table. */ ASSERT(dbp->open(dbp, NULL, "employee.db", NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0). |
這一調(diào)用將使用 B-樹作為主索引結(jié)構(gòu)創(chuàng)建表。該表將在 my_databases/personnel 目錄中物化,使用名稱 employee.db。文件將包含一個(gè)表并具有由最后的參數(shù) (0644) 指定的文件系統(tǒng)權(quán)限。指定的標(biāo)記在事務(wù)中創(chuàng)建表,允許以后的事務(wù)操作
(DB_AUTO_COMMIT);允許表不存在時(shí)創(chuàng)建表 (DB_CREATE);并指定可由多個(gè)控制線程同時(shí)使用得到的句柄 (DB_THREAD)。
注意,您尚未指定具體由什么組成主鍵 (索引) 或數(shù)據(jù)字段好似存儲(chǔ)于該表中。這都將是應(yīng)用程序的任務(wù),稍后接觸到插入、選擇和更新部分的內(nèi)容時(shí)這將變得更清楚。
現(xiàn)在,我們來看看如果在 employee id 上有一個(gè)主索引同時(shí)對(duì)姓使用一個(gè)次索引時(shí)將會(huì)出現(xiàn)什么情況。
您應(yīng)該使用上述 SQL 查詢并執(zhí)行。
CREATE INDEX lname ON employee (last_name)
在 Berkeley DB 中,次索引就像是表。然后您可以聯(lián)合表,使一個(gè)表成為另一個(gè)表的次索引。為實(shí)施這一功能,需要更深入地了解應(yīng)用程序要使用的數(shù)據(jù)表示。
假設(shè)應(yīng)用程序要使用 C 結(jié)構(gòu)以在我們的員工表中包含字節(jié)組。您可以按下面所示定義結(jié)構(gòu):
typedef struct _emp_data { char lname[20]; char fname[15]; float salary; char street[20]; char city[15]; char state[2]; int zip; } emp_data; |
假設(shè)員工 ID 為簡單的整數(shù):
typedef int emp_key;
在 Berkeley DB 中,操作鍵或數(shù)據(jù)項(xiàng)時(shí),您使用 DBT 結(jié)構(gòu)。DBT 包含了不透明的字節(jié)串,以指針和長度來表示。指針由 DBT 的數(shù)據(jù)字段引用,長度存儲(chǔ)于 DBT 的大小字段中。如果希望操縱表示一個(gè)員工的鍵/數(shù)據(jù)對(duì),您需要將一個(gè) DBT 用于 emp_key,另一個(gè)用于 emp_data。
DBT key_dbt, data_dbt; emp_key ekey; emp_data edata; memset(&key_dbt, 0, sizeof(key_dbt)); memset(&data_dbt, 0, sizeof(data_dbt)); /* * Now make the key and data DBT's reference the key and data * variables. */ key_dbt.data = &ekey; key_dbt.size = sizeof(ekey); data_dbt.data = &edata; data_dbt.size = sizeof(edata); |
這里我們可以了解到 SQL 中的字節(jié)組由鍵/數(shù)據(jù)對(duì)表示,應(yīng)用程序負(fù)責(zé)了解如何解釋這些對(duì)。
有了這些作為背景后,我們返回到次索引的討論。因?yàn)?span lang="EN-US"> Berkeley DB 不了解鍵/數(shù)據(jù)對(duì)中數(shù)據(jù)元素的模式和結(jié)構(gòu),所以需要應(yīng)用程序的幫助來找到我們用作次索引的字段。應(yīng)用程序以回調(diào)函數(shù)方式提供幫助。回調(diào)函數(shù)接受鍵/數(shù)據(jù)對(duì)輸入并返回引用了用作次鍵的值的 DBT。
所以要?jiǎng)?chuàng)建 last_name 的次索引,必須編寫一個(gè)回調(diào)函數(shù),接受鍵/數(shù)據(jù)對(duì)輸入并返回引用了數(shù)據(jù)項(xiàng)的 last_name 字段的 DBT。
int lname_callback(DB *dbp, const DBT *key, const DBT *data, DBT *skey) { emp_data *edata; /* * We know that the opaque byte-string represented by the data DBT * represents one of our emp_data structures, so let's cast it * to one of those so that we can manipulate it. */ edata = data->data; skey->data = edata->lname; skey->size = strlen((edata->lname); return (0); } |
寫完回調(diào)函數(shù)后,可以指定次索引。次索引只是一個(gè)表,所以先創(chuàng)建一個(gè)表:
DB *sdbp; ASSERT(db_create(&sdbp, dbenv, 0) == 0); /* Configure sdbp. */ ASSERT(sdbp->open(sdbp, NULL, "emp_lname.db", NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0); |
再次使用 B 樹結(jié)構(gòu)對(duì)姓進(jìn)行索引,保留以前使用的所有標(biāo)記和模式。
最后,您必須將次索引表與主表(員工表)相關(guān)聯(lián)。請(qǐng)記住,dbp 是員工表的句柄, sdbp 是次索引表的句柄。
ASSERT(dbp->associate(dbp, NULL, sdbp, lname_callback, flags) == 0);
注意事項(xiàng):
· 可以了創(chuàng)建任意多的次索引。重要的問題是次索引降低了插入的速度 (因?yàn)槟仨殲槊總€(gè)次索引創(chuàng)建索引項(xiàng)),但如果使用次鍵值(如,姓)來查詢和選擇字節(jié)組, 它們將大大改進(jìn)查詢的性能。
· 在任何時(shí)候更新主表時(shí),只要打開并關(guān)聯(lián)了次索引,次索引將始終保持最新。但如果忘記打開并關(guān)聯(lián)次索引,然后更改基表,您會(huì)發(fā)現(xiàn)次索引不是最新的。
應(yīng)當(dāng)避免這一情況。
DDL 中的最后兩個(gè)操作是丟棄命令:丟棄索引、表和數(shù)據(jù)庫。
如同在 SQL 中丟棄索引和刪除表一樣,您也能在 Berkeley DB 中完成這些操作。在 SQL 中,您可以執(zhí)行以下命令
DROP TABLE employee
或者
DROP INDEX lname
在 SQL 中丟棄一個(gè)表將丟棄與之關(guān)聯(lián)的所有索引,在 Berkeley DB 中,您必須顯式完成此任務(wù)。幸運(yùn)的是,在 Berkeley DB 中丟棄表或索引是同樣的操作。
移除表前,必須關(guān)閉表上的所有數(shù)據(jù)庫句柄。關(guān)閉表容易;假設(shè)我們要丟棄員工數(shù)據(jù)庫的次索引。先關(guān)閉次索引:
sdbp->close(sdbp, 0)
在發(fā)出數(shù)據(jù)庫句柄的關(guān)閉命令后,不能再使用句柄。
關(guān)閉次索引表后,您可以使用 dbenv 句柄的 dbremove 方法將其移除:
DB_ENV *dbenv; ASSERT(dbenv->dbremove(dbenv, NULL, "emp_lname.db", NULL, DB_AUTO_COMMIT) == 0); |
可以使用同一調(diào)用順序 (closing 和 dbremoving) 來丟棄表。
假設(shè)不想丟棄表,只想更改其名稱。您也可完成這一操作。
與移除一樣,首先要關(guān)閉表句柄:
dbp->close(dbp, 0);
現(xiàn)在您可以更改表名稱:
DB_ENV *dbenv; ASSERT(dbenv->dbrename(dbenv, NULL, "employee.db", NULL, "newemp.db", DB_AUTO_COMMIT) == 0); |
最后,您可能想銷毀數(shù)據(jù)庫。在 SQL 中執(zhí)行
DROP DATABASE personnel
這一命令在 Berkeley DB 中也有對(duì)應(yīng)的命令。
首先,必須關(guān)閉環(huán)境:
ASSERT(dbenv->close(dbenv, 0) == 0);
與關(guān)閉表句柄一樣,當(dāng)關(guān)閉環(huán)境句柄后,將不能使用該句柄。所以,為了丟棄表,您需要?jiǎng)?chuàng)建一個(gè)新句柄,然后使用該句柄移除數(shù)據(jù)庫(環(huán)境)。
ASSERT(db_env_create(&dbenv, 0) == 0); ASSERT(dbenv->remove(dbenv, "my_databases/personnel", 0) == 0); |
至此,我們完成了 SQL 的 DDL 到 Berkeley DB 的轉(zhuǎn)換。接下來,我們將討論如何完成 SQL DML 到 Berkeley DB 的轉(zhuǎn)換。
在 Berkeley DB 中執(zhí)行 SQL DML 操作
我們已經(jīng)介紹了 SQL 的 DDL 和其在 Berkeley DB 中的實(shí)現(xiàn),現(xiàn)在要將數(shù)據(jù)添加到數(shù)據(jù)庫,討論 SQL 的插入、更新和刪除。
在 SQL 中使用插入語句將數(shù)據(jù)插入表:
INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00, "Main Street", "Disney Land", "CA", 98765); |
SQL 插入都變成了數(shù)據(jù)庫或游標(biāo)句柄的 Berkeley DB“put”方法;我們先討論數(shù)據(jù)庫,然后再探討游標(biāo)。
假設(shè)您已經(jīng)打開了一個(gè)表,有一個(gè)數(shù)據(jù)庫句柄 dbp 引用了員工表。現(xiàn)在,雇傭 Mickey Mouse。
DB *dbp; DBT key_dbt, data_dbt; emp_data edata; emp_key ekey; /* Put the value into the employee key. */ ekey = 00010002; /* Initialize an emp_data structure. */ strcpy(edata.lname, "Mouse"); strcpy(edata.fname, "Mickey"); edata.salary = 1000000.00; strcpy(edata.street, "Main Street"); strcpy(edata.city, "Disney Land"); strcpy(edata.state, "CA"); edata.zip = 98765; /* Initialize DBTs */ memset(&key_dbt, 0, sizeof(key_dbt)); memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, assign key and data values to DBTs. */ key->data = &ekey; key->size = sizeof(ekey); data->data = &edata; data->size = sizeof(edata); /* Finally, put the data into the database. */ ASSERT(dbp->put(dbp, NULL, &key_dbt, &data_dbt, DB_AUTO_COMMIT) == 0); |
請(qǐng)注意,如果已經(jīng)有將任何次索引與員工表相關(guān)聯(lián)(如在 SQL 中),則在插入時(shí)將自動(dòng)對(duì)其進(jìn)行更新。
現(xiàn)在,假設(shè)表中有些數(shù)據(jù),您希望對(duì)這些數(shù)據(jù)進(jìn)行更改。例如,要給 Mickey 漲工資!有多種完成方法。
第一個(gè)方法與上面的插入代碼一樣 — 如果您在一個(gè)表上使用 PUT 方法并且該鍵已存在 (且表不允許單鍵有相同的數(shù)值), PUT 將使用新版本代替舊版本。因此,以下步驟將使用新記錄替換 Mickey 的記錄,薪水將為 $2,000,000,而不是 $1,000,000。
/* Put the value into the employee key. */ ekey = 00010002; /* Initialize an emp_data structure. */ strcpy(edata.lname, "Mouse"); strcpy(edata.fname, "Mickey"); edata.salary = 2000000.00; strcpy(edata.street, "Main Street"); strcpy(edata.city, "Disney Land"); strcpy(edata.state, "CA"); edata.zip = 98765; /* Initialize DBTs */ memset(&key_dbt, 0, sizeof(key_dbt)); memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, assign key and data values to DBTs. */ key->data = &ekey; key->size = sizeof(ekey); data->data = &edata; data->size = sizeof(edata); /* Finally, put the data into the database. */ ASSERT(dbp->put(dbp, NULL, &key_dbt, &data_dbt, DB_AUTO_COMMIT) == 0); |
請(qǐng)注意,該方法較麻煩,為完成它,您需要了解數(shù)據(jù)庫中所有其他域的值。因此,不同于
UPDATE employees SET salary = 2000000 WHERE empid = 000100002
其中,您只需知曉 employee ID,而現(xiàn)在需要知曉所有信息。難道在 Berkeley DB 沒有可用方法嗎?答是是有。如果確切地知道要替換的數(shù)據(jù)項(xiàng)字節(jié),您可以使用與等同于更新命令的方法。
要使用此方法,您需要引入游標(biāo)概念。游標(biāo)表示表中的某個(gè)位置。它讓您遍歷表并保留當(dāng)前項(xiàng)的情況,然后再對(duì)其操作。
在 Berkeley DB 中創(chuàng)建游標(biāo)很簡單 — 它是數(shù)據(jù)庫句柄的方法:
DBC *dbc; DB *dbp; ASSERT(dbp->cursor(dbp, NULL, 0) == 0); |
有游標(biāo)后,我們需要將其定位于 Mickey 的記錄,以便能對(duì)其進(jìn)行更新。這等同于 SQL 語句的 WHERE 部分。
DBT key_dbt, data_dbt; emp_data *edata; emp_key ekey; /* We'd like to look up Mickey's key. */ emp_key = 0010002; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = &emp_key; key_dbt.size = sizeof(emp_key); /* * We want the data returned, so we don't need to initialize the * employee data data structure. */ memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, set the cursor to the record with the key emp_key. */ dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET); |
接下來我們就可以更改薪水了 (處理子句的“SET salary=2000000”部分)
/* Change the salary. */ edata = data_dbt->data; edata.salary = 2000000; |
最后,應(yīng)用 SQL 語句的 UPDATE 部分:
dbc->c_put(dbc, &key_dbt, &data_dbt, DB_CURRENT);
在本例中,您事先不知道 Mickey 的記錄內(nèi)容,因此需要檢索然后再更新。
或者,甚至無需檢索記錄。DBT 上的 DB_DBT_PARTIAL 標(biāo)記值指示您在獲取/插入記錄的一部分,所以 Berkeley DB 可以忽略除該部分外的所有內(nèi)容。
再試一次:
emp_data edata; float salary; /* We'd like to look up Mickey's key. */ emp_key = 0010002; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = &emp_key; key_dbt.size = sizeof(emp_key); |
不檢索整個(gè)記錄,不檢索任何東西 — 即執(zhí)行 PARTIAL 獲取,指定您只需要 0 字節(jié)的數(shù)據(jù)項(xiàng)。
/* We don't want the data, we just want to position the cursor. */ memset(&data_dbt, 0, sizeof(data_dbt)); data_dbt->flags = DB_DBT_PARTIAL; data_dbt->dlen = 0; /* Position the cursor on Mickey's record */ dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET); /* * Now, prepare for a partial put. Note that the DBT has already * been initialized for partial operations. We need to specify * where in the data item we wish to place the new bytes and * how many bytes we'd like to replace. */ salary = 2000000.00; /* The DBT contains just the salary information. */ data_dbt->data = &salary; data_dbt->size = sizeof(salary); /* * dlen and doff tell Berkeley DB where to place this information * in the record. dlen indicates how many bytes we are replacing -- * in this case we're replacing the length of the salary field in * the structure (sizeof(emp_data.salary)). doff indicates where * in the data record we will place these new bytes -- we need to * compute the offset of the salary field. */ data_dbt->dlen = sizeof(emp_data.salary); data_dbt->doff = ((char *)&edata.salary - (char *)&edata); /* Now, put the record back with the new data. */ dbc->c_put(dbc, &key_dbt, &data_dbt, DB_CURRENT); |
數(shù)據(jù)檢索
了解如何向表插入數(shù)據(jù)后,現(xiàn)在學(xué)習(xí)如何檢索它。讓我們從最簡單的方法開始:根據(jù)其主鍵查找值。
SELECT * FROM employees WHERE id=0010002
您已經(jīng)知道如何使用游標(biāo)來完成此任務(wù):
DBT key_dbt, data_dbt; emp_data *edata; emp_key ekey; /* We'd like to look up Mickey's key. */ emp_key = 0010002; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = &emp_key; key_dbt.size = sizeof(emp_key); /* * We want the data returned, so we don't need to initialize the * employee data data structure. */ memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, set the cursor to the record with the key emp_key. */ dbc->c_get(dbc, &key_dbt, &data_dbt, DB_SET); |
使用上面的游標(biāo)操作,因?yàn)槲覀兩院笠略撚涗洝<偃缰幌霗z索記錄,則甚至不需要游標(biāo)。所需要的是 dbp 句柄的 get 方法:
DBT key_dbt, data_dbt; emp_data *edata; emp_key ekey; /* We'd like to look up Mickey's key. */ emp_key = 0010002; memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = &emp_key; key_dbt.size = sizeof(emp_key); /* * We want the data returned, so we don't need to initialize the * employee data data structure. */ memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, use the dbp method. */ dbp->get(dbp, NULL, &key_dbt, &data_dbt, 0); |
因此,這與上面的 SELECT 表達(dá)式一樣。
到目前為止,我們都是根據(jù)主鍵來查找記錄。如果不知道主鍵,該怎么辦?下面提供了幾種方法:
使用次鍵值查找記錄。
遍歷共享同一鍵的項(xiàng)目。
遍歷整個(gè)數(shù)據(jù)庫。
下面詳述以上方法。
使用次鍵
與在 SQL 中一樣,根據(jù)次鍵檢索和使用主鍵非常類似。
事實(shí)上,SQL 查詢看起來相同,除了 where 子句:
SELECT * FROM employees WHERE last_name = "Mouse"
Berkeley DB 調(diào)用與其對(duì)等的主鍵調(diào)用類似。
它使用的不是主鍵示例中的 dbp,而是使用 sdbp 根據(jù)次鍵來進(jìn)行查找:
DBT key_dbt, data_dbt; emp_data *edata; /* We'd like to look up by Mickey's last name. */ memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = "Mouse"; key_dbt.size = strlen((char *)key_dbt.data); /* * We want the data returned, so we don't need to initialize the * employee data data structure. */ memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, call the get method. */ sdbp->get(sdbp, NULL, &key_dbt, &data_dbt, 0); |
data_dbt 中返回的內(nèi)容非常有趣。返回的是主數(shù)據(jù)庫中的數(shù)據(jù) — 即在數(shù)據(jù) DBT 中返回了同樣的東西,不管您是使用主鍵還是次鍵查找。
但是,您會(huì)發(fā)現(xiàn)按次鍵查找時(shí),得到的結(jié)果與按主鍵檢索或 SQL 語句得到的結(jié)果有所不同。主鍵丟失,因?yàn)闆]有位置來返回它。所以,上面的代碼實(shí)際上實(shí)施
SELECT last_name, first_name, salary, street, city, state, zip FROM employees WHERE last_name="Mouse" |
如果您需要主鍵,該如何做?答案是使用 dbp->pget 或 dbc->pget 方法。這兩個(gè)方法與 get 方法一樣,只是它們專門設(shè)計(jì)用于您需要返回主鍵時(shí)的次索引查詢。因此,在這一情形中,結(jié)果中會(huì)包括主鍵、次鍵和數(shù)據(jù)元素:
DBT key_dbt, pkey_dbt, data_dbt; emp_data *edata; /* We'd like to look up by Mickey's last name. */ memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = "Mouse"; key_dbt.size = strlen((char *)key_dbt.data); /* Set up the dbt into which to return the primary. */ memset(&pkey_dbt, 0, sizeof(pkey_dbt)); /* * We want the data returned, so we don't need to initialize the * employee data data structure. */ memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, get the record and the primary key. */ sdbp->pget(sdbp, NULL, &key_dbt, &pkey_dbt, &data_dbt, 0); |
該代碼等同于 SQL 次選擇:
SELECT * FROM employees WHERE last_name="Mouse"
遍歷多個(gè)記錄
到現(xiàn)在為止,我們僅返回了一個(gè)記錄。SQL 允許您返回多個(gè)記錄 (換言之,姓為 Mouse 的所有員工)。如何在 Berkeley DB 中實(shí)現(xiàn)此目的?
讓我們考慮兩種情形。第一個(gè)情形,按照鍵來查找項(xiàng)目。第二個(gè)情形,搜索數(shù)據(jù)庫,按照不帶鍵的字段查找項(xiàng)目。
假設(shè)您希望查找所有姓 Mouse 的員工(假設(shè)有多個(gè))。這意味著已經(jīng)創(chuàng)建了 last_name 次索引,從而允許重復(fù)。打開數(shù)據(jù)庫前,需要對(duì)它進(jìn)行配置以支持重復(fù):
sdbp->set_flags(sdbp, DB_DUP); ASSERT(sdbp->open(sdbp, NULL, "emp_lname.db", NULL, DB_BTREE, DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0); |
現(xiàn)在,按次索引檢索時(shí),您可能想使用游標(biāo)來完成。開始時(shí)使用前面使用的代碼,可以添加一個(gè)循環(huán)以遍歷共享同一次鍵的項(xiàng)目:
DBT key_dbt, data_dbt; DBC *sdc; emp_data *edata; /* We'd like to look up by Mickey's last name. */ memset(&key_dbt, 0, sizeof(key_dbt)); key_dbt.data = "Mouse"; key_dbt.size = strlen((char *)key_dbt.data); /* * We want the data and primary key returned, so we need only * initialize the DBTs for them to be returned. */ memset(&data_dbt, 0, sizeof(data_dbt)); memset(&pkey_dbt, 0, sizeof(pkey_dbt)); /* Now, create a cursor. */ sdbp->cursor(sdbp, NULL, &sdbc, 0); /* Now loop over all items with the specified key. */ for (ret = sdbc->pget(sdbc, &key_dbt, &pkey_dbt, &data_dbt, DB_SET); ret == 0: ret = sdbc->pget(sdbc, &key_dbt, &pkey_dbt, &data_dbt, DB_NEXT_DUP) { /* Do per-record processing in here. */ } |
通過使用游標(biāo)查找?guī)е付ㄦI的第一個(gè)項(xiàng)目,完成游標(biāo)的初始化,然后遍歷數(shù)據(jù)庫中帶同一鍵的所有項(xiàng)目。
另一個(gè)可能的根據(jù)鍵進(jìn)行遍歷的形式為查詢形式,如
SELECT * FROM employees WHERE id >= 1000000 AND id < 2000000
再次使用游標(biāo)來遍歷,但這一次您想創(chuàng)建一個(gè)起始和結(jié)束點(diǎn)。Berkeley DB 使得起始點(diǎn)的建立非常簡單,結(jié)束點(diǎn)由應(yīng)用程序來完成。
DBT key_dbt, data_dbt; DBC *dc; emp_key ekey; /* Set the starting point. */ memset(&key_dbt, 0, sizeof(key_dbt)); ekey = 1000000; key_dbt.data = &ekey; key_dbt.size = sizeof(ekey); key_dbt.flags = DB_DBT_USERMEM; key_dbt.ulen = sizeof(ekey); memset(&data_dbt, 0, sizeof(data_dbt)); /* Now, create a cursor. */ dbp->cursor(dbp, NULL, &dbc, 0); /* Now loop over items starting with the low key. */ for (ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_SET_RANGE); ret == 0: ret = dbc->get(dbc, &key_dbt, &data_dbt, DB_NEXT)) { /* Check if we are still in the range. */ if (ekey >= 2000000) break; /* Do per-record processing in here. */ } |
要注意兩點(diǎn):1) 以 DB_SET_RANGE 標(biāo)記作為循環(huán)的開始,這會(huì)將游標(biāo)定位于第一個(gè)大于或等于指定鍵的項(xiàng)目; 2) 應(yīng)用程序必須檢查循環(huán)內(nèi)范圍的終點(diǎn)。