本文发表在 rolia.net 枫下论坛作者: 飞鹰
作 者: Matthias Warkus
原文作者: Matthias Warkus
翻译:yanzi clyan@sohu.com
本文简介
当第一次有人建议我为LinuxPlanet写数据库管理系统的比较时,我摇了摇头,我从来没做过这样的比较,但是后来,我想:有何不可?也许一种新鲜天真的方法并不坏。因此就有本文:一种全新眼光看缤纷的Linux数据库世界。本文共4部分。
在这里比较的数据库管理器是免费的或有便宜的许可费,并且他们可在所有层次的Linux系统上运行,从低端硬件到高端多处理器服务器,他们已经广泛地应用世界范围的生产系统上。为了比较,我把我自己放在了运行典型的Linux机器的境地,每天都由 DBMS提供动力。
我将考察:
mSQL
MySQL
PostgreSQL
我将以一个这3个数据库系统的安装和配置的讨论开始,然后我将在第2部分继续,通过用SQL的一个小数据库的设计,指出我碰到的困难和试图使数据库系统接受的差异,我也将解释一个程序的编码,它在数据库上执行各种的真实操作,例如增加数据并产生报表,同时讨论数据库在处理上的各种差别。这个客户程序针对所有3个数据库系统用C语言实现。为此,我必须不仅学习所有系统SQL API,还有SQL。如果没有其他东西,这将肯定饶有兴趣地看下去。
第三部分将包含一些真实的基准测试,作为奖赏。我已经有了在Linux 新闻组上款观看关于数据库的基准测试结果没完没了的争论的保持怀疑的乐趣,因此我将只测试客户程序做随机事情的时间性能,既在理想的条件下运行跑又通过多个并发进程的时候。那时我将已经涉及DBMS之间的大多数差别,因此你应该有了关于他们的强项和弱项的一个良好概念。
最后的部分将是你所期待的一切:结论、覆盖的特征、实现、在DBMS之间的API和性能差别。
但是现在,准备深入数据库管理的王国的探险吧。
获得并安装 PostgreSQL
位于PostgreSQL 网站的软件小节,有一个FTP和HTTP镜像网站的列表,可从他们下载分发,也有定购DBMS CD的选择。构造并安装它是一个多步骤过程。
首先,如果你想要建立Postgresql的Perl语言绑定,在你系统上的Perl 5 可执行文件必须已知是perl5的名字。通常,这意味着你需要做一个符号连接。
也许要做的最重要的事情是创造一个postgres用户。在我的系统上,这个命令可运行:
useradd - c “ PostgreSQL Administrator” - d /opt/postgresql - g users postgres
在其他系统上,命令可能有所不同。注意我假定postgres是在users组里。你可能比较喜欢给数据库主管一个它自己的组。
如果不另外说明,所有其它安装步骤需要以用户postgres身份来做。
解开PostgreSQL下载包并进入源代码目录树:
tar - xvzf postgresql-6.5.2.tar.gz
cd postgresql-6.5.2
用root用户,创建PostgreSQL目录并把交给postgres用户:
mkdir /opt/pgsql
chown postgres.users /opt/pgsql
进入源代码目录并配置软件包。我用下列选择:
./configure -- prefix=/opt/pgsql -- with-perl
进行构造和安装过程:
cd .. /doc
make install
cd .. /src
make all
make install
现在是执行一些附加的系统配置的时间了。
作为root,将下行
/opt/pgsql/lib
加入/etc/ld.so.conf并运行ldconfig。
安装Perl绑定,当用root登录后:
cd interface/perl5
make install
设置postgres账号。将下列行增加到.profile文件中:
PATH=$PATH:/opt/pgsql/bin
MANPATH=$MANPATH:/opt/pgsql/man
PGLIB=/opt/pgsql/lib
PGDATA=/opt/pgsql/data
export PATH MANPATH PGLIB PGDATA
LC_COLLATE=C
LC_CTYPE=C
export LC_COLLATE LC_CTYPE
最后3行对init过程和也许某些测试是必要的试。对实际的用户,他们当然应该设置到适当的现场,但是每个使用 PostgreSQL的人至少需要有PGLIB和PGDATA的设置。
Source .profile文件:
source .profile
运行后期安装:
initdb
获得并安装 MySQL
MySQL的最新版本可从MySQL下载页。有很多可选择的镜像站点。你需要执行下列步骤:
解开包并进入源代码目录树:
tar - xvzf mysql-3.22.26a.tar.gz
cd mysql-3.22.26a
配置软件包;我使用下列选项目:
./configure -- prefix=/opt/mysql
构造它:
make
安装它;用root用户:
make install
然后你需要配置安装:
作为root用户,创建授权数据库表:
scripts/mysql_install_db
作为root,启动服务器:
/opt/mysql/bin/safe_mysqld &
作为root,创建mysql用户:
useradd - c “ MySQL Administrator” - d /opt/mysql - g users mysql
作为root,把安装树属权赋给mysql用户:
chown - R mysql.users /opt/mysql
获得并安装 mSQL
下载mSQL不比下载PostgreSQL或MySQL那样简单, 但是,其分发更小。从Hughes技术网页开始并且寻找更进一步的指令。为了下载mSQL,首先需要在该站点注册并且通过一个基于CGI的下载系统,如有必要允许你在一些镜像站点间选择。构造和安装的mSQL的步骤如下:
解开包并进入源代码目录:
tar - xvzf msql-2.0.11.tar.gz
cd msql-2.0.11
产生目标定义:
make target
进入你平台的目标目录:
cd targets/Linux-2.2.12-i586
配置软件包:
./setup
编辑site.mm;我将安装路径改成/opt/msql。
构造它:
make all
作为root 用户安装它:
make install
在构造和安装的mSQL以后,你需要配置它:
作为root,创建msql用户:
useradd - c “mSQL Administrator” - d /opt/msql - g users msql
作为root,把目录属权交给msql用户:
chown - R msql.users /opt/msql
作 者: Matthias Warkus
数据库设计
编者按:这是涉及可自由地获得的Linux DBMS的系列文章的第二部分。
为了测试并比较3个数据库管理系统,我当然需要一个数据库来管理他们。在读完了《SQL傻瓜书》后,我有了一些如何设计现实的数据库的基本知识,因此我拣选了一特别无聊的真实世界情形(一个因特网书店,其他?)并且写下了一个小型数据库设计以便探讨一点Sql的能力。
在这个数据库里有5个表,BOOK保存可得到的书的登记信息;CUSTOMER包含登记的顾客。对每份订单,BOOKORDER创建一行,参照其顾客。对每个定购的项目,在ORDER_POSITION里引用它的订单号。作为一种奖励,针对书的排名我增加了一RATING表。
下列的SQL代码可装入到一个数据库系统的SQL监控程序并且被接受应该没有任何问题。所有的表在创建前被删除因此确保他们不会在创建之前已经存在。
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13) UNIQUE,
WHOLESALE_PRICE NUMERIC(4,2),
RETAIL_PRICE NUMERIC(4,2),
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(
CHECK (STATUS IN (′ACCEPTED′,
′DELAYED′,
′SHIPPED′,
′RETURNED′,
′PAID′)),
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE NUMERIC(1,0),
COMMENT CHARACTER VARYING(300),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
当然,这是一个极其简单的数据库。它看上去真实,但是它不适用于真实世界的应。它不保存顾客记录或任何东西,并且它甚至没有书的出版商的列。它只是一个测试环境。
注意我不想花大气力强制CUSTOMER.ISO_COUNTRY_CODE为今天是实际有效的编码。我在代码做了一点限制以测试数据库系统是否接受他们;我没尝试使数据库无懈可击。
改变设计适应PostgreSQL
当我将遵循ANSI标准的CREATE TABLE语句装入Postgresql的psql监控视程序是,我遇到的困难是很少的。我得到一些警告:外部关键字限制被接受但还没有实现,而且我不得不裁减RATING的COMMENT字段到255个字符,因为这是PostgreSQL的CHARACTER VARYING类型的字段的最大字段宽度。系统为存储大量数据提供BLOB数据类型,但是它们不在标准版本内,因此我决定了不使用他们。另外的问题是相当愚蠢--因为我不能找到有关PostgreSQL如何强制NUMERIC到C数据类型,也因为我不想使用float以避免舍入,我决定使得货币字段为分值(cent)的整数数字。
我最后得到了这个略有不同的脚本:
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13) UNIQUE,
WHOLESALE_PRICE INTEGER,
RETAIL_PRICE INTEGER,
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(
CHECK (STATUS IN (′ACCEPTED′,
′DELAYED′,
′SHIPPED′,
′RETURNED′,
′PAID′)),
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE SMALLINT,
COMMENT CHARACTER VARYING(255),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
使设计适应MySQL
MySQL象PostgreSQL一样忽略外部关键字的限制,但是它搞了个UNIQUE限制。最后的脚本与PostgreSQL脚本差不多:
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13),
WHOLESALE_PRICE INTEGER,
RETAIL_PRICE INTEGER,
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(,
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE NUMERIC(1,0),
COMMENT CHARACTER VARYING(255),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
使设计适应 mSQL
因为mSQL是一个精简的数据库管理器(的确,有些人可能怀疑MySQL和mSQL是否是数据库管理系统),它放弃了大多数Sql的功能而仅仅接受SQL的一个严格限制的子集。这样,mSQL的脚本看上有很大不同:
DROP TABLE BOOK
CREATE TABLE BOOK (
ARTICLE_NO INTEGER NOT NULL,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13),
WHOLESALE_PRICE MONEY,
RETAIL_PRICE MONEY,
COPIES_AVAILABLE INTEGER
)
DROP TABLE CUSTOMER
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER NOT NULL,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
)
DROP TABLE BOOKORDER
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER NOT NULL,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(1)
)
DROP TABLE ORDER_POSITION
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER NOT NULL,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT
)
DROP TABLE RATING
CREATE TABLE RATING (
RATING_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
SCORE SMALLINT,
COMMENT TEXT(255)
)
几乎所有的约束都不见了,并且NUMERIC和CHARACTER VARYING分别由MONEY和TEXT代替。
在mSQL的监视程序中有令人沮丧的不足:它似乎不能接受从标准输入输入SQL脚本,这样, 需要剪切/粘贴代码。mSQL也讨厌分号;最终我只能一个一个地输入命令并用\g(“go”斜杠命令)终止每条命令 。
实现测试客户
为了比较3个数据库管理器,我决定为执行在bookstore数据库上的交易的目的用C写了一个测试客户。结果,我实现了一些操作,它们能比较API。为了性能比较,我随后充分实现了它们,并且把一个非交互式模式加入客户程序,因此它能自己运行,产生随意的数据且随机执行交易。
我决定了在样品数据库上实现下列行动:
增加一本新书: INSERT INTO BOOK (...) VALUES (...);
删除一本存在的书: DELETE FROM BOOK WHERE ARTICLE_NO=...;
增加一个顾客: INSERT INTO CUSTOMER (...) VALUES (...);
删除一个顾客: DELETE FROM CUSTOMER WHERE CUSTOMER_NO=...;
订书的一个顾客: INSERT INTO BOOKORDER (...) VALUES (...); INSERT INTO ORDER_POSITION (...) VALUES (...);;
评估一本书的一个顾客: INSERT INTO RATING (...) VALUES (...);
改变一份订单的状态: UPDATE BOOKORDER SET STATUS=... WHERE ORDER_NO=...;
然后,能生成下列报表:
书籍列表: SELECT * FROM BOOK;
顾客列表: SELECT * FROM CUSTOMER;
正在投递的交货表,按状态排序: SELECT * FROM BOOKORDER ORDER BY STATUS;
书籍的利润额,最后有平均值: SELECT RETAIL_PRICE-WHOLESALE_PRICE FROM BOOK; SELECT AVG(RETAIL_PRICE-WHOLESALE_PRICE) FROM BOOK;
书评、评级和为一本书的平均评级: SELECT * FROM RATING WHERE ARTICLE_NO=...; SELECT AVG(SCORE) FROM RATING WHERE ARTICLE_NO=...;
将客户带入PostgreSQL的生活
关于用C编程PostgreSQL的好处是你能使用嵌入式SQL。(而且,至少我喜欢它)它不是很好地文档化,但是ESQL预处理器ecpg运行并能产生PostgreSQL接口代码就好。Sql 的定式思维方法有时妨碍了我;但是,开发客户程序并不是很难的。
我说过“不是很好地文档化”吗?那是一个保守说法。否则PostgreSQL完整的HTML 文档在这方面非常缺乏。我从书本得到的ESQL知识是初级的,而且联机文档没帮助太多,因此我不得不自己了解如何由ecpg将C的变量强制转换为NUMERIC值--还有其他东西,而且,ESQL预处理器不是很详细,且无论何时它碰到任何小错误,总是似乎完全释放出来,这对任何从事又长期准备的项目的人来说将是一个持久战。
在编程PostgreSQL的客户程序时,我碰到了一些小错误。例如,如果文档记录是可能的话,在声明一个光标(cursor)时,ecpg将不接受一个 FOR READ ONLY子句 。ORDER BY子句甚至没被实现。我遇见的问题大都ecpg预处理器有关。Postgres有一个 C API(不管怎么说,ESQL需要被编译进一些东西),它可能是优秀的,但是我没使用它(这就是生活)。当有ESQL时,我准备使用ESQL。
这是摘自postgres-client.pgc的list_books()函数:
void list_books(void)
{
EXEC SQL BEGIN DECLARE SECTION;
int article_no;
char author_first_names[30];
char author_last_names[30];
char title[30];
char isbn[14];
int wholesale_price;
int retail_price;
int copies_available;
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE book_cursor CURSOR FOR
SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES, AUTHOR_LAST_NAMES,
TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,
COPIES_AVAILABLE FROM BOOK;
EXEC SQL OPEN book_cursor;
while (1)
{
EXEC SQL FETCH NEXT FROM book_cursor
INTO :article_no, :author_first_names, :author_last_names,
:title, :isbn, :wholesale_price, :retail_price,
:copies_available;
if (sqlca.sqlcode == 100) /* 100 == NOT FOUND */
break; /* bail out */
printf("\nArticle no. %d\n", article_no);
printf("%s, %s:\n", author_last_names, author_first_names);
printf(" %s (%s)\n", title, isbn);
printf("Bought at %d; selling at %d; %d copies available\n\n",
wholesale_price, retail_price, copies_available);
};
EXEC SQL CLOSE book_cursor;
}
代码是相当直观。它声明一些宿主变量,在一个BEGIN/END DECLARE SECTION构造中包装声明,打开一个SELECT光标查询,并且然后一行一行地取到宿主变量中,光标然后关闭。
我使用了更旧的, 遭到一致反对的sqlcode变量而不是更现代的sqlstate,因为这种方式更容易检查一个NOT FOUND情形。
把客户带入MySQL的生活
Mysql的C API是相当易用的。核心元素是包含有关数据库连接的信息和其状态的结构,它通过连接MySQL服务器进行初始化。该结构的一根指针必须被传递给所有的 MySQL 客户函数。
查询以字符串提交;这意味着一个人必须处理 C 字符串变换功能,包含空字节(\0) 的数据应该能使用,情况变得更复杂了,因为随后传递了一个计数字符串而不是一个 C字符串。
为了获取查询结果,一个指向MYSQL_RES结构的指针和一个数值变量用适当的 API 函数初始化,然后将一行取进一个MYSQL_ROW变量,它是一个字符串数组,直接将结果放进整数变量,就像Postgresql的ESQL的实现能做的那样,但这是不可能的,结果缓冲区随后被释放。只要你能理解,语义几乎与在ESQL使用光标相同。
list_books(void)
{
int count;
MYSQL_RES *result;
mysql_query(&bookstore, "SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES,AUTHOR_LAST_NAMES, TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,COPIES_AVAILABLE FROM BOOK");
result = mysql_store_result(&bookstore);
for(count = mysql_num_rows(result); count > 0; count--)
{
MYSQL_ROW record;
record = mysql_fetch_row(result);
printf("\nArticle no. %s\n", record[0]);
printf("%s, %s:\n", record[2], record[1]);
printf(" %s (%s)\n", record[3], record[4]);
printf("Bought at %s; selling at %s; %s copies available\n\n",
record[5], record[6], record[7]);
};
mysql_free_result(result);
}
mysql_free_result ( 结果 );
}
API函数简明扼要,但足够了, Texinfo格式的文档作为Mysql文档的主要来源。
把客户带入mSQL的
mSQL和Mysql C API 之间的差别非常非常小。这样, 甚至可能有一个自动变换器。主要的差别是:
mSQL 不存储连接数据块, 仅存一个数字(int bookstore)
一些 mSQL 函数不拿连接作为一个参数
mSQL 函数名字是Pascal风格(他们使用大写首字符而不是下划线)
方便的MONEY数据类型是一个有2个的十进制位的固定精度小数类型。为了使mSQL正确地在MONEY列中将分币(cent)存入整数数字里,我需要转换他们,强制到float,分离他们并且在add_new_book()函数中的sprintf语句格式化他们。
这是list_books(), 移植到 mSQL :
void
list_books(void)
{
int count;
m_result *result;
msqlQuery(bookstore, "SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES,AUTHOR_LAST_NAMES, TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,COPIES_AVAILABLE FROM BOOK");
result = msqlStoreResult();
for(count = msqlNumRows(result); count > 0; count--)
{
m_row record;
record = msqlFetchRow(result);
printf("\nArticle no. %s\n", record[0]);
printf("%s, %s:\n", record[2], record[1]);
printf(" %s (%s)\n", record[3], record[4]);
printf("Bought at %s; selling at %s; %s copies available\n\n",
record[5], record[6], record[7]);
};
msqlFreeResult(result);
}
mSQL的 C API文档可以在mSQL 手册里找到,它以PostScript和一个大的HTML文件与mSQL一起发行。
一些早期结论
所有这3个讨论的数据库系统是相当容易安装、设置和编程。实现C API的客户库是很小的;与现今的比如GUI工具箱,他们的大小是可以忽略的,并且在客户程序的二进制大小或存储器足迹(footprint)没有太大的差别。
Postgresql的ESQL API的不断增加的冗长和更长的准备时间通过少花些精力在转换字符串到非字符串后反过来进行弥补。
到目前为止, 我没有说过任何关于性能的事情。我将在这个系列的下一部分做深入研究。
作 者: Matthias Warkus
原文作者: Matthias Warkus
译者:yanzi clyan@sohu.com
评估大量因素
我们将要在中途改变方向。在这部分,我将涉及与性能无关的话题。在下一个并且是最后一部分,我将全面研究基准测试并以最终结论结束。
一般差别
不同于PostgreSQL,MySQL和mSQL不是真正的关系数据库管理系统。我看到有人在新闻组里把MySQL称为“只是一个快速存储工具”,并且mSQL甚至被称为了一个玩具--不敢恭维。至少mSQL实现了部分一个完整的SQL DBM应该提供的功能。
如果一个人需要一个真实的RDBMS,三者中唯一可行的选择是PostgreSQL。如果计算原始的性能表现,特别是如果对数据库所做的存取并不复杂并且大多数是自动的,一个更小的系统可能更好一些。因此,mSQL和MySQL被宣传为网数据库系统。
许可证
PostgreSQL以一个BSD风格许可证被分发,在所有相关的方面均是自由的(也许对一些狂热者来说太自由了),如果版权声明被保留,基本上一个人可以用该软件做任何事情。
MySQL是免费的并且在某些条件下源代码允许被修改,但是禁止为了商业目的的再分发。
mSQL对非商业性组织的使用是免费的;但在一个14天评估时期以后,购买一个许可证是必要的。
因为这些差别,在使用他们之一的企业的人们需要仔细地考虑许可条件。
ANSI 标准的实现
这3个系统都是在叫喊是完全实现 ANSI SQL 标准的,公平地讲,这在我看来有点可怕。当MySQL实现了开发者已经定义好的一个子集时,mSQL甚至没有尝试真正遵循ANSI 标准。PostgreSQL 最后定位在与 ANSI 完全兼容,但是它仍然有一条长路要走。
PostgreSQL确实还没有支持参考完整性(RI),但是只测试了DBMS的事务(transaction)。另外,新的SQL特征像SQLSTATE变量也没有实现。
MySQL既不支持事务也不保证参考完整性;对事务存取数据库表能明确地为锁定和解锁。
mSQL缺乏 ANSI SQL 的大多数特征,它仅仅实现了一个最最少的API,没有事务和参考完整性。
APIs
所有3个系统测试的API大部分对处理是透明的,发生任何问题通常是由于不正确的文档,而不是API本身。
mSQL和MySQL都没有嵌入式SQL(ESQL)预处理器功能。随着ESQL的诞生,现在我相当喜欢它,但是使用mSQL 和 MySQL 本身提供的C API并不困难。有同样光标的含义,但以一个不同的方法实现,并且把字符串传递给C函数仅比在代码中使用码嵌入式 SQL语句稍难一点儿。
除了提到的ESQL API,PostgreSQL带有C API、C++绑定、JDBC、ODBC、Perl绑定、Python和Tcl绑定。
MySQL对Win32平台有附加的ODBC支持;语言联编 (接口)至少有C++、Eiffel、Java、Perl、Python、PHP和Tcl可以得到。
mSQL与Lite(一种类似C的脚本语言,与分发一起发行)紧密结合,可以得到一个称为 W3-mSQL的一个网站集成包,它是JDBC、ODBC、Perl和PHP API。
注意我没有测试那些任何附加的绑定和特征;他们的质量和文档的表述不是很好。能获得很多对这3个系统的第三方扩展;本文不再赘述。
文档和更多
PostgreSQL以DocBook SGML格式记录文档。手册分为管理员指南、程序员指南、用户指南和一本教程。另外,FAQ和各种各样的说明文件涉及一些话题。软件的好多领域缺乏足够的文档。
MySQL以GNU Texinfo格式记录文档;手册看起来完全。
mSQL有一个单个文件的手册(没有超文本),它有PostScript和HTML形式。作为能从一个商业软件产品的的角度所期望的,它覆盖了所有的特征。
认证和一般的安全
这时我还没谈及的一个话题,但是需要在这个比较中提及的是存取认证。ANSI SQL 对于存取控制提供很复杂且很精致的机制, 也就是GRANT和REVOKE语句。
PostgreSQL和MySQL能理解这些标准语句,但内部处理存取控制是不同的。可以得到mSQL的网站集成包中的一个认证增强程序(W3-mSQL),但是在它基本形态中,大多数数据库系统似乎没有任何内置的存取控制支持。不像safe_mysqld和postmaster,mSQL数据库守护程序假定有root运行的,它可以可能造成安全隐患。
任何大型数据库都需要一个安全概念,就像它需要一个彻底的数据库设计一样。不可能说他们中支持认证系统(即 PostgreSQL或MySQL)的哪一个是更安全的;这里, 任何事情均取决于设计。
在本系列的第四部分,本文做作者集中精力研究DBMS的性能测量和工具的全面总结。更多精彩文章及讨论,请光临枫下论坛 rolia.net
作 者: Matthias Warkus
原文作者: Matthias Warkus
翻译:yanzi clyan@sohu.com
本文简介
当第一次有人建议我为LinuxPlanet写数据库管理系统的比较时,我摇了摇头,我从来没做过这样的比较,但是后来,我想:有何不可?也许一种新鲜天真的方法并不坏。因此就有本文:一种全新眼光看缤纷的Linux数据库世界。本文共4部分。
在这里比较的数据库管理器是免费的或有便宜的许可费,并且他们可在所有层次的Linux系统上运行,从低端硬件到高端多处理器服务器,他们已经广泛地应用世界范围的生产系统上。为了比较,我把我自己放在了运行典型的Linux机器的境地,每天都由 DBMS提供动力。
我将考察:
mSQL
MySQL
PostgreSQL
我将以一个这3个数据库系统的安装和配置的讨论开始,然后我将在第2部分继续,通过用SQL的一个小数据库的设计,指出我碰到的困难和试图使数据库系统接受的差异,我也将解释一个程序的编码,它在数据库上执行各种的真实操作,例如增加数据并产生报表,同时讨论数据库在处理上的各种差别。这个客户程序针对所有3个数据库系统用C语言实现。为此,我必须不仅学习所有系统SQL API,还有SQL。如果没有其他东西,这将肯定饶有兴趣地看下去。
第三部分将包含一些真实的基准测试,作为奖赏。我已经有了在Linux 新闻组上款观看关于数据库的基准测试结果没完没了的争论的保持怀疑的乐趣,因此我将只测试客户程序做随机事情的时间性能,既在理想的条件下运行跑又通过多个并发进程的时候。那时我将已经涉及DBMS之间的大多数差别,因此你应该有了关于他们的强项和弱项的一个良好概念。
最后的部分将是你所期待的一切:结论、覆盖的特征、实现、在DBMS之间的API和性能差别。
但是现在,准备深入数据库管理的王国的探险吧。
获得并安装 PostgreSQL
位于PostgreSQL 网站的软件小节,有一个FTP和HTTP镜像网站的列表,可从他们下载分发,也有定购DBMS CD的选择。构造并安装它是一个多步骤过程。
首先,如果你想要建立Postgresql的Perl语言绑定,在你系统上的Perl 5 可执行文件必须已知是perl5的名字。通常,这意味着你需要做一个符号连接。
也许要做的最重要的事情是创造一个postgres用户。在我的系统上,这个命令可运行:
useradd - c “ PostgreSQL Administrator” - d /opt/postgresql - g users postgres
在其他系统上,命令可能有所不同。注意我假定postgres是在users组里。你可能比较喜欢给数据库主管一个它自己的组。
如果不另外说明,所有其它安装步骤需要以用户postgres身份来做。
解开PostgreSQL下载包并进入源代码目录树:
tar - xvzf postgresql-6.5.2.tar.gz
cd postgresql-6.5.2
用root用户,创建PostgreSQL目录并把交给postgres用户:
mkdir /opt/pgsql
chown postgres.users /opt/pgsql
进入源代码目录并配置软件包。我用下列选择:
./configure -- prefix=/opt/pgsql -- with-perl
进行构造和安装过程:
cd .. /doc
make install
cd .. /src
make all
make install
现在是执行一些附加的系统配置的时间了。
作为root,将下行
/opt/pgsql/lib
加入/etc/ld.so.conf并运行ldconfig。
安装Perl绑定,当用root登录后:
cd interface/perl5
make install
设置postgres账号。将下列行增加到.profile文件中:
PATH=$PATH:/opt/pgsql/bin
MANPATH=$MANPATH:/opt/pgsql/man
PGLIB=/opt/pgsql/lib
PGDATA=/opt/pgsql/data
export PATH MANPATH PGLIB PGDATA
LC_COLLATE=C
LC_CTYPE=C
export LC_COLLATE LC_CTYPE
最后3行对init过程和也许某些测试是必要的试。对实际的用户,他们当然应该设置到适当的现场,但是每个使用 PostgreSQL的人至少需要有PGLIB和PGDATA的设置。
Source .profile文件:
source .profile
运行后期安装:
initdb
获得并安装 MySQL
MySQL的最新版本可从MySQL下载页。有很多可选择的镜像站点。你需要执行下列步骤:
解开包并进入源代码目录树:
tar - xvzf mysql-3.22.26a.tar.gz
cd mysql-3.22.26a
配置软件包;我使用下列选项目:
./configure -- prefix=/opt/mysql
构造它:
make
安装它;用root用户:
make install
然后你需要配置安装:
作为root用户,创建授权数据库表:
scripts/mysql_install_db
作为root,启动服务器:
/opt/mysql/bin/safe_mysqld &
作为root,创建mysql用户:
useradd - c “ MySQL Administrator” - d /opt/mysql - g users mysql
作为root,把安装树属权赋给mysql用户:
chown - R mysql.users /opt/mysql
获得并安装 mSQL
下载mSQL不比下载PostgreSQL或MySQL那样简单, 但是,其分发更小。从Hughes技术网页开始并且寻找更进一步的指令。为了下载mSQL,首先需要在该站点注册并且通过一个基于CGI的下载系统,如有必要允许你在一些镜像站点间选择。构造和安装的mSQL的步骤如下:
解开包并进入源代码目录:
tar - xvzf msql-2.0.11.tar.gz
cd msql-2.0.11
产生目标定义:
make target
进入你平台的目标目录:
cd targets/Linux-2.2.12-i586
配置软件包:
./setup
编辑site.mm;我将安装路径改成/opt/msql。
构造它:
make all
作为root 用户安装它:
make install
在构造和安装的mSQL以后,你需要配置它:
作为root,创建msql用户:
useradd - c “mSQL Administrator” - d /opt/msql - g users msql
作为root,把目录属权交给msql用户:
chown - R msql.users /opt/msql
作 者: Matthias Warkus
数据库设计
编者按:这是涉及可自由地获得的Linux DBMS的系列文章的第二部分。
为了测试并比较3个数据库管理系统,我当然需要一个数据库来管理他们。在读完了《SQL傻瓜书》后,我有了一些如何设计现实的数据库的基本知识,因此我拣选了一特别无聊的真实世界情形(一个因特网书店,其他?)并且写下了一个小型数据库设计以便探讨一点Sql的能力。
在这个数据库里有5个表,BOOK保存可得到的书的登记信息;CUSTOMER包含登记的顾客。对每份订单,BOOKORDER创建一行,参照其顾客。对每个定购的项目,在ORDER_POSITION里引用它的订单号。作为一种奖励,针对书的排名我增加了一RATING表。
下列的SQL代码可装入到一个数据库系统的SQL监控程序并且被接受应该没有任何问题。所有的表在创建前被删除因此确保他们不会在创建之前已经存在。
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13) UNIQUE,
WHOLESALE_PRICE NUMERIC(4,2),
RETAIL_PRICE NUMERIC(4,2),
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(
CHECK (STATUS IN (′ACCEPTED′,
′DELAYED′,
′SHIPPED′,
′RETURNED′,
′PAID′)),
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE NUMERIC(1,0),
COMMENT CHARACTER VARYING(300),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
当然,这是一个极其简单的数据库。它看上去真实,但是它不适用于真实世界的应。它不保存顾客记录或任何东西,并且它甚至没有书的出版商的列。它只是一个测试环境。
注意我不想花大气力强制CUSTOMER.ISO_COUNTRY_CODE为今天是实际有效的编码。我在代码做了一点限制以测试数据库系统是否接受他们;我没尝试使数据库无懈可击。
改变设计适应PostgreSQL
当我将遵循ANSI标准的CREATE TABLE语句装入Postgresql的psql监控视程序是,我遇到的困难是很少的。我得到一些警告:外部关键字限制被接受但还没有实现,而且我不得不裁减RATING的COMMENT字段到255个字符,因为这是PostgreSQL的CHARACTER VARYING类型的字段的最大字段宽度。系统为存储大量数据提供BLOB数据类型,但是它们不在标准版本内,因此我决定了不使用他们。另外的问题是相当愚蠢--因为我不能找到有关PostgreSQL如何强制NUMERIC到C数据类型,也因为我不想使用float以避免舍入,我决定使得货币字段为分值(cent)的整数数字。
我最后得到了这个略有不同的脚本:
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13) UNIQUE,
WHOLESALE_PRICE INTEGER,
RETAIL_PRICE INTEGER,
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(
CHECK (STATUS IN (′ACCEPTED′,
′DELAYED′,
′SHIPPED′,
′RETURNED′,
′PAID′)),
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE SMALLINT,
COMMENT CHARACTER VARYING(255),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
使设计适应MySQL
MySQL象PostgreSQL一样忽略外部关键字的限制,但是它搞了个UNIQUE限制。最后的脚本与PostgreSQL脚本差不多:
DROP TABLE BOOK;
CREATE TABLE BOOK (
ARTICLE_NO INTEGER PRIMARY KEY,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13),
WHOLESALE_PRICE INTEGER,
RETAIL_PRICE INTEGER,
COPIES_AVAILABLE INTEGER
);
DROP TABLE CUSTOMER;
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER PRIMARY KEY,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
);
DROP TABLE BOOKORDER;
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER PRIMARY KEY,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(,
CONSTRAINT CUSTOMER_FK FOREIGN KEY (CUSTOMER_NO)
REFERENCES KUNDE (KUNDENNAME)
);
DROP TABLE ORDER_POSITION;
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER PRIMARY KEY,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT,
CONSTRAINT ORDER_FK FOREIGN KEY (ORDER_NO)
REFERENCES BOOKORDER (ORDER_NO),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
DROP TABLE RATING;
CREATE TABLE RATING (
RATING_NO INTEGER PRIMARY KEY,
ARTICLE_NO INTEGER NOT NULL,
SCORE NUMERIC(1,0),
COMMENT CHARACTER VARYING(255),
CONSTRAINT BOOK_FK FOREIGN KEY (ARTICLE_NO)
REFERENCES BOOK (ARTICLE_NO)
);
使设计适应 mSQL
因为mSQL是一个精简的数据库管理器(的确,有些人可能怀疑MySQL和mSQL是否是数据库管理系统),它放弃了大多数Sql的功能而仅仅接受SQL的一个严格限制的子集。这样,mSQL的脚本看上有很大不同:
DROP TABLE BOOK
CREATE TABLE BOOK (
ARTICLE_NO INTEGER NOT NULL,
AUTHOR_FIRST_NAMES CHARACTER(30),
AUTHOR_LAST_NAMES CHARACTER(30),
TITLE CHARACTER(30),
ISBN CHARACTER(13),
WHOLESALE_PRICE MONEY,
RETAIL_PRICE MONEY,
COPIES_AVAILABLE INTEGER
)
DROP TABLE CUSTOMER
CREATE TABLE CUSTOMER (
CUSTOMER_NO INTEGER NOT NULL,
FIRST_NAMES CHARACTER(30),
LAST_NAMES CHARACTER(30),
STREET CHARACTER(30),
HOUSE_NO SMALLINT,
POSTCODE CHARACTER(7),
TOWN CHARACTER(30),
ISO_COUNTRY_CODE CHARACTER(2)
)
DROP TABLE BOOKORDER
CREATE TABLE BOOKORDER (
ORDER_NO INTEGER NOT NULL,
CUSTOMER_NO INTEGER NOT NULL,
ORDERED DATE,
DELIVERY DATE,
STATUS CHARACTER(1)
)
DROP TABLE ORDER_POSITION
CREATE TABLE ORDER_POSITION (
POSITION_NO INTEGER NOT NULL,
ORDER_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
NUMBER SMALLINT
)
DROP TABLE RATING
CREATE TABLE RATING (
RATING_NO INTEGER NOT NULL,
ARTICLE_NO INTEGER NOT NULL,
SCORE SMALLINT,
COMMENT TEXT(255)
)
几乎所有的约束都不见了,并且NUMERIC和CHARACTER VARYING分别由MONEY和TEXT代替。
在mSQL的监视程序中有令人沮丧的不足:它似乎不能接受从标准输入输入SQL脚本,这样, 需要剪切/粘贴代码。mSQL也讨厌分号;最终我只能一个一个地输入命令并用\g(“go”斜杠命令)终止每条命令 。
实现测试客户
为了比较3个数据库管理器,我决定为执行在bookstore数据库上的交易的目的用C写了一个测试客户。结果,我实现了一些操作,它们能比较API。为了性能比较,我随后充分实现了它们,并且把一个非交互式模式加入客户程序,因此它能自己运行,产生随意的数据且随机执行交易。
我决定了在样品数据库上实现下列行动:
增加一本新书: INSERT INTO BOOK (...) VALUES (...);
删除一本存在的书: DELETE FROM BOOK WHERE ARTICLE_NO=...;
增加一个顾客: INSERT INTO CUSTOMER (...) VALUES (...);
删除一个顾客: DELETE FROM CUSTOMER WHERE CUSTOMER_NO=...;
订书的一个顾客: INSERT INTO BOOKORDER (...) VALUES (...); INSERT INTO ORDER_POSITION (...) VALUES (...);;
评估一本书的一个顾客: INSERT INTO RATING (...) VALUES (...);
改变一份订单的状态: UPDATE BOOKORDER SET STATUS=... WHERE ORDER_NO=...;
然后,能生成下列报表:
书籍列表: SELECT * FROM BOOK;
顾客列表: SELECT * FROM CUSTOMER;
正在投递的交货表,按状态排序: SELECT * FROM BOOKORDER ORDER BY STATUS;
书籍的利润额,最后有平均值: SELECT RETAIL_PRICE-WHOLESALE_PRICE FROM BOOK; SELECT AVG(RETAIL_PRICE-WHOLESALE_PRICE) FROM BOOK;
书评、评级和为一本书的平均评级: SELECT * FROM RATING WHERE ARTICLE_NO=...; SELECT AVG(SCORE) FROM RATING WHERE ARTICLE_NO=...;
将客户带入PostgreSQL的生活
关于用C编程PostgreSQL的好处是你能使用嵌入式SQL。(而且,至少我喜欢它)它不是很好地文档化,但是ESQL预处理器ecpg运行并能产生PostgreSQL接口代码就好。Sql 的定式思维方法有时妨碍了我;但是,开发客户程序并不是很难的。
我说过“不是很好地文档化”吗?那是一个保守说法。否则PostgreSQL完整的HTML 文档在这方面非常缺乏。我从书本得到的ESQL知识是初级的,而且联机文档没帮助太多,因此我不得不自己了解如何由ecpg将C的变量强制转换为NUMERIC值--还有其他东西,而且,ESQL预处理器不是很详细,且无论何时它碰到任何小错误,总是似乎完全释放出来,这对任何从事又长期准备的项目的人来说将是一个持久战。
在编程PostgreSQL的客户程序时,我碰到了一些小错误。例如,如果文档记录是可能的话,在声明一个光标(cursor)时,ecpg将不接受一个 FOR READ ONLY子句 。ORDER BY子句甚至没被实现。我遇见的问题大都ecpg预处理器有关。Postgres有一个 C API(不管怎么说,ESQL需要被编译进一些东西),它可能是优秀的,但是我没使用它(这就是生活)。当有ESQL时,我准备使用ESQL。
这是摘自postgres-client.pgc的list_books()函数:
void list_books(void)
{
EXEC SQL BEGIN DECLARE SECTION;
int article_no;
char author_first_names[30];
char author_last_names[30];
char title[30];
char isbn[14];
int wholesale_price;
int retail_price;
int copies_available;
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE book_cursor CURSOR FOR
SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES, AUTHOR_LAST_NAMES,
TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,
COPIES_AVAILABLE FROM BOOK;
EXEC SQL OPEN book_cursor;
while (1)
{
EXEC SQL FETCH NEXT FROM book_cursor
INTO :article_no, :author_first_names, :author_last_names,
:title, :isbn, :wholesale_price, :retail_price,
:copies_available;
if (sqlca.sqlcode == 100) /* 100 == NOT FOUND */
break; /* bail out */
printf("\nArticle no. %d\n", article_no);
printf("%s, %s:\n", author_last_names, author_first_names);
printf(" %s (%s)\n", title, isbn);
printf("Bought at %d; selling at %d; %d copies available\n\n",
wholesale_price, retail_price, copies_available);
};
EXEC SQL CLOSE book_cursor;
}
代码是相当直观。它声明一些宿主变量,在一个BEGIN/END DECLARE SECTION构造中包装声明,打开一个SELECT光标查询,并且然后一行一行地取到宿主变量中,光标然后关闭。
我使用了更旧的, 遭到一致反对的sqlcode变量而不是更现代的sqlstate,因为这种方式更容易检查一个NOT FOUND情形。
把客户带入MySQL的生活
Mysql的C API是相当易用的。核心元素是包含有关数据库连接的信息和其状态的结构,它通过连接MySQL服务器进行初始化。该结构的一根指针必须被传递给所有的 MySQL 客户函数。
查询以字符串提交;这意味着一个人必须处理 C 字符串变换功能,包含空字节(\0) 的数据应该能使用,情况变得更复杂了,因为随后传递了一个计数字符串而不是一个 C字符串。
为了获取查询结果,一个指向MYSQL_RES结构的指针和一个数值变量用适当的 API 函数初始化,然后将一行取进一个MYSQL_ROW变量,它是一个字符串数组,直接将结果放进整数变量,就像Postgresql的ESQL的实现能做的那样,但这是不可能的,结果缓冲区随后被释放。只要你能理解,语义几乎与在ESQL使用光标相同。
list_books(void)
{
int count;
MYSQL_RES *result;
mysql_query(&bookstore, "SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES,AUTHOR_LAST_NAMES, TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,COPIES_AVAILABLE FROM BOOK");
result = mysql_store_result(&bookstore);
for(count = mysql_num_rows(result); count > 0; count--)
{
MYSQL_ROW record;
record = mysql_fetch_row(result);
printf("\nArticle no. %s\n", record[0]);
printf("%s, %s:\n", record[2], record[1]);
printf(" %s (%s)\n", record[3], record[4]);
printf("Bought at %s; selling at %s; %s copies available\n\n",
record[5], record[6], record[7]);
};
mysql_free_result(result);
}
mysql_free_result ( 结果 );
}
API函数简明扼要,但足够了, Texinfo格式的文档作为Mysql文档的主要来源。
把客户带入mSQL的
mSQL和Mysql C API 之间的差别非常非常小。这样, 甚至可能有一个自动变换器。主要的差别是:
mSQL 不存储连接数据块, 仅存一个数字(int bookstore)
一些 mSQL 函数不拿连接作为一个参数
mSQL 函数名字是Pascal风格(他们使用大写首字符而不是下划线)
方便的MONEY数据类型是一个有2个的十进制位的固定精度小数类型。为了使mSQL正确地在MONEY列中将分币(cent)存入整数数字里,我需要转换他们,强制到float,分离他们并且在add_new_book()函数中的sprintf语句格式化他们。
这是list_books(), 移植到 mSQL :
void
list_books(void)
{
int count;
m_result *result;
msqlQuery(bookstore, "SELECT ARTICLE_NO, AUTHOR_FIRST_NAMES,AUTHOR_LAST_NAMES, TITLE, ISBN, WHOLESALE_PRICE, RETAIL_PRICE,COPIES_AVAILABLE FROM BOOK");
result = msqlStoreResult();
for(count = msqlNumRows(result); count > 0; count--)
{
m_row record;
record = msqlFetchRow(result);
printf("\nArticle no. %s\n", record[0]);
printf("%s, %s:\n", record[2], record[1]);
printf(" %s (%s)\n", record[3], record[4]);
printf("Bought at %s; selling at %s; %s copies available\n\n",
record[5], record[6], record[7]);
};
msqlFreeResult(result);
}
mSQL的 C API文档可以在mSQL 手册里找到,它以PostScript和一个大的HTML文件与mSQL一起发行。
一些早期结论
所有这3个讨论的数据库系统是相当容易安装、设置和编程。实现C API的客户库是很小的;与现今的比如GUI工具箱,他们的大小是可以忽略的,并且在客户程序的二进制大小或存储器足迹(footprint)没有太大的差别。
Postgresql的ESQL API的不断增加的冗长和更长的准备时间通过少花些精力在转换字符串到非字符串后反过来进行弥补。
到目前为止, 我没有说过任何关于性能的事情。我将在这个系列的下一部分做深入研究。
作 者: Matthias Warkus
原文作者: Matthias Warkus
译者:yanzi clyan@sohu.com
评估大量因素
我们将要在中途改变方向。在这部分,我将涉及与性能无关的话题。在下一个并且是最后一部分,我将全面研究基准测试并以最终结论结束。
一般差别
不同于PostgreSQL,MySQL和mSQL不是真正的关系数据库管理系统。我看到有人在新闻组里把MySQL称为“只是一个快速存储工具”,并且mSQL甚至被称为了一个玩具--不敢恭维。至少mSQL实现了部分一个完整的SQL DBM应该提供的功能。
如果一个人需要一个真实的RDBMS,三者中唯一可行的选择是PostgreSQL。如果计算原始的性能表现,特别是如果对数据库所做的存取并不复杂并且大多数是自动的,一个更小的系统可能更好一些。因此,mSQL和MySQL被宣传为网数据库系统。
许可证
PostgreSQL以一个BSD风格许可证被分发,在所有相关的方面均是自由的(也许对一些狂热者来说太自由了),如果版权声明被保留,基本上一个人可以用该软件做任何事情。
MySQL是免费的并且在某些条件下源代码允许被修改,但是禁止为了商业目的的再分发。
mSQL对非商业性组织的使用是免费的;但在一个14天评估时期以后,购买一个许可证是必要的。
因为这些差别,在使用他们之一的企业的人们需要仔细地考虑许可条件。
ANSI 标准的实现
这3个系统都是在叫喊是完全实现 ANSI SQL 标准的,公平地讲,这在我看来有点可怕。当MySQL实现了开发者已经定义好的一个子集时,mSQL甚至没有尝试真正遵循ANSI 标准。PostgreSQL 最后定位在与 ANSI 完全兼容,但是它仍然有一条长路要走。
PostgreSQL确实还没有支持参考完整性(RI),但是只测试了DBMS的事务(transaction)。另外,新的SQL特征像SQLSTATE变量也没有实现。
MySQL既不支持事务也不保证参考完整性;对事务存取数据库表能明确地为锁定和解锁。
mSQL缺乏 ANSI SQL 的大多数特征,它仅仅实现了一个最最少的API,没有事务和参考完整性。
APIs
所有3个系统测试的API大部分对处理是透明的,发生任何问题通常是由于不正确的文档,而不是API本身。
mSQL和MySQL都没有嵌入式SQL(ESQL)预处理器功能。随着ESQL的诞生,现在我相当喜欢它,但是使用mSQL 和 MySQL 本身提供的C API并不困难。有同样光标的含义,但以一个不同的方法实现,并且把字符串传递给C函数仅比在代码中使用码嵌入式 SQL语句稍难一点儿。
除了提到的ESQL API,PostgreSQL带有C API、C++绑定、JDBC、ODBC、Perl绑定、Python和Tcl绑定。
MySQL对Win32平台有附加的ODBC支持;语言联编 (接口)至少有C++、Eiffel、Java、Perl、Python、PHP和Tcl可以得到。
mSQL与Lite(一种类似C的脚本语言,与分发一起发行)紧密结合,可以得到一个称为 W3-mSQL的一个网站集成包,它是JDBC、ODBC、Perl和PHP API。
注意我没有测试那些任何附加的绑定和特征;他们的质量和文档的表述不是很好。能获得很多对这3个系统的第三方扩展;本文不再赘述。
文档和更多
PostgreSQL以DocBook SGML格式记录文档。手册分为管理员指南、程序员指南、用户指南和一本教程。另外,FAQ和各种各样的说明文件涉及一些话题。软件的好多领域缺乏足够的文档。
MySQL以GNU Texinfo格式记录文档;手册看起来完全。
mSQL有一个单个文件的手册(没有超文本),它有PostScript和HTML形式。作为能从一个商业软件产品的的角度所期望的,它覆盖了所有的特征。
认证和一般的安全
这时我还没谈及的一个话题,但是需要在这个比较中提及的是存取认证。ANSI SQL 对于存取控制提供很复杂且很精致的机制, 也就是GRANT和REVOKE语句。
PostgreSQL和MySQL能理解这些标准语句,但内部处理存取控制是不同的。可以得到mSQL的网站集成包中的一个认证增强程序(W3-mSQL),但是在它基本形态中,大多数数据库系统似乎没有任何内置的存取控制支持。不像safe_mysqld和postmaster,mSQL数据库守护程序假定有root运行的,它可以可能造成安全隐患。
任何大型数据库都需要一个安全概念,就像它需要一个彻底的数据库设计一样。不可能说他们中支持认证系统(即 PostgreSQL或MySQL)的哪一个是更安全的;这里, 任何事情均取决于设计。
在本系列的第四部分,本文做作者集中精力研究DBMS的性能测量和工具的全面总结。更多精彩文章及讨论,请光临枫下论坛 rolia.net