首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

VFP 訪問異構資料庫實戰:SPT 技術深度解析與事務管理最佳實踐

     編輯:MySQL 資料庫相關知識

Visual FoxPro (VFP) 通過 ODBC 封裝提供兩種訪問遠程異構資料庫(SQL Server、Oracle、DB2 等)的技術:遠程視圖與SQL Pass-Through (SPT)。遠程視圖是 SPT 的可視化子集,操作簡單但功能受限;SPT 雖無圖形界面、需手工管理連接,卻擁有多游標返回、非 SELECT 執行、存儲過程調用、遠程函數執行、客戶端事務控制五大核心優勢,是構建健壯 C/S 架構的關鍵利器。本文以 SQL Server Northwind 庫為例,系統講解 SPT 核心操作模式。

一、連接管理:建立與斷開

* 設置環境:從不顯示 ODBC 登錄對話框
SQLSETPROP(0, "DispLogin", 3)

* 建立連接字符串(DSN-less 推薦,避免客戶端配置 DSN)
LOCAL lcConnStr, hConn
lcConnStr = "Driver=SQL Server;Server=BOE;Uid=sa;Pwd=;Database=Northwind;"

* 同步連接,返回連接句柄(>0 成功,<0 失敗)
hConn = SQLSTRINGCONNECT(lcConnStr)

IF hConn <= 0
    MESSAGEBOX("連接失敗:" + SQLERRMSG(), 16, "VFP SPT Demo")
    RETURN
ELSE
    MESSAGEBOX("連接成功,句柄:" + TRANSFORM(hConn), 64, "VFP SPT Demo")
ENDIF

* 斷開連接(程序結束前必須調用,防止連接泄漏)
SQLDISCONNECT(hConn)
最佳實踐: 連接句柄 hConn 應作為全局/會話級變量持有,避免重複連接開銷。異常分支務必 TRY...CATCH...FINALLY SQLDISCONNECT(hConn) 保證釋放。

二、一次獲取多個游標(批量查詢)

LOCAL lcSQL, lnCursorCount
lcSQL = "SELECT * FROM Employees;" + CHR(10) + ;
        "SELECT * FROM Customers;" + CHR(10) + ;
        "SELECT * FROM Products;"

* SQLEXEC 返回游標數量,默認命名 TEMP, TEMP1, TEMP2...
lnCursorCount = SQLEXEC(hConn, lcSQL, "TEMP")
? "返回游標數:" + TRANSFORM(lnCursorCount)  && 3

* 使用游標
SELECT TEMP
LIST FIRST 5 FIELDS EmployeeID, LastName, FirstName
SELECT TEMP1
LIST FIRST 5 FIELDS CustomerID, CompanyName
SELECT TEMP2
LIST FIRST 5 FIELDS ProductID, ProductName

* 釋放游標
USE IN SELECT("TEMP")
USE IN SELECT("TEMP1")
USE IN SELECT("TEMP2")

三、執行非 SELECT 語句(DML/DDL)

LOCAL lcSQL, lnResult
lcSQL = "IF EXISTS (SELECT * FROM Customers WHERE CustomerID = 'TEST') " + ;
        "DELETE FROM Customers WHERE CustomerID = 'TEST'; " + ;
        "ELSE " + ;
        "INSERT INTO Customers (CustomerID, CompanyName) VALUES ('TEST', '這是一個測試!');"

lnResult = SQLEXEC(hConn, lcSQL)
IF lnResult < 0
    MESSAGEBOX("執行失敗:" + SQLERRMSG(), 16, "SPT DML")
ELSE
    MESSAGEBOX("執行成功,影響行數:" + TRANSFORM(SQLROWSETCOUNT(hConn)), 64, "SPT DML")
ENDIF

參數化防注入:兩種方案

* 方案一:字符串拼接(需嚴格轉義,不推薦生產用)
LOCAL lcCustID
lcCustID = 'TEST'
lcSQL = "IF EXISTS (SELECT * FROM Customers WHERE CustomerID = '" + lcCustID + "') ..."

