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

首頁(yè) > 開(kāi)發(fā) > 綜合 > 正文

使用 SQL pass-through

2024-07-21 02:10:25
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

第四節(jié) 使用 sql pass-through

與使用視圖相比的另一個(gè)選擇是單獨(dú)地使用 sql pass-through。這就意味著你發(fā)送 sql 語(yǔ)句到后端并明白地告訴它要做什么。如果你想添加一條記錄可發(fā)送一個(gè) insert。要保存記錄可發(fā)送一個(gè) update。顯然這比使用視圖要做更多的工作。但是它允許你完全地控制發(fā)生什么和什么時(shí)候?qū)?huì)發(fā)生。

載入表單

表單 membexec.scx 是一個(gè)與上面的表單相似的表單,它只是使用 sql pass-through 而不是視圖。以下是表單的 load 方法代碼。
open database library
thisform.nhandle = sqlconnect('cnlibrary')
if thisform.nhandle < 0
  thisform.showerror
  thisform.lconnected = .f.
  else
  lcsql = "select member.member_no, lastname, firstname, " + ;
          "  middleinitial, street, city, state, zip, " + ;
          "  phone_no, expr_date, birth_date = null, " + ;
          "  adult_member_no = null " + ;
          "from member, adult " + ;
          "where member.member_no = -99 "
  if sqlexec(thisform.nhandle, lcsql, "c_member") < 0
    thisform.showerror
    thisform.lconnected = .f.
  endif
    = cursorsetprop("buffering", db_bufoptrecord, 'c_member')
endif

在表單載入時(shí),sqlconnect() 用于使用保存在 visual foxpro library 數(shù)據(jù)庫(kù)中的 cnlibrary 連接來(lái)建立到 sql server 的連接。如果 sqlconnect() 失敗,除了退出和回家外,你沒(méi)有別的事可做。
如果連接正常,以發(fā)送到 sql server 一個(gè)搜索成員號(hào) -99 的 select 語(yǔ)句來(lái)創(chuàng)建了一個(gè)空的游標(biāo)。即使沒(méi)有返回記錄,visual foxpro 也會(huì)創(chuàng)建該游標(biāo)。然后設(shè)置該游標(biāo)的開(kāi)放式緩存。這樣做的原因是可以在該表單上使用緩存了的游標(biāo)。這反映了一些視圖的易用性。

定位一個(gè)成員

當(dāng)在表單上使用視圖時(shí),定位一個(gè)成員只需簡(jiǎn)單地為視圖參數(shù)賦值并重新獲取值(requery)。當(dāng)使用 sql pass-through 時(shí)要稍稍復(fù)雜一些。要獲取成員信息,要?jiǎng)?chuàng)建一條 select 語(yǔ)句并發(fā)送到服務(wù)器。在下面的代碼中你可以看到這是一個(gè) union select。如果成員存在于 member 表和 adult 或 juvenile 表中。
lcsql = "select member.member_no, lastname, firstname, " + ;
        "  middleinitial, street, city, state, zip, " + ;
        "  phone_no, expr_date, birth_date = null, " + ;
        "  adult_member_no = null " + ;
        "from member, adult " + ;
        "where member.member_no = adult.member_no " + ;
        "  and member.member_no = " + ;
        "    alltrim(thisform.txtmemberid.value) + " " + ;
        "union " + ;
        "select member.member_no, lastname, firstname, " + ;
        "  middleinitial, street, city, state, zip, " + ;
        "  phone_no, expr_date, birth_date, " + ;
        "  adult_member_no " + ;
        "from member, adult, juvenile " + ;
        "where member.member_no = juvenile.member_no " + ;
        "  and adult.member_no = " + ;
        "    juvenile.adult_member_no " + ;
        "  and member.member_no = " + ;
        "    alltrim(thisform.txtmemberid.value)
  
if sqlexec(thisform.nhandle, lcsql, "c_member") < 0
<略去的代碼>

如果 c_member 游標(biāo)為空則沒(méi)有輸入的 id 成員存在。否則所有成員信息都在游標(biāo)中。游標(biāo)設(shè)置了行緩存,且表單控件用游標(biāo)中的成員信息填充。
union 允許你發(fā)送一個(gè) select 到服務(wù)器并獲取該成員的所有信息。在早期的示例中,需要對(duì)兩個(gè)或三個(gè)視圖進(jìn)行重查詢(requery) 而不是象這里一樣只需要一個(gè) sqlexec()。注意,如果你使用視圖設(shè)計(jì)器,你不能創(chuàng)建一個(gè)帶 union 的遠(yuǎn)程視圖。但你可以用 create sql view 命令來(lái)創(chuàng)建這種視圖。

添加一個(gè)成人

