2007年2月23日金曜日

SQLiteのつくりかた。

VC++ 2005 Express Editionでもできるstatic-libをこしらえる。

  1. SQLiteのぺぇぢからソースを拾ってくる。
    sqlite-source-#_#_##.zip なーんて名前のやつ。
    テキトーなディレクトリに展開しとく。
  2. VC++IDEでプロジェクトを起こす。Win32スタティック・ライブラリでよかろぉ。

  3. ソースやらヘッダやら、一切合財プロジェクト・ディレクトリにコピーする。
  4. プロジェクトにソース(*.c)を追加。
    このとき tclsqlite.c および shell.c は選択しない。

  5. 同様にヘッダ(*.h)を追加。とにかくぜーんぶ投げ込みゃえぇ。

  6. プロジェクト・プロパティを設定。
    _CRT_SECURE_NO_DEPRECATETHREADSAFE を定義する。

  7. やおらbuild。warningがぐちゃぐちゃ出るがスルーの方向でソリューション・ディレクトリ/releaseSQLite.lib が出来上がったハズ。これで完了。
    こいつとヘッダ:sqlite3.h があれば使えるから。
  8. てすとがてらお試し。
    このソリューションにもひとつWin32コンソールアプリケーション:shellを追加する。

  9. shell.cをプロジェクトに追加。

    プロジェクトの依存関係を設定。

  10. buildして実行!

  11. おまけ。上記SQLをC/C++-APIで呼び出す
    実際にSQLiteをアプリケーションで利用する際のすげー簡単な例。
    #include 
    #include
    #include
    #include

    int main() {
    sqlite3* db;

    /*
    ** databaseをオープン。
     ** ファイル名を ":memory:" とすれば、on-memory データベースとなる。
    */

    if ( sqlite3_open(":memory:", &db) ) {
    std::cerr << sqlite3_errmsg(db) << std::endl;
    sqlite3_close(db);
    return 1;
    }

    /*
    ** TABLE を生成する。
    ** sqlite_exec の結果が SQLITE_OK でないとき、エラーメッセージが errmsg に返される。
    ** このエラーメッセージは sqlite_free で解放すべし。
    */

    char* errmsg;
    if ( sqlite3_exec(db,
    "CREATE TABLE wankuma (id INTEGER PRIMARY KEY, name TEXT, place TEXT)",
    0, 0, &errmsg) != SQLITE_OK ) {
    std::cerr << errmsg << std::endl;
    sqlite3_free(errmsg);
    sqlite3_close(db);
    return 1;
    }

    /*
    ** レコードを追加する。
    ** sqlite3_prepare によってSQL文をコンパイル。
    ** SQL文中の ? の箇所に sqlite3_bind_xxxx で値をバインド(差し込み)する。
    */

    sqlite3_stmt* statement;
    sqlite3_prepare(db, "INSERT INTO wankuma (name, place) VALUES ( ?, ? )", -1, &statement, NULL);
    const char* data[] = {
    "episteme", "yokohama",
    "naka", "oosaka",
    "janne", "nagoya",
    0, 0
    };

    /*
    ** 値のバインドと実行。
    ** sqlite3_reset のに続いて sqlite3_bind_xxx によってバインドする。
    ** このとき最初の ? はインデクス 1 であることに留意すべし。
    ** 引き続いて sqlite3_step によって実行される。
    ** 一連の実行の後、sqlite3_finalize すること。
    */

    for ( const char** p = data; *p != 0;) {
    sqlite3_reset(statement);
    sqlite3_bind_text(statement, 1, *p, (int)strlen(*p), SQLITE_STATIC); ++p;
    sqlite3_bind_text(statement, 2, *p, (int)strlen(*p), SQLITE_STATIC); ++p;
    sqlite3_step(statement);
    }
    sqlite3_finalize(statement);

    /*
    ** QUERY を行う。
    ** sqlite3_step が一つ以上の結果を返すとき、SQLITE_ROW を返す。
    ** 従って sqlite3_step の戻り値が SQLITE_ROW である間繰り返すことで
    ** 複数の結果を取り出す。
    */

    sqlite3_prepare(db, "SELECT * FROM wankuma", -1, &statement, NULL);
    while ( sqlite3_step(statement) == SQLITE_ROW ) {
    std::cout << std::setw( 3) << sqlite3_column_int(statement, 0)
    << std::setw(10) << sqlite3_column_text(statement,1)
    << std::setw(10) << sqlite3_column_text(statement,2)
    << std::endl;
    }
    sqlite3_finalize(statement);

    /*
    ** databaseをクローズする。
    */

    sqlite3_close(db);
    return 0;
    }

2007年2月22日木曜日

SQLite中如何用api操作blob类型的字段

