国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Perl > 正文

Perl DBI 基礎(chǔ)

2024-07-21 02:13:11
字體:
供稿:網(wǎng)友
注冊會員,創(chuàng)建你的web開發(fā)資料庫,   本節(jié)提供dbi 的背景信息—在編寫自己的腳本和支持其他人編寫的腳本時,需要這些信息。如果已經(jīng)熟悉dbi,則可以略過這節(jié),直接跳到7 . 3節(jié)“運行dbi”。

dbi 數(shù)據(jù)類型

    從某些方面來說,使用perl dbi api 類似于使用第6章介紹的c 客戶機庫。在使用c 客戶機庫時,主要依靠指向結(jié)構(gòu)或數(shù)組的指針來調(diào)用函數(shù)和訪問與mysql相關(guān)的數(shù)據(jù)。在使用dbi api 時,除了函數(shù)稱為方法,指針稱為引用外,也調(diào)用函數(shù)和使用指向結(jié)構(gòu)的指針。
指針變量稱為句柄,句柄指向的峁鉤莆韻蟆?br>     dbi 使用若干種句柄。它們往往通過表7-1所示的慣用名稱在dbi 文件中引用。而慣用的非句柄變量的名稱如表7 - 2所示。實際上,在本章中,我們并不使用每個變量名,但是,在閱讀其他人編寫的dbi 腳本時,了解它們是有用的。
表7-1慣用的perl dbi 句柄變量名

名稱說明
$dbh數(shù)據(jù)庫對象的句柄
$sth語句(查詢)對象的句柄
$fh打開文件的句柄
$h“通用”句柄;其意義取決于上下文

表7-2 慣用的perl dbi 非句柄變量的名稱

名稱說明
$rc從返回真或假的操作中返回的代碼
$rv從返回整數(shù)的操作中返回的值
$rows從返回行數(shù)的操作中返回的值
@ary查詢返回的表示一行值的數(shù)組(列表)


一個簡單的dbi 腳本

    讓我們從一個簡單腳本d um p _ member s開始,它舉例說明了dbi 程序設(shè)計中若干標準概念,如與mysql服務(wù)器的連接和斷開、檢索數(shù)據(jù)等。此腳本產(chǎn)生的結(jié)果為以制表符分隔形式列出的歷史同盟成員。這個格式本身并不讓人感興趣:在這里,了解如何使用dbi 比產(chǎn)生漂亮的輸出更為重要。
    dump_members 如下:


    要想自己試驗這個腳本,可以下載它(請參閱符錄a),或使用文本編輯器創(chuàng)建它,然后使之可執(zhí)行,以便能運行。當然,可能至少需要更改一些連接參數(shù)(主機名、數(shù)據(jù)庫名、用戶名和口令)。本章中的其他dbi 腳本也是這樣。在參數(shù)缺省時,本章下載腳本的權(quán)限設(shè)置為只允許讀。如果您將自己的mysql用戶名和口令放在它們之中,我建議將它們保留為這種方式,以便其他人不能讀取這些值。以后,在7 . 2 . 8節(jié)“指定連接參數(shù)”中,我們將看到如何從選項文件中獲得這些參數(shù),而不是將它們直接放在腳本中。
    現(xiàn)在,讓我們逐行看完這個腳本。第一行是標準行,指出哪里可以找到perl 的指示器:
    #! /usr/bin/perl
    在本章將要討論的腳本中,每個腳本都包含這行;以后不再說明。此腳本中至少應(yīng)該含有一個簡短的目的說明,這是一個好主意,所以下一行是一個注釋,給閱讀此腳本的人提供一個關(guān)于它做什么的線索:
    # dump_members.dump historical league's membership list
    從‘#’字符到行尾部的文本為注釋。有必要做一些練習,就是在整個腳本中編寫一些注釋來解釋它們?nèi)绾喂ぷ鳌?br>    接下來是兩個use 行:
    use dbi;
    use strict;
    use dbi 告知perl 解釋程序它需要引入dbi 模塊。如果沒有這一行,試圖在腳本中做與dbi 相關(guān)的任何事,都將出現(xiàn)錯誤。不需要指出想要哪個dbd 級別的模塊。在連接數(shù)據(jù)庫時,dbi 會激活相應(yīng)的模塊。
    use strict 告知perl,在使用它們之前需要聲明變量。如果沒有use strict 行,也可以編寫腳本,但是,它有助于發(fā)現(xiàn)錯誤,所以建議始終要包括這行。例如,置為嚴格模式時,如果聲明變量$ my _ v a r,但是之后錯誤地用$mv_var 來訪問,則在運行這個腳本時,將獲得下面的消息:
    global symbol "$mv_var" requires explicit package name at line n
    這個消息會使您想,“怎么了?$ m v _ v a r?我從未使用過這種名稱的變量!”,然后,找到腳本中的第n行,看是什么問題,并改正它。如果不用嚴格模式, perl 不會給出$ m v _ v a r;將只是簡單地按具有un d e f(未定義的)值的該名稱創(chuàng)建一個新的變量,并毫無動靜地使用它,然后,您會莫名其妙腳本為什么不工作。
    因為我們在嚴格模式下操作,所以我們將定義腳本使用的變量:

    現(xiàn)在我們準備連接數(shù)據(jù)庫:

    connect( ) 調(diào)用作為dbi->connect( ) 來調(diào)用,因為它是dbi 類的方法。不必真正知道它是什么意思;它只是一個使人頭痛的面向?qū)ο蟮男性挘ㄈ绻拇_想知道,那么它意味著connect( ) 是“屬于”dbi 的一個函數(shù))。connect( ) 有若干參數(shù):
    數(shù)據(jù)源。(經(jīng)常調(diào)用的數(shù)據(jù)源名稱,或d s n。)數(shù)據(jù)源格式由要使用的特定dbd 模塊需求來確定。對于mysql驅(qū)動程序,允許的格式如下:
    "dbi:mysql:db_name"
    "dbi:mysql:db_name:host_name"
    對于第一種格式,主機名缺省為localhost(實際上有其他允許的數(shù)據(jù)源格式,我們將在后面7 . 2 . 8節(jié)“指定連接參數(shù)”中討論)。“dbi”大寫沒關(guān)系,但是“ mysql”必須小寫。
    用戶名和口令。
    表示額外連接屬性的可選參數(shù)。這個參數(shù)控制dbi 的錯誤處理行為,我們指定的看起來有點奇怪的構(gòu)造啟用了raiseerror 屬性。這導致dbi 檢查與數(shù)據(jù)庫相關(guān)的錯誤,并顯示消息,而且只要它檢測到錯誤就退出(這就是為什么在dump_members 腳本中的任何地方都沒有看到錯誤檢查代碼的原因; dbi 將它全部處理了)。7 . 2 . 3節(jié)“處理錯誤”包括了對錯誤響應(yīng)的可選方法。
    如果connect( ) 調(diào)用成功,則它返回數(shù)據(jù)庫句柄,我們分配給$dbh(如果connect( ) 失敗,通常返回un d e f。然而,因為我們在腳本中啟用了r a i s e e r r o r,所以connect( )不返回;但是,dbi 將顯示一條錯誤消息,并且在出現(xiàn)錯誤時退出)。
    連接到數(shù)據(jù)庫后, dump_members 發(fā)布一條select 語句查詢來檢索全體成員列表,然后,執(zhí)行一個循環(huán)來處理返回的每一行。這些行構(gòu)成了結(jié)果集。
    為了完成s e l e c t語句,首先需要準備,然后再運行它:
    # issue query
    $sth=$dbh->prepare("select last_name,first_name,suffix,email,"
         "street,city,state,zip,phone from member order by last_name");
    $sth->execute();
    利用數(shù)據(jù)庫句柄調(diào)用prepare( );在執(zhí)行前,它將sql 語句傳遞給預處理的驅(qū)動程序。實際上,在這里某些驅(qū)動程序做了一些有關(guān)這條語句的事情。其他驅(qū)動程序只是記住它,直到調(diào)用execute( ) 使這條語句被執(zhí)行為止。從prepare( ) 返回的值是一個語句句柄$ s t h,如果出現(xiàn)錯誤,則為un d e f。在進一步處理與這條語句相關(guān)的所有內(nèi)容時,都使用這個語句句柄。
    請注意,指定的這個查詢沒有分號結(jié)束符。您無疑有這樣的(經(jīng)過長時間使用mysql程序養(yǎng)成的)習慣,用‘ ;’字符終止sql 語句。然而,在使用dbi時,最好打破這個習慣,因為分號經(jīng)常導致查詢出現(xiàn)語法錯誤而失敗。向查詢增加‘ / g’也類似,使用dbi 時不要這樣。
    在調(diào)用一個方法而不用向它傳遞任何參數(shù)時,可以沒有這個圓括號。下列兩個調(diào)用是等價的:
    $sth->execute();
    $sth->execute;
    我寧愿有圓括號,因為它使人感到這個調(diào)用看上去不像變量。您的選擇就可能不同了。
    調(diào)用execute( ) 后,可以處理成員列表的行。在dump_members 腳本中,提取行的循環(huán)簡單地顯示了每行的內(nèi)容:

    fetchrow_array( ) 返回含有當前行的列值的數(shù)組,在沒有剩余的行時,返回一個空數(shù)組。這樣,此循環(huán)提取了由select 語句返回的連續(xù)行,并顯示列值之間用制表符分隔的每一行。在數(shù)據(jù)庫中null 作為undef 值返回到perl 腳本,但是將它們顯示為空字符串,而不是單詞“null”。
    請注意,制表符和換行符(表示為‘ / t’和‘/ n’)括在雙引號中。在perl 中,只解釋出現(xiàn)在雙引號內(nèi)的轉(zhuǎn)義符序列,不解釋出現(xiàn)在單引號內(nèi)的轉(zhuǎn)義符序列。如果使用單引號,則輸出將為字符串“/ t”和“/ n”。
    提取行的循環(huán)終止以后,調(diào)用finish( ) 告知dbi 不再需要語句句柄,并且釋放分配給它的所有臨時資源。實際上,除非只提取結(jié)果集的一部分(無論是設(shè)計的原因,還是因為出現(xiàn)一些問題),否則不需要調(diào)用finish( )。然而,在提取循環(huán)之后, finish( ) 始終是很保險的,我認為調(diào)用并執(zhí)行finish( ),比區(qū)分何時需要,何時不需要更容易一些。
    我們已經(jīng)顯示完了全部成員列表,所以我們可以從服務(wù)器上斷開連接,并且退出:
    $dbh->disconnect();
    exit(0);
    dump_members 示出了許多dbi 程序的大多數(shù)通用概念,而且不必了解更多的知識,就可以著手編寫自己的dbi 程序。例如,要想寫出一些其他表的內(nèi)容,所需要做的只是更改傳遞給prepare( ) 方法的select 語句的文本。而且實際上,如果想了解這種技術(shù)的某些應(yīng)用,可略過這部分,直接跳到7 . 3節(jié)“運行dbi”中討論如何生成歷史同盟一年一度的宴會成員列表程序和league 打印目錄的部分。然而,dbi 提供許多其他有用的功能。下一節(jié)介紹了一些,以便能夠在perl 腳本中看看如何完成比運行一條簡單的select 語句更多的事情。