當(dāng)用戶按下 add 按鈕時(shí),一條空白的記錄添加到 c_member 游標(biāo)中。這與早期的視圖示例不同。
select c_member
= tablerevert()
append blank

僅出于可讀性的原因,添加新成員的代碼位于表單的 addmember 方法中。因?yàn)樘砑右粋€(gè)成員包括向兩個(gè)表添加記錄,因此開(kāi)始一個(gè)事務(wù)處理。只在使用視圖的表單中,使用 sqlsetprop() 函數(shù)來(lái)開(kāi)始事務(wù)處理。
= sqlsetprop(thisform.nhandle, "transactions", 2)

當(dāng)用視圖來(lái)訪問(wèn)遠(yuǎn)程數(shù)據(jù)時(shí),你可以依靠 visual foxpro 來(lái)為你處理大多數(shù)的后臺(tái)工作。例如,在早期的表單中,你看到了要發(fā)送一個(gè) insert 或 update 到服務(wù)器,你只需要發(fā)布一條 tableupdate()。insert 或 update 的語(yǔ)法都由 visual foxpro 為你做了。
這里的表單合作 sqlexec() 函數(shù)來(lái)發(fā)送 sql 語(yǔ)句到服務(wù)器。這就意味著你必須自己構(gòu)造 sql 語(yǔ)句。在開(kāi)始事務(wù)處理后,構(gòu)造一個(gè) insert 語(yǔ)句來(lái)添加新記錄到 member 表中。
* 添加新成員到 member 表
lcsql = "insert member (lastname, firstname, " + ;
                       "middleinitial, photograph) " + ;
        "values ('" + ;
             alltrim(thisform.txtfirstname.value) + ;
             "', '" + ;
             alltrim(thisform.txtlastname.value) + ;
             "', '" + ;
             alltrim(thisform.txtmiddleinitial.value) + ;
             "', " + "null)"
if sqlexec(thisform.nhandle, lcsql) < 0
  thisform.showerror
  * rollback the transaction
  = sqlrollback(thisform.nhandle)
  return
endif

你現(xiàn)在需要知道 sql server 為新成員指定的 member_no。代碼與早期表單相似。
* 找出新成員的 member_no
if sqlexec(thisform.nhandle, "select @@identity") < 0
  <略去的代碼>

nnewmemberid = sqlresult.exp

然后構(gòu)造一個(gè) insert 來(lái)添加新記錄到 adult 表。從服務(wù)器獲取的 @@identity 值用于該 insert 中來(lái)正確地連接 adult 和 member 中的記錄。
* 添加新成員到 adult 表
lcsql = "insert adult (member_no, street, city, state, " + ;
                      "zip, phone_no, expr_date) " + ;
        "values (" + alltrim(str(nnewmemberid)) + ", '" +;
             alltrim(thisform.txtstreet.value) + ;
             "', '" + ;
             alltrim(thisform.txtcity.value) + ;
             "', '" + ;
             alltrim(thisform.txtstate.value) + ;
             "', '" + ;
             alltrim(thisform.txtzip.value) + ;
             "', '" + ;
             alltrim(thisform.txtphonenumber.value) + ;
             "', "'" + ;
             ttoc(dtot(gomonth(date(),12))) + "' )"
if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>

如前所述,如果一切正常,則提交事務(wù)處理否則回滾。

保存修改


保存已存在的成員的信息的代碼在表單的 updatemember 方法中。要保存信息,發(fā)送一條 update 語(yǔ)句到服務(wù)器。表單中的更新語(yǔ)句如下:

update <table> set <column1> = <value1>,
                   <column2> = <value2>,
                   等等...

這相當(dāng)?shù)刂苯亓水?dāng),雖然構(gòu)造一個(gè)要發(fā)送到服務(wù)器的 update 語(yǔ)句看起來(lái)有些龐大。你知道表的字段名和表單上的控件中的值。你可以一個(gè)字段接一個(gè)字段地生成 update 的 set 部分。但是,象上面這樣書(shū)寫(xiě)可以使程序更清晰。你也可能不想浪費(fèi) sql server 的時(shí)間來(lái)更新沒(méi)有修改的字段。這里的代碼使用了 oldval() 函數(shù),使用游標(biāo)緩存使這樣做變?yōu)榭赡埽獙⒂螛?biāo)中各字段的值與原始的值進(jìn)行比較。只有當(dāng)它被修改了時(shí)才讓它成員 update 語(yǔ)句的一部分。另外,遠(yuǎn)程視圖自動(dòng)地這樣做了。
lcsql = ""
* 更新該成員到 member 表
if c_member.firstname <> oldval("c_member.firstname")
  lcsql = lcsql + "firstname = '" + ;
          alltrim(thisform.txtfirstname.value) + "', "