在实际的编程开发当中我们经常要处理一些大容量二进制数据的存储,如图片或者音乐等等。对于这些二进制数据(blob字段)我们不能像处理普通的文本那样简单的插入或者查询,为此SQLite提供了一组函数来处理这种BLOB字段类型。下面的代码演示了如何使用这些API函数。

首先我们要建立一个数据库:

sqlite3_exec(db, "CREATE TABLE list (fliename varchar(128) UNIQUE, fzip blob);", 0, 0, &zErrMsg);

//由于mmmm.rar是一个二进制文件,所以要在使用insert语句时先用?号代替

sqlite3_prepare(db, "insert into list values ('mmmm.rar',?);", -1, &stat, 0);

FILE *fp;

long filesize = 0;

char * ffile;

fp = fopen("mmmm.rar", "rb");

if(fp != NULL)

{

//计算文件的大小

fseek(fp, 0, SEEK_END);

filesize = ftell(fp);

fseek(fp, 0, SEEK_SET);

//读取文件

ffile = new char[filesize+1];

size_t sz = fread(ffile, sizeof(char), filesize+1, fp);

fclose(fp);

}

//将文件数据绑定到insert语句中,替换“?”部分

sqlite3_bind_blob(stat, 1, ffile, filesize, NULL);

//执行绑定之后的SQL语句

sqlite3_step(stat);

这时数据库当中已经有了一条包含BLOB字段的数据。接下来我们要读取这条数据:

//选取该条数据

sqlite3_prepare(db, "select * from list;", -1, &stat, 0);

sqlite3_step(stat);

//得到纪录中的BLOB字段

const void * test = sqlite3_column_blob(stat, 1);

//得到字段中数据的长度

int size = sqlite3_column_bytes(stat, 1);

//拷贝该字段

sprintf(buffer2, "%s", test);

此时可以将buffer2写入到文件当中,至此BLOB数据处理完毕。

下载本文所附的代码

2007年2月21日水曜日

SQLite语法备忘录

SQLite内建语法表

结构定义
CREATE TABLE

创建新表。

语法:

sql-command ::= CREATE [TEMP | TEMPORARY] TABLE table-name (
column-def [, column-def]*
[, constraint]*
)
sql-command ::= CREATE [TEMP | TEMPORARY] TABLE [database-name.] table-name AS select-statement
column-def ::= name [type] [[CONSTRAINT name] column-constraint]*
type ::= typename |
typename ( number ) |
typename ( number , number )
column-constraint ::= NOT NULL [ conflict-clause ] |
PRIMARY KEY
[sort-order] [ conflict-clause ] |
UNIQUE
[ conflict-clause ] |
CHECK (
expr ) [ conflict-clause ] |
DEFAULT
value |
COLLATE
collation-name
constraint ::= PRIMARY KEY ( column-list ) [ conflict-clause ] |
UNIQUE (
column-list ) [ conflict-clause ] |
CHECK (
expr ) [ conflict-clause ]
conflict-clause ::= ON CONFLICT conflict-algorithm

CREATE VIEW

创建一个视图(虚拟表),该表以另一种方式表示一个或多个表中的数据。

语法:

sql-command ::= CREATE [TEMP | TEMPORARY] VIEW [database-name.] view-name AS select-statement

例子:
CREATE VIEW master_view AS
SELECT * FROM sqlite_master WHERE type='view';
说明:
创建一个名为master_view的视图,其中包括sqlite_master这个表中的所有视图表。

CREATE TRIGGER

创建触发器,触发器是一种特殊的存储过程,在用户试图对指定的表执行指定的数据修改语句时自动执行。

语法:

sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name [ BEFORE | AFTER ]
database-event ON [database-name .] table-name
trigger-action
sql-statement ::= CREATE [TEMP | TEMPORARY] TRIGGER trigger-name INSTEAD OF
database-event ON [database-name .] view-name
trigger-action
database-event ::= DELETE |
INSERT
|
UPDATE
|
UPDATE OF
column-list
trigger-action ::= [ FOR EACH ROW | FOR EACH STATEMENT ] [ WHEN expression ]
BEGIN
trigger-step ; [ trigger-step ; ]*
END
trigger-step ::= update-statement | insert-statement |
delete-statement | select-statement

例子:
CREATE TRIGGER update_customer_address UPDATE OF address ON customers
BEGIN
UPDATE orders SET address = new.address WHERE customer_name = old.name;
END;
说明:
创建了一个名为update_customer_address的触发器,当用户更新customers表中的address字段时,将触发并更新orders表中的address字段为新的值。
比如执行如下一条语句:
UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';
数据库将自动执行如下语句:
UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

CREATE INDEX

为给定表或视图创建索引。

语法:

sql-statement ::= CREATE [UNIQUE] INDEX index-name
ON
[database-name .] table-name ( column-name [, column-name]* )
[ ON CONFLICT conflict-algorithm ]
column-name ::= name [ COLLATE collation-name] [ ASC | DESC ]

例子:
CREATE INDEX idx_email ON customers (email);
说明:
为customers表中的email创建一个名为idx_email的字段。

结构删除
DROP TABLE

删除表定义及该表的所有索引。

语法:

sql-command ::= DROP TABLE [database-name.] table-name

例子:
DROP TABLE customers;
DROP VIEW

删除一个视图。

语法:

sql-command ::= DROP VIEW view-name

例子:
DROP VIEW master_view;

DROP TRIGGER

删除一个触发器。

语法:

sql-statement ::= DROP TRIGGER [database-name .] trigger-name

例子:
DROP TRIGGER update_customer_address;

DROP INDEX

删除一个索引。

语法:

sql-command ::= DROP INDEX [database-name .] index-name

例子:
DROP INDEX idx_email;

数据操作
INSERT

将新行插入到表。

语法:

sql-statement ::= INSERT [OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] VALUES(value-list) |
INSERT
[OR conflict-algorithm] INTO [database-name .] table-name [(column-list)] select-statement

UPDATE

更新表中的现有数据。

语法:

sql-statement ::= UPDATE [ OR conflict-algorithm ] [database-name .] table-name
SET
assignment [, assignment]*
[WHERE expr]
assignment ::= column-name = expr

DELETE

从表中删除行。

语法:

sql-statement ::= DELETE FROM [database-name .] table-name [WHERE expr]

SELECT

从表中检索数据。

语法:

sql-statement ::= SELECT [ALL | DISTINCT] result [FROM table-list]
[WHERE expr]
[GROUP BY expr-list]
[HAVING expr]
[compound-op select]*
[ORDER BY sort-expr-list]
[LIMIT integer [( OFFSET | , ) integer]]
result ::= result-column [, result-column]*
result-column ::= * | table-name . * | expr [ [AS] string ]
table-list ::= table [join-op table join-args]*
table ::= table-name [AS alias] |
(
select ) [AS alias]
join-op ::= , | [NATURAL] [LEFT | RIGHT | FULL] [OUTER | INNER | CROSS] JOIN
join-args ::= [ON expr] [USING ( id-list )]
sort-expr-list ::= expr [sort-order] [, expr [sort-order]]*
sort-order ::= [ COLLATE collation-name ] [ ASC | DESC ]
compound_op ::= UNION | UNION ALL | INTERSECT | EXCEPT

REPLACE

类似INSERT

语法:

sql-statement ::= REPLACE INTO [database-name .] table-name [( column-list )] VALUES ( value-list ) |
REPLACE INTO
[database-name .] table-name [( column-list )] select-statement

事务处理
BEGIN TRANSACTION

标记一个事务的起始点。

语法:

sql-statement ::= BEGIN [TRANSACTION [name]]

END TRANSACTION

标记一个事务的终止。

语法:

sql-statement ::= END [TRANSACTION [name]]

COMMIT TRANSACTION

标志一个事务的结束。

语法:

sql-statement ::= COMMIT [TRANSACTION [name]]

ROLLBACK TRANSACTION

将事务回滚到事务的起点。

语法:

sql-statement ::= ROLLBACK [TRANSACTION [name]]

其他操作
COPY

主要用于导入大量的数据。

语法:

sql-statement ::= COPY [ OR conflict-algorithm ] [database-name .] table-name FROM filename
[ USING DELIMITERS delim ]

例子:
COPY customers FROM customers.csv;
EXPLAIN

语法:

sql-statement ::= EXPLAIN sql-statement

PRAGMA

语法:

sql-statement ::= PRAGMA name [= value] |
PRAGMA
function(arg)

VACUUM

语法:

sql-statement ::= VACUUM [index-or-table-name]

ATTACH DATABASE

附加一个数据库到当前的数据库连接。

语法:

sql-statement ::= ATTACH [DATABASE] database-filename AS database-name

DETTACH DATABASE

从当前的数据库分离一个使用ATTACH DATABASE附加的数据库。

语法:

sql-command ::= DETACH [DATABASE] database-name

SQLite内建函数表

