|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
MySQL已经为支持所有最流行的Web2.0语言做好了准备,诸如Ruby、Ajax等,当然还有PHP。有的业界分析师说过,“每一个Web2.0公司实质上就是一个数据库公司。本节供应DBI的背景信息―在编写本人的剧本和撑持其别人编写的剧本时,必要这些信息。假如已熟习DBI,则能够略过这节,间接跳到7.3节“运转DBI”。
DBI数据范例
从某些方面来讲,利用PerlDBIAPI相似于利用第6章先容的C客户机库。在利用C客户机库时,次要依托指向布局或数组的指针来挪用函数和会见与MySQL相干的数据。在利用DBIAPI时,除函数称为办法,指针称为援用外,也挪用函数和利用指向布局的指针。
指针变量称为句柄,句柄指向的峁钩莆韵蟆?br>DBI利用多少种句柄。它们常常经由过程表7-1所示的习用称号在DBI文件中援用。而习用的非句柄变量的称号如表7-2所示。实践上,在本章中,我们其实不利用每一个变量名,可是,在浏览其别人编写的DBI剧本时,懂得它们是有效的。
表7-1习用的PerlDBI句柄变量名
称号申明$dbh数据库工具的句柄$sth语句(查询)工具的句柄$fh翻开文件的句柄$h“通用”句柄;其意义取决于高低文表7-2习用的PerlDBI非句柄变量的称号
称号申明$rc从前往真或假的操纵中前往的代码$rv从前往整数的操纵中前往的值$rows从前往行数的操纵中前往的值@ary查询前往的暗示一行值的数组(列表)
一个复杂的DBI剧本
让我们从一个复杂剧本dump_members入手下手,它举例申明了DBI程序计划中多少尺度观点,如与MySQL服务器的毗连和断开、检索数据等。此剧本发生的了局为以制表符分开情势列出的汗青联盟成员。这个格局自己其实不让人感乐趣:在这里,懂得怎样利用DBI比发生大度的输入更加主要。
dump_members以下:
要想本人实验这个剧本,能够下载它(请参阅符录A),或利用文本编纂器创立它,然后使之可实行,以便能运转。固然,大概最少必要变动一些毗连参数(主机名、数据库名、用户名和口令)。本章中的其他DBI剧本也是如许。在参数缺省时,本章下载剧本的权限设置为只同意读。假如您将本人的MySQL用户名和口令放在它们当中,我倡议将它们保存为这类体例,以便其别人不克不及读取这些值。今后,在7.2.8节“指定毗连参数”中,我们将看到怎样从选项文件中取得这些参数,而不是将它们间接放在剧本中。
如今,让我们逐行看完这个剧本。第一行是尺度行,指出那里能够找到Perl的唆使器:
#!/usr/bin/perl
在本章将要会商的剧本中,每一个剧本都包括这行;今后不再申明。此剧本中最少应当含有一个冗长的目标申明,这是一个好主张,以是下一行是一个正文,给浏览此剧本的人供应一个关于它做甚么的线索:
#dump_members.dumpHistoricalLeaguesmembershiplist
从‘#’字符到行尾部的文本为正文。有需要做一些实习,就是在全部剧本中编写一些正文来注释它们怎样事情。
接上去是两个use行:
useDBI;
usestrict;
useDBI告诉Perl注释程序它必要引进DBI模块。假如没有这一行,试图在剧本中做与DBI相干的任何事,都将呈现毛病。不必要指出想要哪一个DBD级其余模块。在毗连数据库时,DBI会激活响应的模块。
usestrict告诉Perl,在利用它们之前必要声明变量。假如没有usestrict行,也能够编写剧本,可是,它有助于发明毛病,以是倡议一直要包含这行。比方,置为严厉形式时,假如声明变量$my_var,可是以后毛病地用$mv_var来会见,则在运转这个剧本时,将取得上面的动静:
Globalsymbol"$mv_var"requiresexplicitpackagenameatlinen
这个动静会使您想,“怎样了?$mv_var?我从未利用过这类称号的变量!”,然后,找到剧本中的第n行,看是甚么成绩,并更正它。假如不必严厉形式,Perl不会给出$mv_var;将只是复杂地按具有undef(不决义的)值的该称号创立一个新的变量,并毫无动态地利用它,然后,您会稀里糊涂剧本为何不事情。
由于我们在严厉形式下操纵,以是我们将界说剧本利用的变量:
如今我们筹办毗连数据库:
connect()挪用作为DBI->connect()来挪用,由于它是DBI类的办法。不用真正晓得它是甚么意义;它只是一个令人头痛的面向工具的行话(假如切实其实想晓得,那末它意味着connect()是“属于”DBI的一个函数)。connect()有多少参数:
数据源。(常常挪用的数据源称号,或DSN。)数据源格局由要利用的特定DBD模块需求来断定。关于MySQL驱动程序,同意的格局以下:
"DBI:mysql:db_name"
"DBI:mysql:db_name:host_name"
关于第一种格局,主机名缺省为localhost(实践上有其他同意的数据源格局,我们将在前面7.2.8节“指定毗连参数”中会商)。“DBI”年夜写不妨,可是“mysql”必需小写。
用户名和口令。
暗示分外毗连属性的可选参数。这个参数把持DBI的毛病处置举动,我们指定的看起来有点奇异的机关启用了RaiseError属性。这招致DBI反省与数据库相干的毛病,并显现动静,并且只需它检测到毛病就加入(这就是为何在dump_members剧本中的任何中央都没有看到毛病反省代码的缘故原由;DBI将它全体处置了)。7.2.3节“处置毛病”包含了对毛病呼应的可选办法。
假如connect()挪用乐成,则它前往数据库句柄,我们分派给$dbh(假如connect()失利,一般前往undef。但是,由于我们在剧本中启用了RaiseError,以是connect()不前往;可是,DBI将显现一条毛病动静,而且在呈现毛病时加入)。
毗连到数据库后,dump_members公布一条SELECT语句查询来检索全部成员列表,然后,实行一个轮回来处置前往的每行。这些行组成了却果集。
为了完成SELECT语句,起首必要筹办,然后再运转它:
#issuequery
$sth=$dbh->prepare("SELECTlast_name,first_name,suffix,email,"
"street,city,state,zip,phoneFROMmemberORDERBYlast_name");
$sth->execute();
使用数据库句柄挪用prepare();在实行前,它将SQL语句传送给预处置的驱动程序。实践上,在这里某些驱动程序做了一些有关这条语句的事变。其他驱动程序只是记着它,直到挪用execute()使这条语句被实行为止。从prepare()前往的值是一个语句句柄$sth,假如呈现毛病,则为undef。在进一步处置与这条语句相干的一切内容时,都利用这个语句句柄。
请注重,指定的这个查询没有分号停止符。您无疑有如许的(经由长工夫利用mysql程序养成的)习气,用‘;’字符停止SQL语句。但是,在利用DBI时,最好冲破这个习气,由于分号常常招致查询呈现语法毛病而失利。向查询增添‘g’也相似,利用DBI时不要如许。
在挪用一个办法而不必向它传送任何参数时,能够没有这个圆括号。以下两个挪用是等价的:
$sth->execute();
$sth->execute;
我宁肯有圆括号,由于它令人感应这个挪用看上往不像变量。您的选择便可能分歧了。
挪用execute()后,能够处置成员列表的行。在dump_members剧本中,提取行的轮回复杂地显现了每行的内容:
fetchrow_array()前往含有以后行的列值的数组,在没有残剩的行时,前往一个空数组。如许,此轮回提取了由SELECT语句前往的一连行,并显现列值之间用制表符分开的每行。在数据库中NULL作为undef值前往到Perl剧本,可是将它们显现为空字符串,而不是单词“NULL”。
请注重,制表符和换行符(暗示为‘ ’和‘
’)括在双引号中。在Perl中,只注释呈现在双引号内的本义符序列,不注释呈现在单引号内的本义符序列。假如利用单引号,则输入将为字符串“ ”和“
”。
提取行的轮回停止今后,挪用finish()告诉DBI不再必要语句句柄,而且开释分派给它的一切一时资本。实践上,除非只提取了局集的一部分(不管是计划的缘故原由,仍是由于呈现一些成绩),不然不必要挪用finish()。但是,在提取轮回以后,finish()一直是很保险的,我以为挪用并实行finish(),比辨别什么时候必要,什么时候不必要更简单一些。
我们已显现完了全体成员列表,以是我们能够从服务器上断开毗连,而且加入:
$dbh->disconnect();
exit(0);
dump_members示出了很多DBI程序的年夜多半通用观点,并且不用懂得更多的常识,就能够动手编写本人的DBI程序。比方,要想写出一些其他表的内容,所必要做的只是变动传送给prepare()办法的SELECT语句的文本。并且实践上,假如想懂得这类手艺的某些使用,可略过这部分,间接跳到7.3节“运转DBI”中会商怎样天生汗青联盟一年一度的宴会成员列表程序和League打印目次的部分。但是,DBI供应很多其他有效的功效。下一节先容了一些,以便可以在Perl剧本中看看怎样完成比运转一条复杂的SELECT语句更多的事变。
处置毛病
在dump_members挪用connect()办法时,应当启用RaiseError毛病处置属性,以便这些毛病用一条毛病动静就可以主动地停止响应的剧本。也能够用其他体例处置这些毛病。比方,能够本人反省毛病而不用利用DBI。
为了检察怎样把持DBI的毛病处置举动,我们来细心检察一下connect()挪用的终极参数。上面两个相干的属性是RaiseError和PrintError:
假如启用RaiseError(设为非零值),假如在DBI办法中呈现毛病,则DBI挪用die()来显现一条动静而且加入。
假如启用PrintError,在呈现DBI毛病时,DBI会挪用warn()来显现一条动静,可是响应剧本会持续实行。
缺省时,RaiseError是禁用的,而PrintError启用。在此情形下,假如connect()挪用失利,则DBI显现一条动静,并且持续实行。如许,假如省略connect()的四个参数,则失掉缺省的毛病处置举动,能够以下反省毛病:
$dbh=DBI->connect($dsn,$user_name,$password)orexit(1);
假如呈现毛病,则connect()前往undef暗示失利,而且触发对exit()的挪用。由于DBI已显现了毛病动静,以是您就纷歧定要显现它了。
假如明白给出该毛病反省属性的缺省值,可以下挪用connect()。
$dbh=DBI->connect($dsn,$user_name,$password,{RaiseError=>0,PrintError=>1})
orexit(1);
这就必要更多的编写事情,可是即便对不经意的读者,处置毛病举动也会更加分明。
假如想本人反省毛病,并显现本人的动静,应当禁用RaiseError和PrintError:
变量$DBI::err和$DBI::errstr,只用于所显现的die()挪用中,有助于机关毛病动静。它们含有MySQL毛病代码和毛病字符串,十分像CAPI函数中的mysql_errno()和mysql_error()。
假如仅仅要DBI处置毛病,以便不用本人反省它们,则启用RaiseError:
$dbh=DBI->connect($dsn,$user_name,$password,{RaiseError=>1});
到今朝为止,这是最简单的办法,而且是dump_members带来的。假如在剧本加入时,想要实行某品种型的扫除代码,启用RaiseError多是不得当的,只管在这类情形下,能够从头界说$SIG{_DIE_}句柄,能够做想做的事变。
制止启用RaiseError属性的另外一个缘故原由是DBI在它的动静中显现手艺信息,以下:
disconnect(DBI::db=HASH(0x197aae4)invalidates1activestatement.Either
destroystatementhandlesorcallfinishonthembeforedisconnecting.
关于编程者来讲,这是好的信息,但对一般用户大概没有甚么意义。在此情况,最好本人反省毛病,以即可以显现对希冀利用这个剧本的人更成心义的动静。大概也可在这里思索从头界说$SIG{_DIE_}句柄。如许大概很有效,由于它同意启用RaiseError来使毛病处置复杂化,而不是用本人的动静交换DBI给出的缺省毛病动静。为了供应本人的_DIE_句柄,可在实行任何DBI挪用之前,举行上面的事情:
$SIG{_DIE_}=sub{die"Sorry,anerroroccurred
";};
也能够用一般的作风界说一个子例程,并使用这个子例程的援用来设置这个句柄值:
除在connect()挪用中逐字传送毛病处置属性以外,还能够使用散列界说它们,并传送对这个散列的援用。有人发明以这类体例筹办属性设置使剧本更简单浏览和编纂,可是在功效上这两种办法是不异的。上面是一个申明怎样利用属性散列的样例:
上面的剧本dump_members2举例申明了当要本人反省毛病并显现本人的动静时,怎样编写剧本。dump_member2处置和dump_members一样的查询,可是明白地禁用PrintError和RaiseError,然后测试每一个DBI挪用的了局。假如呈现毛病,在加入之前,剧本挪用了子例程bail_out()显现动静及$DBI::err和$DBI::errstr的内容:
除bail_out()是加入而不是前往到挪用者之外,bail_out()相似于我们在第6章中为编写C程序利用的print_error()函数。每次想显现毛病动静时,bail_out()排除了写出$DBI::err和$DBI::errstr称号的贫苦。一样,经由过程封装显现到子例程的毛病动静,可变动子例程使全部剧本中毛病动静的格局分歧。
dump_member2剧本在提取行轮回的前面有一个测试,这是dump_members所没有的。由于假如在fetchrow_array()中呈现毛病,dump_members2不会主动地加入,以是人们判别轮回是由于了局集读取完成而停止(一般停止),仍是由于呈现毛病而停止做出断定是很坚苦的。固然,任何一种体例,轮回都将停止,可是假如呈现毛病,则将删截剧本的输入。假如没有毛病反省,运转该剧本的人将没法晓得是不是有错!假如本人反省毛病,应当反省提取轮回的了局。
处置不前往了局集的查询
DELETE、INSERT、REPLACE和UPDATE等实行后不前往行的语句比SELECT、DESCRIB、EXPLAIN和SHOW等实行后前往行的语句的处置绝对要简单一些。为处置一条非SELECT语句,使用数据库句柄,将它传送给do()。do()办法在一个步骤内筹办和实行该查询。比方,入手下手输出一个新的成员,MarcisBrown,停止日期为2002年6月3日,能够如许做:
do()办法前往触及行的计数,假如呈现毛病,则前往undef。由于各类缘故原由,大概呈现毛病(比方,这个查询多是畸形的,或大概没有会见这个表的权利)。关于非undef的前往,注重那些没有遭到影响的行的情形。当这类情形产生时,do()不前往数字0;而是前往字符串“0E0”(0的Perl迷信计数法情势)。“0E0”在数值上等价于0,可是,在前提测试中将其视为真,以即可以将其与初期的undef区分。假如do()前往0,则辨别是呈现了毛病(undef)仍是“没有遭到影响的行”这两种情形将更坚苦。利用上面的两个测试之一能够反省毛病:
if(!defined($rows)){#error}
if(!$rows){#error}
在数值情况中,“0E0”与0等价。上面的代码将准确地显现$rows的任何非undef值的行数:
也能够用printf()利用‘%d’格局显现$row来强迫举行隐含的数字转换:
do()办法等价于后跟execute()的prepare()。后面的INSERT语句能够不挪用do(),以下公布:
处置前往了局集的查询
本章供应了有关完成SELECT查询中提取行轮回的多少选项的具体信息(或其他相似于SELECT的前往行的查询,如DESCRIBE、EXPLAIN和SHOW)。还会商了怎样取得了局中行数的计数值,怎样处置不必要轮回的了局集,和怎样一次检索全部了局集的全体内容等。
1.编写提取行的轮回
dump_members剧本使用DBI办法的尺度序列检索数据:prepare()使驱动程序处置查询,execute()入手下手实行这个查询,fetchrow_array()提取了局会合的每行,finish()开释与这个查询相干的资本。
prepare()、execute()和finish()是处置前往行的查询中十分尺度的部分。但是,关于提取的行,fetchrow_array()实践上只是多少办法中的一种(请参阅表7-3)。
办法名前往值fetchrow_array()行值的数组fetchrow_arrayref()对行值数组的援用fetch()与fetchrow_arrayref()不异fetchrow_hashref()对行值的散列援用,列名键索引上面的例子说示出了如何利用每一个提取行办法。这些例子在全部了局集的行中轮回,关于每行,显现由逗号分开的列值。在某些情形下,编写这些显现代码另有一些更无效的办法,可是这些例子是以可以申明会见单个列值的语法的体例编写的。
可以下利用fetchrow_array():
对fetchrow_array()的每一个挪用都前往行值数组,不再有行时,前往一个空数组。
选择将前往值分派给数组变量,能够在一组标质变量中提取列值。假如想利用比$ary[0]、$ary[1]等更成心义的变量名,就能够如许做。假定要在变量中检索称号和电子邮件值,可以使用fetchrow_array(),能够以下选择并提取行:
固然,在以这类体例利用一列变量时,必需包管查询按准确的序次选择列。DBI不体贴SELECT语句指定列的序次,以是准确地分派变量是您的职责。在提取行时,利用一种称为参数束缚的手艺,也能够使列值主动分派给独自的变量。
fetchrow_arrayref()相似于fetchrow_array(),但不前往包括以后行的列值的数组,而是前往这个数组的援用,在没有乘余行时,前往undef。以下利用:
经由过程数组援用$ary_ref会见数组元素。这相似于援用指针,以是利用了$ary_ref->[$i]而不是$ary[$i]。要想援用全部数组,就要利用@{$ary_ref}布局。
fetchow_arrayef()不合适在列表中提取变量。比方,上面的轮回不起感化:
实践上,只需fetchrow_arrayref()提取一行,这个轮回就可以准确地运转。可是在没有更多的行时,fetchrow_arrayref()前往undef,而且@{undef}分歧法(它有些像在C程序中试图放弃一个NULL指针)。
提取行的第三个办法fetchrow_hashref(),以下利用:
对fetchrow_hashref()的每一个挪用都前往一个按列名索引的行值散列的援用,在没有更多的行时,前往undef。在此情形下,列值不按特定的序次呈现;Perl散列的成员是无序的。但是,散列元素是按列名索引的,以是$hashref供应了一个独自的变量,可经由过程它按称号会见任何列值。这使得能按恣意必要的序次来提取值(大概它们中的任何子集),并且不用晓得SELECT查询检索的列的序次。比方,假如想会见称号和电子邮件域,能够以下举行:
假如但愿将一行值传送给某个函数而又不必要这个函数晓得SELECT语句中指定列的序次时,fetchrow_hashref()长短常有效的。既然云云,能够挪用fetchrow_hashref()来检索行,而且编写一个利用列名会见来自行散列值的函数。
假如利用fetchrow_hashref(),请记着以下告诫:
假如功能很主要,则fetchrow_hashref()并非最好的选择,由于它没有fetchrow_array()或fetchrow_arrayref()的效力高。
作为散列键值利用的列名具有与SELECT语句中写出时不异的字符。在MySQL中,列名不辨别巨细写,以是此查询也是如许,不论以年夜写字母仍是小写字母给出列名,查询了局都是一样的。可是Perl散列索引名是辨别巨细写的,这大概会带来一些成绩。为了不潜伏的巨细写不婚配成绩,可经由过程传送NAME_lc或NAME_uc属性,告诉fetchrow_hashref()强制列名为年夜写或小写:
散列对每一个独一的列名含有一个元素。假如正在实行从多个具有堆叠称号的表中前往列的毗连,则不克不及会见一切的列值。比方,假如公布上面的查询,fetchrow_hashref()将前往只要一个元素的散列:
SELECTa.nameFROMa,bWHEREa.name=b.name
2.断定查询前往的行数
怎样晓得SELECT或相似于SELECT的查询前往的行数?一种办法是,当提取它们时,盘算这些行的数目。实践上,这是晓得SELECT查询前往几行的独一便利的办法。利用MySQL驱动程序,能够在挪用execute()后使用语句句柄挪用rows()办法,可是这对其他数据库引擎其实不便利。并且即便就MySQL来讲,假如已设置了mysql_use_result属性,rows()也不克不及前往准确的了局,直到提取了一切行(有关的具体信息,请参阅附录G)。以是只能如提取行一样对它们举行计数。
3.提取单行的了局
假如了局集只含单个行,则不必要运转轮回来取得了局。假定要编写得出汗青联盟成员以后数目的剧本count_members。完成查询的代码以下所示:
SELECT语句只前往一行,以是不必要轮回;我们只挪用fetchrow_array()一次。别的,由于我们只选择一列,以是乃至不必要将前往值分派给数组。当在标量情况中(单个值而不是所希冀的一列)挪用fetchrow_array()时,它前往这个行的第一列,假如没有更多的无效行,则前往undef。
另外一种希冀最多有一个纪录的查询是一个含有LIMIT1来束缚前往的行数的查询。其一样平常的用法是前往特定列含有最年夜或最小值的行。比方,上面的查询给出比来出身的总统姓名和出身日期:
必需无提取轮回的其他范例的查询使用MAX()或MIN()来选择单个值。可是在一切这些情形下,取得单个行了局的一种更简单的办法就是利用数据库句柄办法selectrow_array(),它分离了prepare()、execute()并在单个挪用中提取行。它前往一个数组(而不是一个援用),假如呈现毛病,则前往一个空数组。前一例子可使用selectrow_array()编写以下:
4.处置完全的了局集
在利用提取轮回时,DBI不供应在了局会合随便查找的办法,或以任何序次而不是以轮回前往的序次来处置行。一样,提取行今后,假如没有保留,前一行会丧失。这类做法其实不必定符合以下情形:
以非一连的序次处置行。思索一种情形,想以汗青联盟的president表中列出的美国总统为主体,举行一些检验。假如但愿每次检验时都以分歧的序次提出成绩,则能够从president表当选择一切行。然后,大概以恣意的序次提取行来改动与所问成绩有关的总统的序次。要想恣意地提取一行,就必需同时会见一切的行。
只利用前往行的子集,对其举行随机选择。比方,当问及总统出身地时,要想呈现多个选择的成绩,则能够任意地提取一行来选择总统(准确的谜底),然后再从取来搅扰的选择中提取多少其他行。
即便的确以一连的序次去向理,也想牢牢捉住全部了局集。假如想经由这些行举行多个传送,这多是必须的。比方,在统计盘算中,大概先扫瞄一遍了局集,来估量数据的一些通用数字属性,然后再次反省这些行,来完成加倍明白的剖析。
能够用几个分歧的体例作为一个全体会见了局集。能够完成这个罕见的提取轮回,并在提取它时保留每行,可使用一次前往全部了局集的办法。不管哪一种办法都以在了局会合包含一行一行的矩阵作为停止,和选择的列一样多。能够以任何序次恣意屡次地处置矩阵的
元素。上面的会商申明这两种办法。
利用提取轮回来捕捉了局集的一种办法是利用fetchrow_array()并保留对这些行援用的数组。除保留一切的行,然后显现矩阵举例申明了怎样断定矩阵中的行数和列数,及怎样会见矩阵的一般成员之外,上面的代码和dump_members中提取和显现的轮回感化是一样的。
在断定矩阵的维数时,必需起首断定行数,由于不管这个矩阵是不是为空,都大概盘算列数。假如$rows为0,则这个矩阵为空,而且$cols也为0。不然,列数大概作为行数组中的元素数目来盘算,用语法@{$matrix[$i]}来全体会见行$i。
在前述的样例中,我们提取每行,然后保留对它的援用。能够假想挪用fetchrow_arrayref()而不是间接地检索行援用大概更无效率:
它不克不及一般事情,由于fetchrow_arrayref()从头利用了援用指向的数组。了局矩阵是一个援用的数组,数组中的每一个元素都指向不异行―最初检索的行。因而,假如想一次提取一行,则要利用fetchrow_array()而不是fetchrow_arrayref()。
另外一个选择是利用提取轮回,可使用前往全部了局集的DBI办法中的一个。比方,fetchall_arrayref()前往对援用数组的援用,数组的每一个元素都指向了局会合某行。这十分复杂,但很无效,这个前往值是对矩阵的援用。要想利用fetchall_arrayref(),则挪用prepare()和execute(),然后以下检索了局:
假如了局集为空,则fetchall_arrayref()前往一个对空数组的援用。假如呈现毛病,则了局为undef,以是假如没有启用RaiseError,则在入手下手利用它之前,要确保反省前往值。
行数和列数由矩阵是不是为空来断定。假如想作为一个数组会见这个矩阵的全部行$i,应当利用语法@{$matrix_ref->[$i]}。
利用fetchall_arrayref()来检索了局集固然比编写一个提取行的轮回要更复杂一些,只管会见数组元素的语法是一个小技能。一个与fetchall_arrayref()办法相相似,但却做了更多事情的办法是selectall_arrayref()。这个办法为您完成了全部prepare()、execute()、提取轮回、finish()序列。为了利用selectall_arrayref(),应当使用数据库句柄间接将查询传送给它:
5.反省NULL值
从数据库中检索数据时,大概必要辨别值为NULL、为0大概为空字符串的列。由于DBI前往NULL列值作为undef,以是这类辨别是简单的。但是,必需确保利用准确的测试。假如试用上面的代码段,则它一切三次显现都为“false!”:
并且,关于这两个测试,这段代码都显现“false!”:
上面这段代码效果不异:
要想辨别NULL列值和非NULL列值,则利用defined()。晓得了没有呈现NULL值以后,利用得当的测试能够在其他范例值之间加以辨别。比方:
以得当的序次完成这些测试是很主要的,由于假如$col_val为空字符串,则第二个和第三个对照就都为真。假如倒置对照的序次,则会毛病地将空字符串标识为0。
援用成绩
迄今为止,我们已使用援用字符串以最基础的体例机关了查询。在援用的字符串含有援用值时,会在Perl辞汇一级发生成绩。在拔出大概选择含有引号、反斜杠或二进制数据的值时,在SQL中也大概出成绩。假如指定一个查询作为Perl援用的字符串,则必需制止在查询字符串自己中呈现援用字符:
Perl和MySQL都同意用单引号或双引号援用字符串,以是夹杂利用援用字符偶然能够制止这类没法援用援用字符本身的情形:
但是,在Perl中,这两品种型的引号其实不等价。只要在双引号外部才注释为变量援用。因而,当想经由过程在查询字符串中嵌进变量援用来机关查询时,单引号并非十分有效的。比方,假如$var的值为14,则上面的两个字符串其实不等价:
两个字符串的注释以下所示;明显,第一个字符串与但愿传送给MySQL服务器的内容更加相像:
用双引号来援用字符串的另外一个选择是利用qq{}布局,它告知Perl在‘qq{’和‘}’之间的每一个字符都要看做为双引号括起的字符串(两个q暗示“双引号”)。比方,以下两行是等价的:
利用qq{}时,机关查询不必过量思索引号的成绩,由于能够在这个查询字符串内自在地利用引号(单引号或双引号),而不必避开它们。别的,还注释了变量援用。qq{}的这两种特征可用上面的INSERT语句来讲明:
纷歧定利用‘{’和‘}’作为qq的分开符。其他格局,如qq()和qq//,也能够利用,只需关闭的分开符不呈现在字符串内便可。我喜好用qq{},由于‘{’不像‘)’或‘/’会呈现在查询的文本内,而且在查询字符串的开头也大概有成绩。比方,‘)’呈现在所显现的INSERT语句的外部,以是qq()关于援用查询字符串来讲不是一个有效的布局。
qq{}布局能跨行,假如想让查询字符串在Perl代码中夺目,这很有效:
假如但愿将查询格局分为多个行,从而使它的可读性更强,如许也很有效。比方,dump_members剧本中的SELECT语句以下:
用qq{}编写以下:
双引号字符串也能够跨行。可是,关于编写多行的字符串,我更喜好用qq{}。我发明当在一行中看到不婚配的引号时,我就天然地往想,“这会是语法毛病吗?”,然后,我就会华侈工夫往寻觅相婚配的引号。
qq{}布局在Perl辞汇级注重了援用的成绩,因而可将引号简单地放到字符串内,而不会使Perl弄混它们。但是,还必需思索SQL级的语法。思索向member表中拔出一笔记录:
do()发送给MySQL的字符串以下所示:
这是分歧法的SQL语句,由于在单引号字符串内呈现了单引号。在第6章中,我们碰到过相似的援用成绩。在那边,我们利用mysql_escape_string()来处置这个成绩。DBI供应了一个相似的机制―在一条语句中,对想按字面利用的每一个援用值,都挪用quote()办法,并利用它的前往值。
后面的例子可编写以下:
如今,do()发送给MySQL的字符串以下所示,具有呈现在援用字符串内的大概对服务器本义的引号:
请注重,在查询字符串中援用$last和$first时,不要增添括起来的引号;quote()办法撑持它们。假如增添了引号,则查询将呈现过量的引号,以下面的例子所示:
这些语句发生上面的输入:
占位符和参数束缚
在后面各节中,我们经由过程把要拔出或选择的值作为选择尺度,间接放在查询字符串中机关了查询。纷歧定非要如许做。DBI同意在查询字符串外部安排一些称为占位符的特别标志符,然后,在实行该查询时,将这些值取代那些标识符来利用。如许做的次要缘故原由是进步功能,出格是在轮回中重复实行某个查询的时分。
为了申明占位符怎样事情,举例申明。假定黉舍新学期刚入手下手,盘算清算学分薄的student表,然后使用包括在文件中的一列先生姓名将其初始化,使其包括新先生。不必占位符,能够以下如许删除现有表的内容,并装进新的姓名:
如许做效力很低,由于INSERT查询的基础格局每次都是不异的,而且在全部轮回中,do()每次都挪用prepare()和execute()。在进进这个轮回之前,只挪用一次prepare()来设置INSERT语句,而且在这个轮回外部只挪用execute(),如许做效力更高一些。只挪用一次prepare(),可制止其他屡次挪用。DBI同意我们如许做:
请注重这个INSERT查询中的‘?’就是一个占位符。挪用execute()时,将查询发送给服务器,传送这个值来取代占位符。一样平常来讲,假如发明在轮回外部挪用了do(),应当在轮回前挪用prepare(),并在这个轮回外部挪用execute()更好一些。
有关占位符的一些注重事项:
在查询字符串内,不要在引号中封装占位符字符。假如如许做,不克不及辨认为占位符。
不要利用quote()办法来指定占位符的值,不然将在拔出的值中失掉分外的引号。
在查询字符串中能够有一个以上的占位符,可是要确保占位符的标志符与传送给execute()的值一样多。
每一个占位符都必需指定一个独自的值,而不是一列值。比方,不克不及运转如许的语句:
为了将NULL指定为占位符,应当利用undef。
不要对关头字利用占位符。如许会出成绩,由于占位符的值是由quote()主动处置的。
关头字将被放在引号括起来的查询中,因而,这个查询会因为语法毛病而失利。
除在轮回中进步效力之外,关于某些数据库引擎,能够从占位符的利用中取得其他的功能优点。某些引擎高速缓存了筹办好的查询,和为无效地运转这个查询所天生的企图。也就是说,假如今后这个服务器收到一样的查询,则它能够再次利用响应的企图而不必天生。查询高速缓存出格有助于庞大的SELECT语句,由于大概必要消费工夫天生较好的实行企图。占位符供应了一个在高速缓存中寻觅查询的好时机,由于它们使查询比间接在查询字符串中嵌进指定的列值来机关查询更通用。关于MySQL,在这类体例下,占位符其实不进步功能,由于没有高速缓存查询。但是,大概仍想利用占位符编写本人的查询;假如偶尔将DBI剧本传送给撑持查询高速缓存的引擎,则这个剧本比没有占位符时运转效力更高。
在查询运转时,同意在查询字符串顶用占位符取代这些值。换句话说,能够参数化这个查询的“输出”。在提取行而不用将值赋给变量时,DBI也供应一个称为参数束缚的输入操纵,同意经由过程检索主动进进这些变量的列值使“输入”参数化。
假定有一个查询,检索member表中的成员姓名。能够告知DBI将选定列的值赋给Perl变量。在提取行时,变量使用响应的列值主动举行更新。上面是一个例子,申明怎样将这些列束缚到变量上,然后在提取轮回中会见它们:
bind_col()的每一个挪用都应当指定一个列号和一个但愿与该列相联的变量的援用。列号从1入手下手。bind_col()应当在execute()以后挪用。
另有一种选择,就是独自挪用bind_col(),能够在bind_columns()的单个挪用中传送全体变量援用:
指定毗连参数
创建服务器的毗连的最间接的办法为,挪用connect()办法时指定一切毗连参数:
假如漏掉毗连参数,则DBI做上面的事变:
假如不决义数据源或不决义空字符串,则利用DBI_DSN情况变量。假如不决义用户名和口令,则利用DBI_USER和DBI_PASS情况变量(但假如它们为空字符串则不利用)。在Windows下,假如不决义用户名,则利用user变量。
假如漏掉了主机名,其缺省值为localhost。
假如将用户名指定为undef或空字符串,则其缺省为UNIX的登录称号。在Windows下,用户名缺省为ODBC。
假如将口令指定为undef或空字符串,则不传送口令。
经由过程将某些选项增加到字符串的初始部分,每一个都在分号后面,能够在数据源中指定这些选项。比方,可使用mysql_read_default_file选项来指定一个选项文件的路径名:
当实行这个剧本时,它将从这个文件中读取毗连参数。假定/u/paul/.my.cnf含有上面的内容:
然后connect()挪用试图毗连到pit-viper-snake-net上的MySQL服务器,而且用口令secret及用户名paul毗连。假如想同意具有准确地设置选项文件的任何人利用您的剧本,则像如许指定命据源:
$ENV{HOME}含有效户运转这个剧本的主目次的路径名,以是这个剧本利用的主机名、用户名和口令将会从每一个用户本人的选项文件中抽掏出来。以这类体例编写剧本,不用在这个剧本中逐字地嵌进毗连参数。
还可使用mysql_read_default_group选项,来指定一个选项文件组。这主动地招致读取用户的.my.cnf文件,而且除[client]组之外,还同意读取一个指定的选项组。比方,假如在DBI剧本中具有指定的选项,则能够将它们列在[dbi]组中,然后以以下体例利用数据源值:
mysql_read_default_file和mysql_read_default_group必要MySQL3.22.10或更新的版本,和DBD::mysql1.21.06或更新的版本。有关指定的数据源字符串的选项的具体信息,请参阅附录G。有关MySQL选项文件格局的具体信息,请参阅附录E。
利用选项文件其实不防碍在connect()挪用中指定毗连参数(比方,假如想这个剧本作为特别的用户来毗连)。在connect()挪用中指定的任何明白的主机名、用户名和口令值都将掩盖在选项文件中找到的毗连参数。比方,想要剧本从命令行平分析--host、--user和--password选项,并利用那些值,假如给定,则优先于在选项文件中发明的任何内容。这是有效的,由于它是尺度的MySQL客户机操纵的体例。DBI剧本将因而切合它的举动。
关于在本章中我们开辟的保存在命令行中的剧本,我将利用一些尺度的毗连设置代码及卸载代码。我只在这里申明它一次,以便我们能够将精神会合在每一个剧本的主体上,我们编写以下代码:
这个代码初始化DBI,在命令行中查找毗连参数,然后利用命令行中的大概在用户运转这个剧本的-/.my.cnf文件中所找到的参数,毗连到MySQL服务器。假如在主目次中设置.my.cnf文件,则当运转这个剧本时,纷歧定要输出任何毗连参数(请记着,设置这类体例,以便没有其别人读取这个文件。有关的引导请参阅附录E)。
我们剧本的最初部分也相似于从剧本到剧本;它复杂地停止这个毗连并加入:
$dhb->disconnect();
exit(0);
当我们读到Web程序计划的部分,即7.4节“在Web使用程序中利用DBI”时,将修正一些这个毗连设置代码,可是基础的头脑是相似的。
调试
当想调试有妨碍的DBI剧本时,一般利用两项手艺,即独自利用一个或一前一后地共同利用。起首,在剧本的全部过程当中编写显现语句。它同意将本人调试的输入计划为想要的体例,但必需手工地增添语句。其次,可使用DBI的内建跟踪才能。这加倍通用,但也加倍
体系,并且它在翻开今后,则会主动地呈现。DBI跟踪也申明一些除此之外就没法取得的有关驱动程序的操纵信息。
1.利用显现语句调试
在MySQL邮件清单中,罕见成绩之一是:“有一个查询,当我在mysql中实行它时运转得很好,可是它不克不及在我的DBI剧本中事情,怎样回事?”寻觅公布分歧查询的DBI剧本和这个提问者所希冀的一样是很寻常的。假如在实行它之前显现查询,则大概会惊奇地看到真
正发送到这个服务器上的内容。假定将一个查询键进到mysql中(没有停止的分号)
然后,在DBI剧本中试着做不异的事变:
只管它是一样的查询,但它不克不及事情。不是吗?试着显现:
print"$query
"
了局以下:
INSERTmember(last_name,first_name,expiration)
VALUES(Brown,Warcia,2002-6-3)
从这个输入中,能够看到是您健忘了VALUES()列表中这些列值前后的引号。指定查询的准确办法以下:
大概,可使用占位符指定查询,并传送这些值,间接拔出到do()办法中:
不幸的是,当作这些的时分,利用显现语句不克不及看到完全查询的模样,由于直到挪用do()才干估量占位符的值。当利用占位符时,跟踪大概对换试办法更有匡助。
2.利用跟踪调试
当试图查出剧本不克不及准确事情的缘故原由时,能够告诉DBI来天生跟踪(调试)信息。跟踪级别局限从0(封闭)到9(最多信息)。一样平常来讲,跟踪级别1和2是最有效的。级别2跟踪申明正在实行的查询文本(包含占位符交换的了局)、挪用quote()的了局等等。这大概对捕捉成绩有极年夜的匡助。
利用trace()办法,能够从自力的剧本外部把持跟踪,大概能够设置DBI_TRACE情况变量来影响所运转的一切DBI剧本的跟踪。
要想利用trace()挪用,则传送一个跟踪级别参数,并能够有选择地再传送一个文件名。假如没有指定文件名,则一切的跟踪输入到STDERR中;不然,它就转到这个定名的文件中。一些样比方下:
当挪用DBI->trace()时,跟踪一切的DBI操纵。一个更精密的办法是,能够用自力的处置级别启用跟踪。当没想好剧本中成绩的地位,并对在那点呈现的每件事的跟踪输入不想加入时,这是有匡助的。比方,假如特定的SELECT查询有成绩,则能够跟踪与这个查询相干的语句句柄:
假如对任何trace()挪用指定一个文件名参数,则不管对DBI作为全体仍是独自的句柄,一切的跟踪输入都要到谁人文件中。
要想对运转的一切DBI剧本全体都翻开跟踪,则从命令注释程序中设置DBI_TRACE情况变量。它的语法取决于利用的命令注释程序:
value的形式和一切命令注释程序的形式一样:数字n暗示在级别n翻开跟踪到STDERR中;文件名翻开级别2跟踪到这个定名的文件,或n=file_name翻开级别n跟踪到这个定名的文件中。上面的样例利用了csh语法:
假如翻开跟踪到命令注释程序中的文件,则确保一旦办理了这个成绩,就将它封闭。将调试输入增添到这个跟踪文件中,而不必重写它,以是假如不当心,则这个文件大概变得十分年夜。极为欠好的设法是在命令注释程序的启动文件(如.cshrc、.lonin或.profile)中界说DBI_TRACE!在UNIX下,可使用上面两个命令(csh语法)之一封闭跟踪:
%setenvDBI_TRACE0
%unsetenvDBI_TRACE
关于sh、ksh或bash,如许做:
$DBI_TRACE=0
$exportDBI_TRACE
在Windows操纵体系中,可使用上面两个命令之一封闭跟踪:
c:>unsetDBI_TRACE
c:>setDBI_TRACE=0
利用了局集元数据
可使用DBI来取得会见了局集元数据――也就是有关由查询选择行的形貌信息。会见与了局集天生的查询所相干的语句句柄的属性来取得这个信息。供应这些属性中有一些是作为可用于高出一切数据库驱动程序的尺度DBI属性(如NUM_OF_FIELDS,了局会合列的数目)。别的一些是MySQL特定的,由DBD::mysql所供应的DBI的MySQL驱动程序。这些属性,如mysql_max_length告诉了每列值的最年夜宽度,不克不及用于其他数据库引擎。要想利用任何MySQL特定的属性,都必需冒着使剧本不成移植到其他数据库的伤害。另外一方面,它们可使它更简单地取得想要的信息。
必需在得当时分哀求元数据。一样平常来讲,直到挪用prepare()和execute()以后,了局集属性才干用于SELECT语句。除此以外,在挪用finish()以后,属性大概变成有效。
让我们来看看怎样利用MySQL的一个元数据属性mysql_max_length,与保存查询列名的DBI级其余NAME属性一同利用。我们能够将这些属性供应的信息兼并起来,编写一个剧本box_out,它以交互形式运转mysql客户机程序时取得的不异边框作风,从SELECT查询发生输入。box_out的主体以下(能够用任何其他的语句交换SELECT语句;编写输入的例程自力于特定的查询):
用execute()将这个查询初始化以后,我们取得了所需的元数据。$sth->{NAME}和$sth->{mysql_max_length}给出了列名和每列值的最年夜宽度。为了在这个查询中为列定名,每一个属性值都援用了一个数组,这个数组含有了局集每列中的一个值。
残剩的盘算十分相似于在第6章中开辟的客户机程序5中所利用的那些内容。比方,为制止偏离输入,假如列的名比该列中任何数据值都宽,则我们要向上调剂列的宽度值。
输入函数print_dashes()和print_row()代码编写以下,它们也相似于客户机程序5中响应的代码:
box_out的输入以下:
我们的下一个剧本利用了列元数据来发生分歧格局的输入。这个剧本show_member,同意疾速扫瞄汗青联盟成员项目,而不必输出任何查询。给出成员的姓,它就如许显现所选择的项目:
利用成员资历号码,大概利用与多少姓相婚配的形式也能够挪用show_members。上面的命令申明成员号码为23的项目,和以字母“C”入手下手的姓的成员项:
show_member剧本的主体以下所示。它利用了NAME属性来断定输入的每行所利用的标号和NUM_OF_FIELDS属性,找出这个了局集含有的列数:
不管地区是甚么,show_member的目标都是申明一个项目标全体内容。经由过程利用SELECT*来检索一切的列和NAME属性来看看它们是甚么,即便从member表中增添或删除列,这个剧本也会事情而不必做修正。
假如不检索任何行就想晓得一个表含有哪些列,则能够公布上面这条查询:
SELECT*FROMtbl_nameWHERE1=0
以一般体例挪用prepare()和execute()以后,能够从@{$sth->{NAME}}中失掉列名。但是,请注重,只管利用“空”查询的这个小秘诀能够在MySQL下运转,可是它不成移植,并且并非对一切的数据库引擎都能够事情的。
有关DBI和DBD::mysql所供应属性的具体信息,请拜见附录G。它完整可使您断定是想经由过程制止MySQL特定的属性而为可移植性消费勉力,仍是在可移植性的开支方面使用它们。
“对于MySQL数据库,无论是在开发方面,还是支持方面,现在有大量强大的MySQL学习教程可以选择。每一个新手开发者可以轻松地使用MySQL数据库进行开发。 |
|