endif
if c_member.lastname <> oldval("c_member.lastname")
  lcsql = lcsql + "lastname = '" +;
          alltrim(thisform.txtlastname.value) + "', "
endif
<略去的代碼>

如果 member 表中沒(méi)有字段被修改,變量 lcsql 中將沒(méi)有內(nèi)容且不會(huì)保存信息到表中。如果有要保存的數(shù)據(jù),構(gòu)造剩下的 update 語(yǔ)句并發(fā)送到服務(wù)器。
if len(lcsql) > 0
  * 添加 update,去掉最后一個(gè)逗號(hào),
  * 添加一個(gè) where 子句
  lcsql = "update member set " + ;
          left(lcsql, len(lcsql) - 2) + ;
          "where member_no = " + ;
          alltrim(thisform.txtmemberid.value)
  if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>

然后對(duì) adult 進(jìn)行與上面相同的處理。接著要做的是我們非常熟悉的事。如果一切正常提交事務(wù)處理否則回滾。

刪除一個(gè)成員

使用 sql pass-through 相對(duì)于遠(yuǎn)程視圖的一個(gè)優(yōu)點(diǎn)是,你增加了對(duì)發(fā)生什么和何時(shí)發(fā)生的控制。當(dāng)用戶單擊 delete 按鈕時(shí)運(yùn)行的代碼就是一個(gè)好的例子。
有很多理由可能使你不能刪除一個(gè)成員。如果成員有相關(guān)的 juveniles 或成員有未結(jié)清的借書(shū)時(shí)任何 delete 都會(huì)失敗。可以很容易地通過(guò)發(fā)送一個(gè) select 到服務(wù)器來(lái)檢查這一點(diǎn)。這里的代碼使用 sqlexec() 來(lái)檢查這兩個(gè)條件。如果兩者都為真,會(huì)顯示一個(gè)友好的信息且不會(huì)進(jìn)一步發(fā)生什么情況。
* 首先檢查是否有一個(gè)活動(dòng)的 juveniles
lcsql = "select member_no from juvenile " + ;
        "where adult_member_no = " + ;
        thisform.txtmemberid.value
if sqlexec(thisform.nhandle, lcsql) < 0
  thisform.showerror
  return
else
  if reccount("sqlresult") <> 0  
    lcmessage = "該成員不能刪除. " + ;
                "他是一個(gè)活動(dòng)的少年的成年。"
    = messagebox(lcmessage, mb_iconinformation)
    return
  endif  
endif

* 現(xiàn)在檢查成員還有活動(dòng)的借書(shū)
lcsql = "select member_no from loan " + ;
        "where member_no = " + ;
        thisform.txtmemberid.value
if sqlexec(thisform.nhandle, lcsql) < 0
  thisform.showerror
  return
else
  if reccount("sqlresult") <> 0  
    lcmessage = "該成員不能刪除。" + ;
                "他還有活動(dòng)的借書(shū)。"
    = messagebox(lcmessage, mb_iconinformation)
    return
  endif  
endif

如果還需要執(zhí)行額外的檢查,代碼可以放在以上代碼的后面。你可以完全控制要檢查的內(nèi)容和順序。如果所有的檢查都成功且成員可以刪除,則開(kāi)始一個(gè)事務(wù)處理。
在 member 表和 loanhist 及 reservation 表單定義了關(guān)系。每一次借書(shū)和還書(shū)都在 loanhist 表中有一條記錄。成員預(yù)約的每一本書(shū)在 reservation 表中有一條記錄。如果成員被刪除,需要?jiǎng)h除這兩個(gè)表中的相關(guān)信息。需要首先刪除它們,否則會(huì)違犯參照完整性。
* 刪除該成員的借書(shū)情況(loan history)記錄
lcsql = "delete loanhist where member_no = " + ;
        alltrim(thisform.txtmemberid.value)
if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>

* 刪除該成員的借書(shū)預(yù)約(loan reservation)記錄
lcsql = "delete reservation where member_no = " + ;
        alltrim(thisform.txtmemberid.value)
if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>
要?jiǎng)h除一個(gè)成人成員必須首先刪除 adult 表中的記錄然后才能刪除 member 表中的記錄。這也是事務(wù)處理的一部分,因此如何有任何錯(cuò)誤發(fā)生則回滾所有處理。
* 刪除成員
lcsql = "delete adult where member_no = " + ;
        alltrim(thisform.txtmemberid.value)
if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>

lcsql = "delete member where member_no = " + ;
        alltrim(thisform.txtmemberid.value)
if sqlexec(thisform.nhandle, lcsql) < 0
<略去的代碼>

如果所有刪除都沒(méi)有問(wèn)題則提交整個(gè)事務(wù)處理且成員被刪除。然后用戶會(huì)看到一個(gè)空的屏幕,因此添加一條空記錄到 c_member 并刷新表單顯示。