處理錯誤

    在dump_members 調(diào)用connect( )方法時,應(yīng)該啟用raiseerror 錯誤處理屬性,以便這些錯誤用一條錯誤消息就能自動地終止相應(yīng)的腳本。也可以用其他方式處理這些錯誤。例如,可以自己檢查錯誤而不必使用dbi。
    為了查看如何控制dbi 的錯誤處理行為,我們來仔細查看一下connect( ) 調(diào)用的最終參數(shù)。下面兩個相關(guān)的屬性是raiseerror 和p r i n t e r r o r:
    如果啟用r a i s e e r r o r(設(shè)為非零值),如果在dbi 方法中出現(xiàn)錯誤,則dbi 調(diào)用die( ) 來顯示一條消息并且退出。
    如果啟用p r i n t e r r o r,在出現(xiàn)dbi錯誤時,dbi 會調(diào)用warn( ) 來顯示一條消息,但是相應(yīng)腳本會繼續(xù)執(zhí)行。
    缺省時, raiseerror 是禁用的,而printerror 啟用。在此情況下,如果connect( )調(diào)用失敗,則dbi 顯示一條消息,而且繼續(xù)執(zhí)行。這樣,如果省略connect( ) 的四個參數(shù),則得到缺省的錯誤處理行為,可以如下檢查錯誤:
    $dbh=dbi->connect($dsn,$user_name,$password) or exit (1);
    如果出現(xiàn)錯誤,則connect( ) 返回undef 表示失敗,并且觸發(fā)對exit( ) 的調(diào)用。因為dbi 已經(jīng)顯示了錯誤消息,所以您就不一定要顯示它了。
    如果明確給出該錯誤檢查屬性的缺省值,可如下調(diào)用connect( )。
    $dbh=dbi->connect($dsn,$user_name,$password,{raiseerror=>0,printerror=>1})
    or exit (1);
    這就需要更多的編寫工作,但是即使對不經(jīng)意的讀者,處理錯誤行為也會更為明顯。
    如果想自己檢查錯誤,并顯示自己的消息,應(yīng)該禁用raiseerror 和p r i n t e r r o r:

    變量$dbi::err 和$ dbi : :er r s t r,只用于所顯示的die( ) 調(diào)用中,有助于構(gòu)造錯誤消息。它們含有mysql錯誤代碼和錯誤字符串,非常像c api 函數(shù)中的mysql_errno( ) 和mysql_error( )。
    如果僅僅要dbi 處理錯誤,以便不必自己檢查它們,則啟用r a i s e e r r o r:
    $dbh=dbi->connect ($dsn,$user_name,$password,{raiseerror=>1});
    到目前為止,這是最容易的方法,并且是dump_members 帶來的。如果在腳本退出時,想要執(zhí)行某種類型的清除代碼,啟用raiseerror 可能是不恰當?shù)模M管在這種情況下,可以重新定義$sig{_die_} 句柄,可以做想做的事情。
    避免啟用raiseerror 屬性的另一個原因是dbi 在它的消息中顯示技術(shù)信息,如下:
    disconnect(dbi::db=hash(0x197aae4)invalidates 1active statement.either
    destroy statement handles or call finish on them before disconnecting.
    對于編程者來說,這是好的信息,但對普通用戶可能沒有什么意義。在此情形,最好自己檢查錯誤,以便可以顯示對期望使用這個腳本的人更有意義的消息。或者也可在這里考慮重新定義$sig{_die_} 句柄。這樣可能很有用,因為它允許啟用raiseerror 來使錯誤處理簡單化,而不是用自己的消息替換dbi 給出的缺省錯誤消息。為了提供自己的_die_ 句柄,可在執(zhí)行任何dbi 調(diào)用以前,進行下面的工作:
    $sig{_die_}=sub{die "sorry,an error occurred/n";};
    也可以用普通的風格定義一個子例程,并利用這個子例程的引用來設(shè)置這個句柄值:

    除了在connect( ) 調(diào)用中逐字傳遞錯誤處理屬性之外,還可以利用散列定義它們,并傳遞對這個散列的引用。有人發(fā)現(xiàn)以這種方式準備屬性設(shè)置使腳本更容易閱讀和編輯,但是在功能上這兩種方法是相同的。下面是一個說明如何使用屬性散列的樣例:

    下面的腳本dump_members2 舉例說明了當要自己檢查錯誤并顯示自己的消息時,如何編寫腳本。dump_member2 處理和dump_members 一樣的查詢,但是明確地禁用printerror 和r a i s e e r r o r,然后測試每個dbi 調(diào)用的結(jié)果。如果出現(xiàn)錯誤,在退出以前,腳本調(diào)用了子例程bail_out( ) 顯示消息及$dbi::err 和$dbi::errstr 的內(nèi)容:


    除了bail_out( ) 是退出而不是返回到調(diào)用者以外, bail_out( ) 類似于我們在第6章中為編寫c 程序使用的print_error( ) 函數(shù)。每次想顯示錯誤消息時, bail_out( ) 解除了寫出$dbi::err 和$dbi::errstr 名稱的麻煩。同樣,通過封裝顯示到子例程的錯誤消息,可更改子例程使整個腳本中錯誤消息的格式一致。
    dump_member2 腳本在提取行循環(huán)的后面有一個測試,這是dump_members 所沒有的。因為如果在fetchrow_array( ) 中出現(xiàn)錯誤,dump_members2 不會自動地退出,所以人們判斷循環(huán)是因為結(jié)果集讀取完成而終止(正常終止),還是因為出現(xiàn)錯誤而終止做出確定是很困難的。當然,任何一種方式,循環(huán)都將終止,但是如果出現(xiàn)錯誤,則將刪截腳本的輸出。如果沒有錯誤檢查,運行該腳本的人將無法知道是否有錯!如果自己檢查錯誤,應(yīng)該檢查提取循環(huán)的結(jié)果。

處理不返回結(jié)果集的查詢

    d e l e t e、insert、replace和update等執(zhí)行后不返回行的語句比s e l e c t、describ、explain 和show 等執(zhí)行后返回行的語句的處理相對要容易一些。為處理一條非select 語句,利用數(shù)據(jù)庫句柄,將它傳遞給do( )。do( ) 方法在一個步驟內(nèi)準備和執(zhí)行該查詢。例如,開始輸入一個新的成員, marcis brown,終止日期為2002 年6 月3 日,可以這樣做:

    do( ) 方法返回涉及行的計數(shù),如果出現(xiàn)錯誤,則返回un d e f。因為各種原因,可能出現(xiàn)錯誤(例如,這個查詢可能是畸形的,或可能沒有訪問這個表的權(quán)力)。對于非undef 的返回,注意那些沒有受到影響的行的情況。當這種情況發(fā)生時, do( ) 不返回數(shù)字0;而是返回字符串“0 e 0”(0的perl科學計數(shù)法形式)。“0 e 0”在數(shù)值上等價于0,但是,在條件測試中將其視為真,以便可以將其與早期的undef 區(qū)別。如果do( ) 返回0,則區(qū)分是出現(xiàn)了錯誤( un d e f)還是“沒有受到影響的行”這兩種情況將更困難。使用下面的兩個測試之一可以檢查錯誤:
    if (!defined ($rows)){#error}
    if (!$rows) {#error}
    在數(shù)值環(huán)境中,“0 e 0”與0 等價。下面的代碼將正確地顯示$rows 的任何非undef 值的行數(shù):

    也可以用printf( ) 使用‘% d’格式顯示$row 來強制進行隱含的數(shù)字轉(zhuǎn)換:

    do( ) 方法等價于后跟execute( ) 的prepare( )。前面的insert 語句可以不調(diào)用do( ),如下發(fā)布:


處理返回結(jié)果集的查詢

    本章提供了有關(guān)實現(xiàn)select 查詢中提取行循環(huán)的若干選項的詳細信息(或其他類似于select 的返回行的查詢,如describ e、explain 和s h o w )。還討論了如何獲得結(jié)果中行數(shù)的計數(shù)值,如何處理不需要循環(huán)的結(jié)果集,以及如何一次檢索整個結(jié)果集的全部內(nèi)容等。
    1. 編寫提取行的循環(huán)
    dump_members 腳本利用dbi 方法的標準序列檢索數(shù)據(jù):prepare( ) 使驅(qū)動程序處理查詢,execute( ) 開始執(zhí)行這個查詢, fetchrow_array( ) 提取結(jié)果集中的每一行, finish( ) 釋放與這個查詢相關(guān)的資源。
    prepare( )、execute( ) 和finish( ) 是處理返回行的查詢中非常標準的部分。然而,對于提取的行,fetchrow_array( ) 實際上只是若干方法中的一種(請參閱表7 - 3)。

方法名返回值
fetchrow_array( )行值的數(shù)組
fetchrow_arrayref( )對行值數(shù)組的引用
fetch( )與fetchrow_arrayref( ) 相同
fetchrow_hashref( )對行值的散列引用,列名鍵索引

    下面的例子說示出了怎樣使用每個提取行方法。這些例子在整個結(jié)果集的行中循環(huán),對于每一行,顯示由逗號分隔的列值。在某些情況下,編寫這些顯示代碼還有一些更有效的方法,但是這些例子是以能夠說明訪問單個列值的語法的方式編寫的。
    可如下使用fetchrow_array( ):

    對fetchrow_array( ) 的每個調(diào)用都返回行值數(shù)組,不再有行時,返回一個空數(shù)組。
    選擇將返回值分配給數(shù)組變量,可以在一組標量變量中提取列值。如果想使用比$ a r y [ 0 ]、$ary[1] 等更有意義的變量名,就可以這樣做。假設(shè)要在變量中檢索名稱和電子郵件值,可使用fetchrow_array( ),可以如下選擇并提取行:

    當然,在以這種方式使用一列變量時,必須保證查詢按正確的次序選擇列。dbi 不關(guān)心select 語句指定列的次序,所以正確地分配變量是您的職責。在提取行時,使用一種稱為參數(shù)約束的技術(shù),也可以使列值自動分配給單獨的變量。
    fetchrow_arrayref( ) 類似于fetchrow_array( ),但不返回包含當前行的列值的數(shù)組,而是返回這個數(shù)組的引用,在沒有乘余行時,返回un d e f。如下使用:

    通過數(shù)組引用$ary_ref 訪問數(shù)組元素。這類似于引用指針,所以使用了$ary_ref->[$i] 而不是$ a r y [ $ i ]。要想引用整個數(shù)組,就要使用@{$ary_ref} 結(jié)構(gòu)。
    fetchow_arrayef( ) 不適合在列表中提取變量。例如,下面的循環(huán)不起作用:

    實際上,只要fetchrow_arrayref( ) 提取一行,這個循環(huán)就能正確地運行。但是在沒有更多的行時, fetchrow_arrayref( ) 返回un d e f,并且@{undef} 不合法(它有些像在c 程序中試圖廢棄一個null 指針)。
    提取行的第三個方法fetchrow_hashref( ),如下使用:

    對fetchrow_hashref( ) 的每個調(diào)用都返回一個按列名索引的行值散列的引用,在沒有更多的行時,返回un d e f。在此情況下,列值不按特定的次序出現(xiàn); perl 散列的成員是無序的。然而,散列元素是按列名索引的,所以$hashref 提供了一個單獨的變量,可通過它按名稱訪問任何列值。這使得能按任意需要的次序來提取值(或者它們中的任何子集),而且不必知道select 查詢檢索的列的次序。例如,如果想訪問名稱和電子郵件域,可以如下進行:

    如果希望將一行值傳遞給某個函數(shù)而又不需要這個函數(shù)知道select 語句中指定列的次序時,fetchrow_hashref( ) 是非常有用的。既然如此,可以調(diào)用fetchrow_hashref( ) 來檢索行,并且編寫一個使用列名訪問來自行散列值的函數(shù)。
    如果使用fetchrow_hashref( ),請記住下列警告:
    如果性能很重要,則fetchrow_hashref( ) 并不是最好的選擇,因為它沒有fetchrow_array( ) 或fetchrow_arrayref( ) 的效率高。
    作為散列鍵值使用的列名具有與select 語句中寫出時相同的字符。在mysql中,列名不區(qū)分大小寫,所以此查詢也是這樣,不管以大寫字母還是小寫字母給出列名,查詢結(jié)果都是一樣的。但是perl 散列索引名是區(qū)分大小寫的,這可能會帶來一些問題。為了避免潛在的大小寫不匹配問題,可通過傳遞name_lc 或name_uc 屬性,告知fetchrow_hashref( ) 強迫列名為大寫或小寫:

    散列對每個唯一的列名含有一個元素。如果正在執(zhí)行從多個具有重疊名稱的表中返回列的連接,則不能訪問所有的列值。例如,如果發(fā)布下面的查詢, fetchrow_hashref( )將返回只有一個元素的散列:
    select a.name from a,b where a.name=b.name
    2. 確定查詢返回的行數(shù)
    如何知道select 或類似于select 的查詢返回的行數(shù)?一種方法是,當提取它們時,計算這些行的數(shù)量。實際上,這是知道select 查詢返回多少行的唯一方便的方法。使用mysql驅(qū)動程序,可以在調(diào)用execute( ) 后利用語句句柄調(diào)用rows( ) 方法,但是這對其他數(shù)據(jù)庫引擎并不方便。而且即使就mysql來說,如果已經(jīng)設(shè)置了mysql_use_result 屬性,rows( ) 也不能返回正確的結(jié)果,直到提取了所有行(有關(guān)的詳細信息,請參閱附錄g)。所以只能如提取行一樣對它們進行計數(shù)。
    3. 提取單行的結(jié)果
    如果結(jié)果集只含單個行,則不需要運行循環(huán)來獲得結(jié)果。假設(shè)要編寫得出歷史同盟成員當前數(shù)量的腳本count _ member s。完成查詢的代碼如下所示:

    select 語句只返回一行,所以不需要循環(huán);我們只調(diào)用fetchrow_array( ) 一次。另外,因為我們只選擇一列,所以甚至不需要將返回值分配給數(shù)組。當在標量環(huán)境中(單個值而不是所期望的一列)調(diào)用fetchrow_array( ) 時,它返回這個行的第一列,如果沒有更多的有效行,則返回un d e f。
    另一種期望最多有一個記錄的查詢是一個含有l(wèi)imit 1來約束返回的行數(shù)的查詢。其一般的用法是返回特定列含有最大或最小值的行。例如,下面的查詢給出最近出生的總統(tǒng)姓名和出生日期:

    必須無提取循環(huán)的其他類型的查詢利用max( ) 或min( ) 來選擇單個值。但是在所有這些情況下,獲得單個行結(jié)果的一種更容易的方法就是使用數(shù)據(jù)庫句柄方法selectrow_array( ),它結(jié)合了prepare( )、execute( ) 并在單個調(diào)用中提取行。它返回一個數(shù)組(而不是一個引用),如果出現(xiàn)錯誤,則返回一個空數(shù)組。前一例子可利用selectrow_array( ) 編寫如下:


    4. 處理完整的結(jié)果集
    在使用提取循環(huán)時, dbi 不提供在結(jié)果集中隨意查找的方法,或以任何次序而不是以循環(huán)返回的次序來處理行。同樣,提取行以后,如果沒有保存,前一行會丟失。這種做法并不一定合適以下情況:
    以非連續(xù)的次序處理行。考慮一種情況,想以歷史同盟的president 表中列出的美國總統(tǒng)為主體,進行一些測驗。如果希望每次測驗時都以不同的次序提出問題,則可以從president 表中選擇所有行。然后,可能以任意的次序提取行來改變與所問問題有關(guān)的總統(tǒng)的次序。要想任意地提取一行,就必須同時訪問所有的行。
    只使用返回行的子集,對其進行隨機選擇。例如,當問及總統(tǒng)出生地時,要想出現(xiàn)多個選擇的問題,則可以隨便地提取一行來選擇總統(tǒng)(正確的答案),然后再從取來干擾的選擇中提取若干其他行。
    即使確實以連續(xù)的次序去處理,也想緊緊抓住整個結(jié)果集。如果想經(jīng)過這些行進行多個傳遞,這可能是必需的。例如,在統(tǒng)計計算中,可能先瀏覽一遍結(jié)果集,來估計數(shù)據(jù)的一些通用數(shù)字屬性,然后再次檢查這些行,來實現(xiàn)更加明確的分析。
    可以用幾個不同的方式作為一個整體訪問結(jié)果集。可以完成這個常見的提取循環(huán),并在提取它時保存每一行,可以使用一次返回整個結(jié)果集的方法。無論哪種方法都以在結(jié)果集中包括一行一行的矩陣作為結(jié)束,和選擇的列一樣多。可以以任何次序任意多次地處理矩陣的
元素。下面的討論說明這兩種方法。
    使用提取循環(huán)來捕獲結(jié)果集的一種方法是使用fetchrow_array( ) 并保存對這些行引用的數(shù)組。除了保存所有的行,然后顯示矩陣舉例說明了如何確定矩陣中的行數(shù)和列數(shù),及如何訪問矩陣的個別成員以外,下面的代碼和dump_members 中提取和顯示的循環(huán)作用是一樣的。


    在確定矩陣的維數(shù)時,必須首先確定行數(shù),因為無論這個矩陣是否為空,都可能計算列數(shù)。如果$rows 為0,則這個矩陣為空,并且$cols 也為0。否則,列數(shù)可能作為行數(shù)組中的元素數(shù)量來計算,用語法@{$matrix[$i]} 來整體訪問行$ i。
    在前述的樣例中, 我們提取每一行, 然后保存對它的引用。可以設(shè)想調(diào)用fetchrow_arrayref( ) 而不是直接地檢索行引用可能更有效率:

    它不能正常工作,因為fetchrow_arrayref( ) 重新使用了引用指向的數(shù)組。結(jié)果矩陣是一個引用的數(shù)組,數(shù)組中的每個元素都指向相同行—最后檢索的行。因此,如果想一次提取一行,則要使用fetchrow_array( ) 而不是fetchrow_arrayref( )。
    另一個選擇是使用提取循環(huán),可以使用返回整個結(jié)果集的dbi 方法中的一個。例如,fetchall_arrayref( ) 返回對引用數(shù)組的引用,數(shù)組的每個元素都指向結(jié)果集中某行。這非常簡單,但很有效,這個返回值是對矩陣的引用。要想使用fetchall_arrayref( ),則調(diào)用prepare( )和execute( ),然后如下檢索結(jié)果:

    如果結(jié)果集為空,則fetchall_arrayref( ) 返回一個對空數(shù)組的引用。如果出現(xiàn)錯誤,則結(jié)果為un d e f,所以如果沒有啟用r a i s e e r r o r,則在開始使用它以前,要確保檢查返回值。
    行數(shù)和列數(shù)由矩陣是否為空來確定。如果想作為一個數(shù)組訪問這個矩陣的整個行$ i,應(yīng)該使用語法@ { $ m a t r i x _ r e f - > [ $ i ] }。
    使用fetchall_arrayref( ) 來檢索結(jié)果集當然比編寫一個提取行的循環(huán)要更簡單一些,盡管訪問數(shù)組元素的語法是一個小技巧。一個與fetchall_arrayref( ) 方法相類似,但卻做了更多工作的方法是selectall_arrayref( )。這個方法為您完成了整個prepare( )、execute( )、提取循環(huán)、finish( ) 序列。為了使用selectall_arrayref( ),應(yīng)該利用數(shù)據(jù)庫句柄直接將查詢傳遞給它:

    5. 檢查null 值
    從數(shù)據(jù)庫中檢索數(shù)據(jù)時,可能需要區(qū)分值為null 、為0 或者為空字符串的列。因為dbi 返回null 列值作為un d e f,所以這種區(qū)分是容易的。然而,必須確保使用正確的測試。如果試用下面的代碼段,則它所有三次顯示都為“ f a l s e !”:

    而且,對于這兩個測試,這段代碼都顯示“ f a l s e !”:

    下面這段代碼效果相同:

    要想?yún)^(qū)分null 列值和非null 列值,則使用defined( )。知道了沒有出現(xiàn)null 值之后,使用適當?shù)臏y試可以在其他類型值之間加以區(qū)分。例如:

    以適當?shù)拇涡蛲瓿蛇@些測試是很重要的,因為如果$col_val 為空字符串,則第二個和第三個比較就都為真。如果顛倒比較的次序,則會錯誤地將空字符串標識為0。