算术函数
abs(X) 返回给定数字表达式的绝对值。
max(X,Y[,...]) 返回表达式的最大值。
min(X,Y[,...]) 返回表达式的最小值。
random(*) 返回随机数。
round(X[,Y]) 返回数字表达式并四舍五入为指定的长度或精度。
字符处理函数
length(X) 返回给定字符串表达式的字符个数。
lower(X) 将大写字符数据转换为小写字符数据后返回字符表达式。
upper(X) 返回将小写字符数据转换为大写的字符表达式。
substr(X,Y,Z) 返回表达式的一部分。
randstr()
quote(A)
like(A,B) 确定给定的字符串是否与指定的模式匹配。
glob(A,B)
条件判断函数
coalesce(X,Y[,...])
ifnull(X,Y)
nullif(X,Y)
集合函数
avg(X) 返回组中值的平均值。
count(X) 返回组中项目的数量。
max(X) 返回组中值的最大值。
min(X) 返回组中值的最小值。
sum(X) 返回表达式中所有值的和。
其他函数
typeof(X) 返回数据的类型。
last_insert_rowid() 返回最后插入的数据的ID。
sqlite_version(*) 返回SQLite的版本。
change_count() 返回受上一语句影响的行数。
last_statement_change_count()

开源嵌入式数据库Bericel ey DB和SQLite的比较

深入分析、比较Berkeley DBsQLiteBerkel ey DBsQLite是源码开放的嵌入式数据库管理系统,无需安装,体积小巧,速度又很快;可以很方便地应用在掌上电脑、PDA、车载设备、移动电话等MySQLSQL Server这些大中型数据库不可实现的嵌入式设备上。

关键词Berkeley DB SOLite 嵌入式数据库

1 嵌入式数据库

通常,我们采用数据库来实现对数据的存储、检索等功能。像MySQL这类基于CS结 构的关系型数据库系统,虽然代表着目前数据库应用的主流,却并不能满足所有应用场合的需要。很多的应用,仅仅利用到了这些数据库产品的基本特性而已。有时 我们需要的可能只是一个简单的基于磁盘文件的数据库系统,这样就不必安装庞大的数据库服务器,以简化数据库应用程序的设计。在某些特殊应用场合,比如在嵌 入式系统中,由于系统的硬件软件资源都有限,这些数据库产品就明显有一些臃肿,甚至是不可实现的。在这些情况下,嵌入式数据库的优势就特别明显了。

嵌入式数据库通常与操作系统和具体应用集成在一起,无须独立运行的数据库引擎,由程序直接调用相应的API去实现对数据的存取操作。更直白地讲,嵌入式数据库是一种具备了基本数据库特性的数据文件。嵌入式数据库与其它数据库产品的区别是,前者是程序驱动式,而后者是引擎响应式。嵌入式数据库的一个很重要的特点是它们的体积非常小,编译后的产品也不过几十KB,在一些移动设备上极具竞争力。

从目前嵌入式应用的发展趋势来看,嵌入式数据库的实现必须充分体现系统的可定制性,即系统选择的技术路线要面向具体的行业应用,因而研究源码开放的嵌入式数据库具有特殊意义。

2 Berkeley DBSQLite

DBkeley DB是一款健壮的、高速的工业级开放源代码的嵌入式数据库管理系统。应用它,程序员只需要调用一些简单的API就可以完成对数据的访问和管理。

Berkeley DB的源代码有CJava两种,函数库本身只有300KB左右,但却能够用来管理多达256TB的数据。Berkeley DB作为一种嵌入式数据库系统在许多方面有着独特的优势。首先,由于其应用程序和数据库管理系统运行在相同的进程空间当中,进行数据操作时可以避免繁琐的进程间通信,因此耗费在通信上的开销自然也就降低到了极低程度。其次,Berkeley DB使用简单的函数调用接口来完成所有的数据库操作,而不是在数据库系统中经常用到的SQL语言,避免了对结构化查询语言进行解析和处理所需的开销。

SQLite的源代码是C,其源代码完全开放。SQLite第一个Alpha版本诞生于20005月。今年5月,SQLite又迎来了一个新的里程一SOLite 3

SQLite有以下特性:支持ACID事务;零配置一无需安装和管理配置;储存在单一磁盘文件中的一个完整的数据库;数据库文件可以在不同字节顺序的机器间自由共享;支持数据库大小至2TB;足够小,全部源码大致3万行c代码,250KB;比目前流行的大多数数据库对数据的操作要快;提供了对事务功能和并发处理的支持,应用Transaction既保证了数据的完整性,也会提高运行速度,因为多条语句一起提交给数据库的速度会比一条一条的提交方式更快;独立,没有额外依赖。

目前,对Berkeley DB的研究开发工作主要是美国的sleepycat公司在进行,在国内几乎没有关于这方面的研究;而SQLite在国内也是鲜有人问津。

21 Berkeley DBSOLite的数据库操作

与常用的数据库管理系统(MySQLOracle)有所不同,在Berkeley DB中并没有数据库服务器的概念。应用程序不需要事先同数据库服务建立起网络连接,而是通过内嵌在程序中的Berkeley DB函数库来完成对数据的保存、查询、修改和删除等操作。所有与数据库相关的操作都由函数库负责统一完成,这样无论是系统中的多个进程,或者是相同进程中的多个线程,都可以在同一时间调用访问数据库的函数;而底层的数据加锁、事务日志和存储管理等都在Berkeley DB函数库中实现。它们对应用程序来讲是完全透明的。