* 方案二:SPT 標準參數標記「?」(推薦,驅動層處理類型轉換與轉義)
lcSQL = "IF EXISTS (SELECT * FROM Customers WHERE CustomerID = ?lcCustID) " + ;
        "DELETE FROM Customers WHERE CustomerID = ?lcCustID; " + ;
        "ELSE INSERT INTO Customers (CustomerID, CompanyName) VALUES (?lcCustID, '測試');"
SQLEXEC(hConn, lcSQL)  && VFP 自動綁定內存變量 lcCustID

四、調用遠程存儲過程與特殊函數

* 調用帶返回值的存儲過程
LOCAL lcSQL, lnResult
lcSQL = "{? = CALL GetCustomerOrderCount('ALFKI')}"  && ODBC CALL 語法
lnResult = SQLEXEC(hConn, lcSQL, "spResult")
IF lnResult > 0
    SELECT spResult
    ? "ALFKI 訂單數:" + TRANSFORM(spResult.ReturnValue)
    USE IN SELECT("spResult")
ENDIF

* 獲取服務器時間(演示特殊函數調用)
SQLEXEC(hConn, "SELECT GETDATE() AS ServerDateTime", "tmpTime")
SELECT tmpTime
? "服務器當前時間:" + TRANSFORM(tmpTime.ServerDateTime)
USE IN SELECT("tmpTime")

五、客戶端事務管理:跨多表更新的 ACID 保障

VFP SPT 事務綁定於連接句柄,該連接上所有 SQLEXEC 執行的 DML 直至 SQLCOMMIT/SQLROLLBACK 形成原子單元。

* 開啟手工事務模式(2=手工,1=自動提交默認)
SQLSETPROP(hConn, "Transactions", 2)

LOCAL lcSQL, lnExec
lcSQL = "DELETE FROM Customers WHERE CustomerID = 'BLAUS';" + CHR(10) + ;
        "INSERT INTO Customers (CustomerID, CompanyName) VALUES ('TEST1', '測試事務');"

lnExec = SQLEXEC(hConn, lcSQL)

IF lnExec > 0
    SQLCOMMIT(hConn)      && 提交事務
    ? "事務提交成功"
ELSE
    SQLROLLBACK(hConn)    && 回滾事務
    ? "事務回滾:" + SQLERRMSG()
ENDIF

* 恢復自動提交模式,防止後續操作誤入未知事務
SQLSETPROP(hConn, "Transactions", 1)
關鍵點:
  • 事務範圍 = 連接句柄。同一連接上所有 DML 自動納入。
  • 異常分支必須 SQLROLLBACK,否則鎖持有至連接斷開,阻塞其他會話。
  • 完成業務單元後立即 SQLSETPROP(..., "Transactions", 1) 重置模式,避免「殭屍事務」。

六、SPT 劣勢與規避策略

七、總結與擴展

VFP SPT 雖屬經典技術,但其直通 ODBC、無中間層開銷、支持完整 SQL 方言、客戶端事務控制的特性,在存量 C/S 系統維護、異構數據遷移、報表即時查詢等場景仍具不可替代價值。掌握連接管理、參數化、多游標、存儲過程調用、事務控制五大核心模式,即可應對 90% 實戰場景。對於新建項目,建議遷移至 .NET (ADO.NET/EF Core) 或 Java (JDBC/MyBatis) 等現代技術棧,獲得類型安全、ORM、連接池、異步非阻塞等現代化能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
劣勢影響規避策略
無圖形界面開發調試依賴代碼、PRINT、DEBUGGER封裝通用 SPT 類庫、統一錯誤處理、日誌記錄
手工維護連接易泄漏、並發控制複雜連接池模式(單例/多例)、RAII 風格封裝、超時檢查釋放
默認只讀游標需額外設置 CursorAdapter 或 SQLSETPROP(..., "FetchMemo", .T.) 才可更新僅讀場景用默認;需更新時顯式 SQLSETPROP(hConn, "CursorType", 1) 或使用 CursorAdapter