問(wèn)題

如何折衷地使用遠(yuǎn)程視圖和 sql pass-through? 

這是更多的工作

顯然,使用 sql pass-through 比使用遠(yuǎn)程視圖需要做更多的工作。視圖為你做了大量的工作,維護(hù)與服務(wù)器的通信和傳遞 inserts,updates 和 deletes。視圖是易于設(shè)置和使用的。

你擁有更多的控制

你看到了兩種觀點(diǎn)的示例,使用遠(yuǎn)程視圖的一個(gè)問(wèn)題是在 visual foxpro 和后端間的通信上你只有較少的控制。對(duì)于多數(shù)據(jù)庫(kù)方案,這不是一個(gè)問(wèn)題。但是,在這里使用的方案中出現(xiàn)了問(wèn)題。問(wèn)題用每一個(gè)表使用一個(gè)視圖得到了緩解但任然存在。當(dāng)視圖不能給你以所需的數(shù)據(jù)輸入能力時(shí),問(wèn)題會(huì)更為突出。
當(dāng)你使用 sql pass-through 時(shí),你可以完全控制 visual foxpro 與后端的通信。你構(gòu)造 sql 語(yǔ)句并用 sqlexec() 來(lái)發(fā)送它們到服務(wù)器。如果需要執(zhí)行數(shù)據(jù)驗(yàn)證和檢查商業(yè)規(guī)則,你可以決定在什么時(shí)候和如何做。

錯(cuò)誤信息可以更友好

因?yàn)槟阍诳刂剖裁窗l(fā)生,并且你手動(dòng)的進(jìn)行數(shù)據(jù)驗(yàn)證,因此你可以控制錯(cuò)誤信息。在可以預(yù)見(jiàn)一些事發(fā)生時(shí),你可以截取 sql server 錯(cuò)誤信息并顯示給用戶一個(gè)可以理解的信息。由于不可預(yù)料的事件你也會(huì)遇到 sqlexec() 失敗的問(wèn)題,如網(wǎng)絡(luò)故障。對(duì)于這些,你可以決定是否分解信息或以原始方式顯示它們。

一方面它提供了較少的互用性

該方法的一個(gè)問(wèn)題是從某種程度上犧牲了互用性。通過(guò) sqlexec() 發(fā)送到后端的 sql 語(yǔ)句是用后端語(yǔ)言編寫(xiě)。在本示例中是 sql server。當(dāng)轉(zhuǎn)換到 oracle 時(shí)需要重寫(xiě)多少代碼? 
雖然不同后端的基本的 select,insert,update 或 delete 格式?jīng)]有太大的不同。因此這里的示例可以很容易 轉(zhuǎn)移到其它后端。但是,重要的一點(diǎn)是取決于你使用的 sql 語(yǔ)句的復(fù)雜性,這可能限制了你交換后端的能力。當(dāng)然,如果應(yīng)用程序是為一種后端且只用一種后端,這將不是大的問(wèn)題。

另一方面它提供了更多的互用性

考慮如果你使用遠(yuǎn)程視圖而且你試著刪除一個(gè)沒(méi)有結(jié)清借書(shū)的成員會(huì)發(fā)生什么。在服務(wù)器上定義的參照完整性會(huì)防止刪除。但是,由 oracle 送回的錯(cuò)誤信息將與 sql server 送回的錯(cuò)誤信息不同。你可以分解錯(cuò)誤信息并將它們轉(zhuǎn)換到友好的格式,但是你將不得不分解不同的后端的信息。這就限制了你的互用性,因?yàn)槟悴坏貌粸椴煌姆?wù)器創(chuàng)建分解例程。
使用 sql pass-through 方法,你會(huì)發(fā)送一個(gè) select 語(yǔ)句到后端在 loan 表中搜索成員。如果 select 找到一條記錄則成員不能刪除。顯示給用戶的信息是相同的,而不論在 sqlexec() 中發(fā)送了什么。這種服務(wù)增了你的互用性,假定不同后端的 selects,inserts,updates 和 deletes 格式?jīng)]有多大變化,這是一種通情達(dá)理的假設(shè)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 衡东县| 连山| 从江县| 福安市| 邓州市| 大关县| 安泽县| 闽清县| 张家川| 宁强县| 陇西县| 扎赉特旗| 和政县| 南投县| 屏山县| 云龙县| 囊谦县| 无为县| 皋兰县| 亚东县| 万荣县| 河源市| 阜城县| 张掖市| 巴彦淖尔市| 寿阳县| 即墨市| 南平市| 崇文区| 土默特右旗| 仁化县| 荆州市| 鄂温| 广西| 西乌珠穆沁旗| 金山区| 新晃| 沙河市| 乐陵市| 龙里县| 游戏|