Berkeley DB不是关系型的数据库,不能应用标准的SQL语句对数据库操作,对它的操作要调用专用的API实现。这些API提供了查询、插入、删除等功能。比如com.sleepycat.db.Db类代表数据库对象。Db类的put( )方法完成的是插入功能;get( )方法完成的是读出数据的功能;com.sleepycat.db.DbcBerkeley DB的游标类,提供了遍历数据库记录的功能。

使用Berkeley DB提供的函数来进行数据库的访问和管理并不复杂。在大多数场合下,只需按照统一的接口标准进行调用就可以完成最基本的操作,Berkeley DBEnvironment为一组数据库同时提供参数设置。更为重要的是,如果要应用更高级的特性,必须要使用Environment功能,比如在要对保存的数据进行加密存储、利用其Transaction、数据加密、同步加锁控制、错误日志等功能的时候。

SQLiteSQL语言很大程度上实现了ANSI SQL92标准,特别是支持视图、触发器、事务,支持嵌套SQL。它通过SQL编译器(SQL Complier)来实现SQL语言对数据库进行操作,支持大部分的SQL命令,如attach databasebegin transactioncommentcommit transactioncopycreate indexcreate tablecreate triggercreate viewdeletedetach databasedrop indexdrop tabledrop triggerdrop viewend transactionexplainexpressioninsertOn conflict clausepragmareplacerollback transactionselectupdate

当然,也有一部分SQL命令SQLite并不支持。比如:不支持:Exists,虽然支持in(inExists的一种情况);不支持多数据库,如create table dbl.tablel as select*from db2table 1;:不支持存储过程;不支持Alter ViewTriggerTable:不支持Truncate SQLiteDelete不带Where字句时和Truancate的效果是一样的;不支持FloorCeiling函数,还有其它许多的函数;没有Auto Increment(自增)字段,但其实SQLite是支持Auto Increment的,即在将该字段设置为“INTEGER PRIMARY KEY”的时候;不支持If Exists等。

22 Berkeley DBS0Lite与普通数据库的差别

Berkeley DB引入了一些新的基本概念,使得数据库应用程序访问和管理数据库变得相对简单起来。

(1)关键字和数据

关键字(key)和数据(data)是。BerkeleyDB用来进行数据库管理的基础,由这两者构成的keydata对,组成了数据库中的一个基本结构单元。整个数据库实际上就是由许多这样的结构单元构成的。通过使用这种方式,在通过API函数访问数据库时,只需提供关键字就能够访问到相应的数据。

关键字和数据在Berkeley DB中都是用一个名为DBT的简单结构来表示的。实际上两者都可以是任意长度的二进制数据。DBT的作用主要是保存相应的内存地址及其长度,其结构如下所示:

typedef struct {

void*data

u_int32_t size

u_int32_t ulen

u_int32_f dlen

u_int32_f doff

u_int32_f flags

}DBT

在使用Berkeley DB进行数据管理时,缺省情况下是一个关键字对应于一个数据;但也可以将数据库配置成一个关键字对应于多个数据,而键值和数据必须是类com.sleepycat.db.Dbt的对象或其子类的对象。

(2)对象句柄

Berkeley DB函数库定义的大多数函数都遵循同样的调用原则:首先创建某个结构,然后再调用该结构中的某些方法。从程序设计的角度来讲,这一点同面向对象的设计原则是非常类似的,即先创建某个对象的一个实例,然后再调用该实例的某些方法。正因为如此,.Berkeley DB引入了对象句柄的概念来表示实例化后的结构,并且将结构中的成员函数称为该句柄的方法。对象句柄的引入使得程序员能够凭借面向对象的思想,来完成对Berkeley DB数据库的访问和操作。

(3)错误处理

对于任何一个函数库来说,如何对错误进行统一的处理都是需要考虑的问题。Berkeley DB提供的所有函数都遵循同样的错误处理原则,即函数成功执行后返回零,否则返回非零值。

对于系统错误(如磁盘空间不足),返回的是一个标准的值;而对于非系统错误,返回的则是一个特定的错误编码。例如,如果在数据库中没有与某个特定关键字所对应的数据,那么在通过该关键字检索数据时就会出现错误。此时函数的返回值将是DBNOTF0uND,表示在数据库中并没有所请求的关键字。所有标准的e rrn0值都大于零,而由Berkeley DB定义的特殊错误编码则都小于零。

BerkeleyDB提供了相应的函数来获得错误代号所对应的错误描述。一旦有错误发生,只需首先调用db_strerror( )函数来获得错误描述信息,然后再调用DB>err()DB->errx()就可以很轻松地输出格式化后的错误信息。