引用問題

    迄今為止,我們已經(jīng)利用引用字符串以最基本的方式構(gòu)造了查詢。在引用的字符串含有引用值時,會在perl 詞匯一級產(chǎn)生問題。在插入或者選擇含有引號、反斜杠或二進制數(shù)據(jù)的值時,在sql 中也可能出問題。如果指定一個查詢作為perl 引用的字符串,則必須避免在查詢字符串本身中出現(xiàn)引用字符:

    perl 和mysql都允許用單引號或雙引號引用字符串,所以混合使用引用字符有時可以避免這種無法引用引用字符自身的情況:

    然而,在perl 中,這兩種類型的引號并不等價。只有在雙引號內(nèi)部才解釋為變量引用。因此,當想通過在查詢字符串中嵌入變量引用來構(gòu)造查詢時,單引號并不是非常有用的。例如,如果$var 的值為14,則下面的兩個字符串并不等價:

    兩個字符串的解釋如下所示;顯然,第一個字符串與希望傳遞給mysql服務(wù)器的內(nèi)容更為相像:

    用雙引號來引用字符串的另一個選擇是使用qq{} 結(jié)構(gòu),它告訴perl 在‘q q {’和‘}’之間的每個字符都要看作為雙引號括起的字符串(兩個q 表示“雙引號”)。例如,下列兩行是等價的:

    使用qq{} 時,構(gòu)造查詢不用過多考慮引號的問題,因為可以在這個查詢字符串內(nèi)自由地使用引號(單引號或雙引號),而不用避開它們。此外,還解釋了變量引用。qq{} 的這兩種特性可用下面的insert 語句來說明:

    不一定使用‘ {’和‘ }’作為qq 的分隔符。其他格式,如qq( ) 和q q / /,也可以使用,只要封閉的分隔符不出現(xiàn)在字符串內(nèi)即可。我喜歡用q q { },因為‘ {’不像‘)’或‘/’會出現(xiàn)在查詢的文本內(nèi),并且在查詢字符串的結(jié)尾也可能有問題。例如,‘)’出現(xiàn)在所顯示的insert 語句的內(nèi)部,所以qq( ) 對于引用查詢字符串來說不是一個有用的結(jié)構(gòu)。
    qq{} 結(jié)構(gòu)能跨行,如果想讓查詢字符串在perl 代碼中醒目,這很有用:

    如果希望將查詢格式分為多個行,從而使它的可讀性更強,這樣也很有用。例如,dump_members 腳本中的select 語句如下:

    用q q { }編寫如下:


    雙引號字符串也可以跨行。但是,對于編寫多行的字符串,我更喜歡用q q { }。我發(fā)現(xiàn)當在一行中看到不匹配的引號時,我就自然地去想,“這會是語法錯誤嗎?”,然后,我就會浪費時間去尋找相匹配的引號。
    qq{} 結(jié)構(gòu)在perl 詞匯級注意了引用的問題,因此可將引號容易地放到字符串內(nèi),而不會使perl 搞混它們。然而,還必須考慮sql 級的語法。考慮向member 表中插入一條記錄:

    do( ) 發(fā)送給mysql的字符串如下所示:

    這是不合法的sql 語句,因為在單引號字符串內(nèi)出現(xiàn)了單引號。在第6章中,我們遇到過類似的引用問題。在那里,我們使用mysql_escape_string( ) 來處理這個問題。dbi 提供了一個類似的機制—在一條語句中,對想按字面使用的每個引用值,都調(diào)用quote( ) 方法,并使用它的返回值。
    前面的例子可編寫如下:

    現(xiàn)在,do( ) 發(fā)送給mysql的字符串如下所示,具有出現(xiàn)在引用字符串內(nèi)的可能對服務(wù)器轉(zhuǎn)義的引號:

    請注意,在查詢字符串中引用$last 和$first 時,不要增加括起來的引號; quote( ) 方法支持它們。如果增加了引號,則查詢將出現(xiàn)過多的引號,如下面的例子所示:

    這些語句產(chǎn)生下面的輸出:


占位符和參數(shù)約束

    在前面各節(jié)中,我們通過把要插入或選擇的值作為選擇標準,直接放在查詢字符串中構(gòu)造了查詢。不一定非要這樣做。dbi允許在查詢字符串內(nèi)部放置一些稱為占位符的特殊標記符,然后,在執(zhí)行該查詢時,將這些值代替那些標識符來使用。這樣做的主要原因是提高性能,特別是在循環(huán)中反復執(zhí)行某個查詢的時候。
    為了說明占位符如何工作,舉例說明。假設(shè)學校新學期剛開始,打算清理學分薄的student 表,然后利用包含在文件中的一列學生姓名將其初始化,使其包含新學生。不用占位符,可以如下這樣刪除現(xiàn)有表的內(nèi)容,并裝入新的姓名:

    這樣做效率很低,因為insert 查詢的基本格式每次都是相同的,并且在整個循環(huán)中,do( ) 每次都調(diào)用prepare( ) 和execute( )。在進入這個循環(huán)以前,只調(diào)用一次prepare( ) 來設(shè)置insert 語句,并且在這個循環(huán)內(nèi)部只調(diào)用execute( ),這樣做效率更高一些。只調(diào)用一次prepare( ),可避免其他多次調(diào)用。dbi 允許我們這樣做:

    請注意這個insert 查詢中的‘ ?’就是一個占位符。調(diào)用execute( ) 時,將查詢發(fā)送給服務(wù)器,傳遞這個值來代替占位符。一般來說,如果發(fā)現(xiàn)在循環(huán)內(nèi)部調(diào)用了do( ),應(yīng)該在循環(huán)前調(diào)用prepare( ),并在這個循環(huán)內(nèi)部調(diào)用execute( ) 更好一些。
    有關(guān)占位符的一些注意事項:
    在查詢字符串內(nèi),不要在引號中封裝占位符字符。如果這樣做,不能識別為占位符。
    不要使用quote( ) 方法來指定占位符的值,否則將在插入的值中得到額外的引號。
    在查詢字符串中可以有一個以上的占位符,但是要確保占位符的標記符與傳遞給execute( ) 的值一樣多。
    每個占位符都必須指定一個單獨的值,而不是一列值。例如,不能運行這樣的語句:

    為了將null 指定為占位符,應(yīng)該使用un d e f。
    不要對關(guān)鍵字使用占位符。這樣會出問題,因為占位符的值是由quote( ) 自動處理的。
    關(guān)鍵字將被放在引號括起來的查詢中,因此,這個查詢會由于語法錯誤而失敗。
    除了在循環(huán)中提高效率以外,對于某些數(shù)據(jù)庫引擎,可以從占位符的使用中獲得其他的性能好處。某些引擎高速緩存了準備好的查詢,以及為有效地運行這個查詢所生成的計劃。也就是說,如果以后這個服務(wù)器收到同樣的查詢,則它可以再次使用相應(yīng)的計劃而不用生成。查詢高速緩存特別有助于復雜的select 語句,因為可能需要花費時間生成較好的執(zhí)行計劃。占位符提供了一個在高速緩存中尋找查詢的好機會,因為它們使查詢比直接在查詢字符串中嵌入指定的列值來構(gòu)造查詢更通用。對于mysql,在這種方式下,占位符并不提高性能,因為沒有高速緩存查詢。然而,可能仍想使用占位符編寫自己的查詢;如果偶然將dbi 腳本傳遞給支持查詢高速緩存的引擎,則這個腳本比沒有占位符時運行效率更高。
    在查詢運行時,允許在查詢字符串中用占位符代替這些值。換句話說,可以參數(shù)化這個查詢的“輸入”。在提取行而不必將值賦給變量時,dbi 也提供一個稱為參數(shù)約束的輸出操作,允許通過檢索自動進入這些變量的列值使“輸出”參數(shù)化。
    假設(shè)有一個查詢,檢索member 表中的成員姓名。可以告訴dbi 將選定列的值賦給perl變量。在提取行時,變量利用相應(yīng)的列值自動進行更新。下面是一個例子,說明如何將這些列約束到變量上,然后在提取循環(huán)中訪問它們:

    bind_col( ) 的每個調(diào)用都應(yīng)該指定一個列號和一個希望與該列相聯(lián)的變量的引用。列號從1開始。bind_col( ) 應(yīng)該在execute( ) 之后調(diào)用。
    還有一種選擇,就是單獨調(diào)用bind_col( ),可以在bind_columns( ) 的單個調(diào)用中傳遞全部變量引用:

指定連接參數(shù)

    建立服務(wù)器的連接的最直接的方法為,調(diào)用connect( ) 方法時指定所有連接參數(shù):

    如果遺漏連接參數(shù),則dbi 做下面的事情:
    如果未定義數(shù)據(jù)源或未定義空字符串,則使用dbi_dsn 環(huán)境變量。如果未定義用戶名和口令,則使用dbi_user 和dbi _ pass 環(huán)境變量(但如果它們?yōu)榭兆址畡t不使用)。在windows 下,如果未定義用戶名,則使用user 變量。
    如果遺漏了主機名,其缺省值為localhost。
    如果將用戶名指定為undef 或空字符串,則其缺省為unix 的登錄名稱。在windows 下,用戶名缺省為odbc。
    如果將口令指定為undef 或空字符串,則不傳送口令。
    通過將某些選項添加到字符串的初始部分,每個都在分號前面,可以在數(shù)據(jù)源中指定這些選項。例如,可以使用mysql_read_default_file 選項來指定一個選項文件的路徑名:

    當執(zhí)行這個腳本時,它將從這個文件中讀取連接參數(shù)。假設(shè)/ u/ paul /.my.cnf 含有下面的內(nèi)容:

    然后connect( ) 調(diào)用試圖連接到p i t - v i per-snake-net 上的mysql服務(wù)器,并且用口令secret 及用戶名paul 連接。如果想允許具有正確地設(shè)置選項文件的任何人使用您的腳本,則像這樣指定數(shù)據(jù)源:

    $env{home} 含有用戶運行這個腳本的主目錄的路徑名,所以這個腳本使用的主機名、用戶名和口令將會從每個用戶自己的選項文件中抽取出來。以這種方式編寫腳本,不必在這個腳本中逐字地嵌入連接參數(shù)。
    還可以使用mysql_read_default_group 選項,來指定一個選項文件組。這自動地導致讀取用戶的.my.cnf 文件,并且除了[client] 組以外,還允許讀取一個指定的選項組。例如,如果在dbi 腳本中具有指定的選項,則可以將它們列在[dbi] 組中,然后以如下方式使用數(shù)據(jù)源值:

    mysql_read_default_file 和mysql_read_default_group 需要mysql3.22.10 或更新的版本,以及dbd::mysql1.21.06 或更新的版本。有關(guān)指定的數(shù)據(jù)源字符串的選項的詳細信息,請參閱附錄g。有關(guān)mysql選項文件格式的詳細信息,請參閱附錄e。
    使用選項文件并不防礙在connect( ) 調(diào)用中指定連接參數(shù)(例如,如果想這個腳本作為特殊的用戶來連接)。在connect( ) 調(diào)用中指定的任何明確的主機名、用戶名和口令值都將覆蓋在選項文件中找到的連接參數(shù)。例如,想要腳本從命令行中分析- - host、--user 和--password 選項,并使用那些值,如果給定,則優(yōu)先于在選項文件中發(fā)現(xiàn)的任何內(nèi)容。這是有用的,因為它是標準的mysql客戶機操作的方式。dbi 腳本將因此符合它的行為。
    對于在本章中我們開發(fā)的保留在命令行中的腳本,我將使用一些標準的連接設(shè)置代碼及卸載代碼。我只在這里說明它一次,以便我們可以將精力集中在每個腳本的主體上,我們編寫如下代碼:


    這個代碼初始化dbi,在命令行中查找連接參數(shù),然后使用命令行中的或者在用戶運行這個腳本的- /.my.cnf 文件中所找到的參數(shù),連接到mysql服務(wù)器。如果在主目錄中設(shè)置.my.cnf 文件,則當運行這個腳本時,不一定要輸入任何連接參數(shù)(請記住,設(shè)置這種方式,以便沒有其他人讀取這個文件。有關(guān)的指導請參閱附錄e)。
    我們腳本的最后部分也類似于從腳本到腳本;它簡單地終止這個連接并退出:
    $dhb->disconnect();
    exit (0);
    當我們讀到web 程序設(shè)計的部分,即7 . 4節(jié)“在web 應(yīng)用程序中使用dbi”時,將修改一些這個連接設(shè)置代碼,但是基本的思想是類似的。

