前言
在 Delphi 中使用 TADOStoredProc 呼叫資料庫預存程序,關鍵在於參數正確建立與綁定、區分有無結果集、異常處理。本文提供從資料模組設計到 UI 綁定的完整實作範例。
資料模組設計 (DataModule)
unit DataMConn;
interface
uses
SysUtils, Classes, DB, ADODB;
type
TDataModule1 = class(TDataModule)
ADOConn: TADOConnection;
ADOSP: TADOStoredProc;
DSP: TDataSource;
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
DataModule1: TDataModule1;
implementation
{$R *.dfm}
procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
ADOConn.ConnectionString :=
'Provider=SQLOLEDB.1;Server=.;Database=TestDB;Integrated Security=SSPI;';
ADOConn.LoginPrompt := False;
ADOConn.Connected := True;
DSP.DataSet := ADOSP; // 綁定 DataSource
end;
end.
呼叫預存程序核心程式碼
procedure TForm1.CallStoredProc;
var
SP: TADOStoredProc;
begin
SP := DataModule1.ADOSP;
// 1. 指定預存程序名稱
SP.ProcedureName := 'GetAbove'; // 例如:CREATE PROC GetAbove @tableName varchar(20), @strDate varchar(20)
// 2. 清除舊參數 (重要:避免重複呼叫時參數殘留)
SP.Parameters.Clear;
// 3. 建立輸入參數
// CreateParameter(Name, DataType, Direction, Size, Value)
SP.Parameters.CreateParameter('@tableName', ftString, pdInput, 20, 'TableA');
SP.Parameters.CreateParameter('@strDate', ftString, pdInput, 20, '2024-01-15');
// 4. 若有輸出參數
// SP.Parameters.CreateParameter('@RowCount', ftInteger, pdOutput, 0, 0);
// 5. 參數賦值 (也可在第 3 步直接給 Value)
SP.Parameters.ParamByName('@tableName').Value := 'TableA';
SP.Parameters.ParamByName('@strDate').Value := '2024-01-15';
try
// 6. 執行:有結果集用 Open,無結果集用 ExecProc
SP.Open;
// 7. 讀取輸出參數 (若有)
// var RowCount: Integer := SP.Parameters.ParamByName('@RowCount').Value;
// 8. 綁定到 DataGrid
DBGrid1.DataSource := DataModule1.DSP;
except
on E: Exception do
begin
ShowMessage('預存程序執行錯誤: ' + E.Message);
SP.Close; // 確保關閉
end;
end;
end;
參數方向與資料類型對照
| 方向常數 | 用途 | 對應 SQL |
pdInput | 輸入參數 | @Param IN |
pdOutput | 輸出參數 | @Param OUT |
pdInputOutput | 輸入輸出參數 | @Param IN OUT |
pdReturnValue | 返回值 | RETURN @Val |
| Delphi ftType | SQL Server 類型 | 備註 |
ftString | varchar/nvarchar | Size 指定長度 |
ftWideString | nvarchar | Unicode 字串 |
ftInteger | int | |
ftLargeint | bigint | |
ftFloat | float/real | |
ftBCD | decimal/numeric | 精確數值 |
ftDateTime | datetime/datetime2 | |
ftBoolean | bit | |
ftBlob | varbinary(max)/image | 二進位資料 |
進階:動態建立參數 (適用未知參數結構)
procedure PrepareSPParameters(SP: TADOStoredProc; const ParamValues: array of Variant);
var
I: Integer;
Param: TParameter;
begin
SP.Parameters.Refresh; // 從資料庫自動獲取參數元資料 (需連線)
// 或手動根據陣列建立
SP.Parameters.Clear;
for I := 0 to High(ParamValues) do
begin
Param := SP.Parameters.CreateParameter(
'@Param' + IntToStr(I + 1),
ftVariant, pdInput, 0, ParamValues[I]);
// ftVariant 由 ADO 自動推斷類型
end;
end;
多結果集處理 (SQL Server)
// 預存程序返回多個 SELECT
SP.Open; // 取得第一個結果集
while not SP.Recordset.EOF do
begin
// 處理第一個結果集
SP.Recordset.MoveNext;
end;
// 移至下一個結果集
if SP.Recordset.NextRecordset(Pointer(0)) then
begin
// 處理第二個結果集
// 可建立另一個 TADODataSet 綁定
// ADODataSet2.Recordset := SP.Recordset;
end;
常見錯誤與排查
| 錯誤現象 | 可能原因 | 解決方案 |
| "Parameter '@xxx' not found" | 參數名稱拼寫錯誤、大小寫不符 (SQL Server 不區分大小寫但 ADO 可能區分) | 使用 ParamByName、檢查 ProcedureName 正確、執行 Parameters.Refresh |
| "Data type mismatch" | Delphi ftType 與 SQL 類型不符 | 對照類型表、數值型別注意 ftBCD vs ftFloat |
| ExecProc 無反應/掛住 | 預存程序內有長時間運算、死結 | 設定 SP.CommandTimeout、優化 SP、非同步執行 |
| 結果集為空但應有資料 | 用 ExecProc 而非 Open | 有 SELECT 回傳必須用 Open |
| 輸出參數值為 Null | 未在 SP 中正確 SET、方向錯誤 | 檢查 SP 定義 OUTPUT、ADO 參數方向 pdOutput |
最佳實務總結
- 集中管理:預存程序呼叫邏輯封裝在資料模組或專用 Service 類別
- 參數化:絕不拼接 SQL 字串,防注入、利用快取計畫
- 資源釋放:
try...finally SP.Close; end 確保游標關閉
- 錯誤包裝:捕獲
EADOError 轉換為業務例外,記錄 SQL 與參數值便於除錯
- 效能:重複呼叫同一 SP 時,重用
TADOStoredProc 實例,僅更新參數值
結語
TADOStoredProc 提供了結構化、型別安全的預存程序呼叫介面。掌握 Parameters.Clear → CreateParameter → ParamByName → Open/ExecProc 這套標準流程,配合資料模組集中連線管理,即能建構穩健、可維護的資料存取層。