SQLite最大的特点在于其数据类型为无数据类型(typelessness)。这意味着可以保存任何类型的数据到所想要保存的任何表的任何列中,无论这列声明的数据类型是什么。虽然在生成表结构的时候,要声明每个域的数据类型,但sQLite并不做任何检查。开发人员要靠自己的程序控制输入与读出数据的类型。这里有一个例外,就是当主键为整型值时,如果要插入一个非整型值时会产生异常。

诚然,SQLite允许忽略数据类型,但是,仍然建议在Create Table语句中指定数据类型,因为数据类型有利于增强程序的可读性。SQLite支持常见的数据类型,如VARCHARNVARCHARTEXTINTEGERFLOATBOOLEANCLOBBLOBTIMESTAMPNUMERICVARYINGCHARACTERNATl0NAI, VARYINGCHARACTER

另外,虽然在插入或读出数据的时候是不区分类型的,但在比较的时候,不同数据类型是有区别的。比如:CREATE TABLE MyTable (a INTEGERb TEXT)

INSERT INT0 MyTable VALIUES(00)

当执行查询

SELECT count(*)FROM MyTable WHERE a==’00’;时,会返回一条记录。因为字段a的类型是整型,而数字000是相等的。而执行查询

SELECT count(*)FROM MyTable WHERE b==‘00’:时,则不会返回记录。因为字段b是字符类型,字符“00”与“0”是不相等的。

2.3 Betkeley DBSQLite数据存储方式比较

Berkeley DB对任何存入的数据都是按原样直接存储到数据文件中去,无论其是二进制数据还是A S C I IUnicode等编码的文本。Berkeley DB提供了四种存储数据的模式:BtreeHashQueueRecno。在打开数据库的时候,要指定一种存储模式。比如,上例中open( )方法中的参数DbDB_BTREE就是指定以Btree模式打开数据库。

SQLite只提供了Btree存储数据的模式。对二进制数据,SQLite不能直接保存,但可以先将二进制的数据转换成ASCII编码,然后再保存。Base64.编码机制是最常见的把二进制数据转换成ASCII编码的手段。在SQLiteC语言代码encode.c中,提供了Base64编码的功能。

Btree模式是以排序的二叉树的方式存储的,Hash是以线性哈希表的方式存储。Queue用逻辑记录号作为键值,以定长的数据为记录值。Recno方式也以逻辑记录号作为键值,但可以保存定长或变长的记录值。这里提到的逻辑记录号有两种,即可变的和固定的。可变逻辑记录号会根据数据记录的增加与删除作相应的变化。Queue模式下,逻辑记录号只能是固定方式。Recno模式则可通过配置来选择是采用哪种类型的记录号作为键值。Btree模式也可以通过设置,将可变的逻辑记录号作为键值。

这几种存储模式各有优缺点,当键值不想用逻辑记录号时,BtreeHash是必须的选择。Btree方式比较适合连续的顺序读取。比如,当键值是时间值,如果经常有从某一时间点开始连续读取后继的记录的操作,Btree是一种很好的选择。对随机的跳跃式读取,Hash模式则更为恰当。QueueRecno都以记录号为键值,但前者适合先进先出的读取方式。Recno则通常是存取变长文本记录的理想存储模式。

24 Berkeley DBS0Lite适用的系统

Berkeley DB为许多编程语言提供了实用的API接口,包括CC++Java、:PerlTclPythonPHP等。它适用平台UNIXPOSIX systemswin32以及嵌入式操作系统WinCEVxWorks等。

通过WrapperSQLite实现了与其它语言的连接。所谓Wrapper即对SQLite提供的接口进行封装,使其它语言可以访问,使用SQLiteSQLite本身提供CTcl的接口,世界各地的程序员还提供了各种语言的SQLite的接口封装, PythonC++Java.Net等几乎所有流行的语言基本都有。sQLite提供一个抽象的操作系统接口层,来保证其在POSIX Win32系统之间的兼容性。

2.5 其它方面

Berkeley DB没有数据库服务器的概念,使用简单的函数调用接口来完成所有的数据库操作,不使用SQL语言;接口简明实用,避免了对结构化查询语言进行解析和处理所需的开销,提高了执行速度;速度极快,可靠性高,但学习起来有一定难度。SQLite则简单易用,速度也很快,但功能却较Berkeley略有逊色,比如加密功能、二进制数据的处理等。

Berkeley DB虽然是开源的产品,但对某些条件下的商业性应用,却不是免费的,而且价格颇为昂贵。这些商业条件包括排除了开源的情况,不发放分布版本的情况等。SQLite是源代码完全的开放,可以免费用于任何用途,包括商业目的。

结语