調(diào)試

    當想調(diào)試有故障的dbi 腳本時,通常使用兩項技術(shù),即單獨使用一個或一前一后地配合使用。首先,在腳本的整個過程中編寫顯示語句。它允許將自己調(diào)試的輸出設(shè)計為想要的方式,但必須手工地增加語句。其次,可以使用dbi 的內(nèi)建跟蹤能力。這更加通用,但也更加
系統(tǒng),而且它在打開以后,則會自動地出現(xiàn)。dbi 跟蹤也說明一些除此以外就無法獲得的有關(guān)驅(qū)動程序的操作信息。
    1. 使用顯示語句調(diào)試
    在mysql郵件清單中,常見問題之一是:“有一個查詢,當我在mysql中執(zhí)行它時運行得很好,但是它不能在我的dbi 腳本中工作,怎么回事?”尋找發(fā)布不同查詢的dbi 腳本和這個發(fā)問者所期望的一樣是很平常的。如果在執(zhí)行它之前顯示查詢,則可能會驚異地看到真
正發(fā)送到這個服務(wù)器上的內(nèi)容。假設(shè)將一個查詢鍵入到mysql中(沒有終止的分號)

    然后,在dbi 腳本中試著做相同的事情:

    盡管它是同樣的查詢,但它不能工作。不是嗎?試著顯示:
    print "$query/n"
    結(jié)果如下:
    insert member (last_name,first_name,expiration)
    values(brown,warcia,2002-6-3)
    從這個輸出中,可以看到是您忘記了values( ) 列表中這些列值前后的引號。指定查詢的正確方法如下:

    或者,可以使用占位符指定查詢,并傳遞這些值,直接插入到do( ) 方法中:

    不幸的是,當做這些的時候,使用顯示語句不能看到完整查詢的樣子,因為直到調(diào)用d o ( )才能估計占位符的值。當使用占位符時,跟蹤可能對調(diào)試方法更有幫助。
    2. 使用跟蹤調(diào)試
    當試圖查出腳本不能正確工作的原因時,可以告知dbi 來生成跟蹤(調(diào)試)信息。跟蹤級別范圍從0(關(guān)閉)到9(最多信息)。一般來說,跟蹤級別1和2 是最有用的。級別2 跟蹤說明正在執(zhí)行的查詢文本(包括占位符替換的結(jié)果)、調(diào)用quote( ) 的結(jié)果等等。這可能對捕獲問題有極大的幫助。
    使用trace( ) 方法,可以從獨立的腳本內(nèi)部控制跟蹤,或者可以設(shè)置dbi_trace 環(huán)境變量來影響所運行的所有dbi 腳本的跟蹤。
    要想使用trace( ) 調(diào)用,則傳遞一個跟蹤級別參數(shù),并可以有選擇地再傳遞一個文件名。如果沒有指定文件名,則所有的跟蹤輸出到stderr 中;否則,它就轉(zhuǎn)到這個命名的文件中。一些樣例如下:

    當調(diào)用dbi->trace( ) 時,跟蹤所有的dbi 操作。一個更精細的方法是,可以用獨立的處理級別啟用跟蹤。當沒想好腳本中問題的位置,并對在那點出現(xiàn)的每件事的跟蹤輸出不想插手時,這是有幫助的。例如,如果特定的select 查詢有問題,則可以跟蹤與這個查詢相關(guān)的語句句柄:

    如果對任何trace( ) 調(diào)用指定一個文件名參數(shù),則無論對dbi 作為整體還是單獨的句柄,所有的跟蹤輸出都要到那個文件中。
    要想對運行的所有dbi 腳本全部都打開跟蹤,則從命令解釋程序中設(shè)置dbi_trace 環(huán)境變量。它的語法取決于使用的命令解釋程序:

    value 的模式和所有命令解釋程序的模式一樣:數(shù)字n表示在級別n打開跟蹤到s t d e r r中;文件名打開級別2 跟蹤到這個命名的文件,或n=file_name 打開級別n跟蹤到這個命名的文件中。下面的樣例使用了csh 語法:

    如果打開跟蹤到命令解釋程序中的文件,則確保一旦解決了這個問題,就將它關(guān)閉。將調(diào)試輸出增加到這個跟蹤文件中,而不用重寫它,所以如果不小心,則這個文件可能變得非常大。極其不好的想法是在命令解釋程序的啟動文件(如.cshrc、.lonin 或. p r o f i l e)中定義dbi _ t r a c e!在unix 下,可以使用下面兩個命令( csh 語法)之一關(guān)閉跟蹤:
    % setenv dbi_trace 0
    % unsetenv dbi_trace
    對于s h、ksh 或b a s h,這樣做:
    $ dbi_trace=0
    $export dbi_trace
    在windows 操作系統(tǒng)中,可以使用下面兩個命令之一關(guān)閉跟蹤:
    c:/>unset dbi_trace
    c:/>set dbi_trace=0