随着人们对移动数据处理和管理需求的不断提高,与各种智能设备紧密结合的嵌入式数据库技术已经得到了学术界、工业界、民用部门等各方面的广泛重视。嵌入式数据库将会使得人们希望随时随地存取任意思数据信息的愿望成为现实,嵌入式数据库将无处不在。开源的嵌入式数据库Berkeley DBSQLite,内核微小,有能够充分适应硬件的能力,能很好地适应嵌入式系统的需要。在具体的嵌入式应用中可以根据具体情况选择应用。

SQLite的体系结构简介

简介

Block Diagram Of SQLite

这篇文档主要描述了SQLite类库的结构。这篇文档的内容对于那些想了解和修改SQLite内部结构的人将会非常有用。

右侧是一个结构图,它显示了SQLite的主要成分及各成分之间是如何相互关联的。接下来的文本将简要的介绍每个单一的成分。

这篇文档描述SQLite第三版,它和 2.8版以及早期的版本基本相似,但在一些细节上是有区别的。

接口程序

SQLite类库大部分的公共接口程序是由main.c, legacy.c, 和 vdbeapi.c源文件中的功能执行的。 但有些程序是分散在其他文件夹的,因为在其他文件夹里他们可以访问有文件作用域的数据结构。 sqlite3_get_table() 这个程序是在table.c中执行的。 sqlite3_mprintf()printf.c中执行。 sqlite3_complete()tokenize.c中执行。 Tcl 接口程序用tclsqlite.c来执行。

为了避免和其它软件在名字上有冲突,SQLite类库中所有的外部符号都是以sqlite3为前缀来命名的。这些被用来做外部使用的符号(换句话说,这些符号用来形成SQLite的API)是以sqlite3_.

来命名的。

Tokenizer

当执行一个包含SQL语句的字符串时,接口程序要把这个字符串传递给tokenizer。Tokenizer的任务是把原有字符串分成一个个标示符,并把这些标示符传递给剖析器。Tokenizer是在C文件夹tokenize.c中用手编译的。

在这个设计中需要注意的一点是,tokenizer调用parser。熟悉YACC和BISON的人们也许会习惯于用parser调用 tokenizer。 The author of SQLite的作者已经尝试了这两种方法,并发现用tokenizer调用parser会使程序运行的更顺利。YACC使程序更滞后一些。

Parser

The parser是一个部分,它基于文件场景赋予tokens意思。SQLite的parser是由 Lemon LALR(1) parser generator产生的。Lemon和YACC/BISON一样做同样的工作,但是它使用不同的输入语句,这个输入语句是不易出错的。 Lemon也产生一个parser,这个parser是可重入的并且是线程安全的。 Lemon 定义了无终端解除程序的概念,所以当遇到语法错误的时候,它不会泄露内存。驱动Lemon的原文件在parse.y.

因为lemon是一个在发展机械上不常见的程序,所以lemon的源代码(只是一个C文件)是在SQLite分布区的"tool"子目录下的。 lemon的文档是在分布区的 "doc"子目录下的。

代码发生器

在剖析器收集完符号并把之转换成完全的SQL语句时,它调用代码产生器来产生虚拟的机器代码,这些机器代码将按照SQL语句的要求来工作。在代码产生器中有许多文件; attach.c, auth.c, build.c, delete.c, expr.c, insert.c, pragma.c, select.c, trigger.c, update.c, vacuum.c and where.c. 正是在这些文件中,最具有重要意义的事情发生了。 expr.c 处理表达式代码的生成。 where.c 处理SELECT, UPDATE and DELETE语句中WHERE子句的代码的生成。文件 attach.c, delete.c, insert.c, select.c, trigger.c update.c,和vacuum.c处理SQL语句中具有同样名字的语句的代码的生成。(每个文件调用expr.c and where.c中的程序) All other 所有SQL的其它语句的代码是由build.c生成的。文件auth.c 执行sqlite3_set_authorizer()的功能.

虚拟机器

由代码生成器产生的程序由虚拟机器来运行。总而言之,虚拟机器主要用来执行一个为操作数据库而设计的抽象的计算引擎。机器有一个用来存储中间数据的存储栈。每个指令包含一个操作代码和三个额外的操作数。

虚拟机器本身是被包含在一个单独的文件vdbe.c中的。虚拟机器也有它自己的标题文件:vdbe.h它在虚拟机器和剩下的SQLite类库之间定义了一个接口程序,vdbeInt.h 它定义了虚拟机器的结构。文件vdbeaux.c包含了虚拟机器所使用的实用程序和一些被其它类库用来建立VM程序的接口程序模块。文件vdbeapi.c 包含虚拟机器的外部接口,比如 sqlite3_bind_... 类的函数。单独的值(字符串,整数,浮动点数值,BLOBS)被存储在一个叫 "Mem"的内部目标程序里,"Mem"是由vdbemem.c执行的。