使用結(jié)果集元數(shù)據(jù)

    可以使用dbi 來獲得訪問結(jié)果集元數(shù)據(jù)——也就是有關(guān)由查詢選擇行的描述信息。訪問與結(jié)果集生成的查詢所相關(guān)的語句句柄的屬性來獲得這個信息。提供這些屬性中有一些是作為可用于橫跨所有數(shù)據(jù)庫驅(qū)動程序的標準dbi 屬性(如n u m _ o f _ f i e l d s,結(jié)果集中列的數(shù)量)。另外一些是mysql特定的,由dbd::mysql所提供的dbi 的mysql驅(qū)動程序。這些屬性,如mysql_max_length 告知了每列值的最大寬度,不能用于其他數(shù)據(jù)庫引擎。要想使用任何mysql特定的屬性,都必須冒著使腳本不可移植到其他數(shù)據(jù)庫的危險。另一方面,它們可以使它更容易地獲得想要的信息。
    必須在適當時候請求元數(shù)據(jù)。一般來說,直到調(diào)用prepare( ) 和execute( ) 之后,結(jié)果集屬性才能用于select 語句。除此之外,在調(diào)用finish( ) 之后,屬性可能變?yōu)闊o效。
    讓我們來看看如何使用mysql的一個元數(shù)據(jù)屬性mysql_ m a x _ l e n g t h,與保留查詢列名的dbi 級別的name 屬性一起使用。我們可以將這些屬性提供的信息合并起來,編寫一個腳本b o x _ o ut,它以交互模式運行mysql客戶機程序時獲得的相同邊框風格,從select 查詢產(chǎn)生輸出。box_out 的主體如下(可以用任何其他的語句替換select 語句;編寫輸出的例程獨立于特定的查詢):


    用execute( ) 將這個查詢初始化之后,我們獲得了所需的元數(shù)據(jù)。$sth->{name} 和$ s t h ->{mysql_max_length} 給出了列名和每列值的最大寬度。為了在這個查詢中為列命名,每個屬性值都引用了一個數(shù)組,這個數(shù)組含有結(jié)果集每列中的一個值。
    剩余的計算非常類似于在第6章中開發(fā)的客戶機程序5中所使用的那些內(nèi)容。例如,為避免偏離輸出,如果列的名比該列中任何數(shù)據(jù)值都寬,則我們要向上調(diào)整列的寬度值。
    輸出函數(shù)print_dashes( ) 和print_row( ) 代碼編寫如下,它們也類似于客戶機程序5中相應(yīng)的代碼:

    box_out 的輸出如下:

    我們的下一個腳本使用了列元數(shù)據(jù)來產(chǎn)生不同格式的輸出。這個腳本s h o w _ member,允許快速瀏覽歷史同盟成員項目,而不用輸入任何查詢。給出成員的姓,它就這樣顯示所選擇的項目:


    使用成員資格號碼,或者使用與若干姓相匹配的模式也可以調(diào)用s h o w _ member s。下面的命令說明成員號碼為2 3的項目,和以字母“c”開始的姓的成員項:

    show_member 腳本的主體如下所示。它使用了name 屬性來確定輸出的每行所使用的標號和num_of_fields 屬性,找出這個結(jié)果集含有的列數(shù):


    無論區(qū)域是什么, show_member 的目的都是說明一個項目的全部內(nèi)容。通過使用select * 來檢索所有的列和name 屬性來看看它們是什么,即使從member 表中增加或刪除列,這個腳本也會工作而不用做修改。
    如果不檢索任何行就想知道一個表含有哪些列,則可以發(fā)布下面這條查詢:
    select * from tbl_name where 1=0
    以正常方式調(diào)用prepare( ) 和execute( ) 之后,可以從@{$sth->{name}} 中得到列名。然而,請注意,盡管使用“空”查詢的這個小竅門可以在mysql下運行,但是它不可移植,而且并不是對所有的數(shù)據(jù)庫引擎都可以工作的。
    有關(guān)dbi 和dbd::mysql所提供屬性的詳細信息,請參見附錄g。它完全可以使您確定是想通過避免mysql特定的屬性而為可移植性花費努力,還是在可移植性的開銷方面利用它們。

上一篇:運行perl DBI

下一篇:Perl 腳本的特點

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 泽州县| 长治县| 美姑县| 马龙县| 安国市| 苍溪县| 濮阳市| 锦州市| 中西区| 连平县| 颍上县| 阿克苏市| 绿春县| 翁牛特旗| 康马县| 朝阳市| 武威市| 六枝特区| 徐闻县| 梧州市| 建始县| 阿拉善右旗| 临朐县| 盘锦市| 吉安县| 乌拉特后旗| 社会| 从江县| 青浦区| 勐海县| 弥勒县| 革吉县| 太白县| 上思县| 略阳县| 碌曲县| 团风县| 和龙市| 关岭| 涟源市| 莫力|