SQLite使用C语言程序来来执行SQL函数。即使内置的SQL函数也是用这种方法来执行的。大部分的SQL内置函数(ex: coalesce(), count(),substr(), and so forth)可以在func.c里发现。日期和时间转换函数在date.c.

B-树

SQLite数据库在磁盘里维护,使用源文件btree.c中的B-树执行。数据库中的每个表格和目录使用一个单独的B-tree。所有的 B-trees 被存储在同样的磁盘文件里。文件格式的细节被记录在btree.c.

开头的备注里。

B-tree子系统的接口程序被标题文件btree.h所定义。.

页面高速缓存

B-tree模块要求信息来源于磁盘上固定规模的程序块。默认程序块的大小是1024个字节,但是可以在512和65536个字节间变化。 页面高速缓存负责读,写和高速缓存这些程序块。页面高速缓存还提供重新运算和提交抽象命令,它还管理关闭数据库文件夹。 B-tree驱动器要求页面高速缓存器中的特别的页,当它想修改页或重新运行改变的时候,它会通报页面高速缓存。为了保证所有的需求被快速,安全和有效的 处理,页面高速缓存处理所有的微小的细节。

运行页面高速缓存的代码在专门的C源文件pager.c中。页面高速缓存的子系统的接口程序被目标文件pager.h所定义。

OS 接口程序

为了在POSIX和Win32 之间提供一些可移植性,SQLite操作系统的接口程序使用一个提取层。 OS提取层的接口程序被定义在os.h. 每个支持的操作系统有它自己的执行文件: Unix使用os_unix.c,windows使用os_win.c。每个具体的操作器具有它自己的标题文件: os_unix.h, os_win.h, etc.

Utilities

内存分配和字符串比较程序位于util.c。剖析器使用的表格符号被hash.c中的无用信息表格维护。源文件utf.c包含UNICODE转换子程序。SQLite有它自己的执行文件printf() (有一些扩展)在printf.c中,还有它自己随机数量产生器在random.c.

测试代码

如果你计算回归测试脚本,多于一半的SQLite代码数据库的代码将被测试。 在主要代码文件中有许多assert()语句。另外,源文件test1.c通过test5.cmd5.c 执行只为测试用的扩展名。os_test.c向后的接口程序用来模拟断电,来验证页面调度程序中的系统性事故恢复机制。

SQLite函数使用教程

SQLite官方的使用介绍中附带了一个C语言的实例,该实例使用了回调的方法处理由SQL语句返回的记录集,这种回调函数的方法比较复杂,而且也并没有获得什么实质性的好处。所以在这里我推荐使用另外一组函数来完成该类操作,经实践检验证明,该方法在效率方面不存在任何问题。具体代码如下:

sqlite3 *db;

char *zErrMsg = 0;

int nrow = 0, ncolumn = 0; //查询语句返回的结果集的行数,列数

char temp[256], FileRoot[256];

char ** azResult; //用于保存查询结果集数据

sprintf(temp, _T("%s"), _T("\\test.db"));

/*

SQLite3UTF-8的编码方式处理字符串,所以在使用sqlite3_open打开数据库之前需要将数据库路径进行编码转换。

*/

CCodingConv::GB2312_2_UTF8(FileRoot, 256, temp, 0);

sqlite3_open(FileRoot, &db);

if(db == NULL)

{

return -1;

}

// sqlite3_get_table函数可以返回SQL语句执行之后的记录集,以及行列数

sqlite3_get_table(db, "SELECT name, age FROM list;", &azResult, &nrow, &ncolumn, &zErrMsg);

//nrow表明一共有多少项符合该次查询条件的记录

for(int k = 1; k <= nrow; k++)

{

printf(azResult[k*ncolumn]); //对应name列的数据

printf(azResult[k*ncolumn+1]); //对应age列的数据

}

//释放为记录集分配的内存,如果返回了空记录集并不会发生错误

sqlite3_free_table(azResult);

//关闭数据库

sqlite3_close(db);

return 0;

注释:原代码中的CCodingConv类可以进行UTF-8GB2312之间的转换。

数据库的表为

CREATE TABLE list (name varchar(40) UNIQUE, age integer);

存入数据:

sqlite3_exec(db, "insert into list values ('tamsyn','24');", 0, 0, &zErrMsg);

sqlite3_exec(db, "insert into list values ('mistletoe','23');", 0, 0, &zErrMsg);

使用这种方法在C语言中可以更方便的使用SQLite3,避免了回调函数的复杂性。并且可以完成任何SQL语言的查询操作。接下来我们的文章会结合实际操作,总结一下如何更高效的使用SQLite数据库!一点愚见还望大家不吝赐教!

下载本文所附的源代码