TADOStoredProc 是 Delphi 通過 ADO 調用 SQL Server / Oracle / PostgreSQL 等數據庫存儲過程的標準組件。網上範例常忽略參數方向、數據類型映射、結果集與輸出參數共存等細節,導致「參數不存在」「類型不匹配」等錯誤。本文給出生產級調用模板與避坑清單。
TADOConnection (名 ADOConn) 並配置連接字符串。TADOStoredProc (名 ADOSP_GetAbove),Connection := ADOConn。TDataSource (名 DS_GetAbove),DataSet := ADOSP_GetAbove。TDBGrid (名 DBGrid1),DataSource := DS_GetAbove。with DataModule1.ADOSP_GetAbove do
begin
ProcedureName := 'GetAbove'; { 1. 指定過程名 }
Parameters.Clear; { 2. 清舊參數 }
{ 3. 手動建立參數:名稱、類型、方向、長度、默認值 }
Parameters.CreateParameter('@tableName', ftString, pdInput, 50, 'MyTable');
Parameters.CreateParameter('@strDate', ftString, pdInput, 20, '2024-01-01');
{ 輸出參數示例 }
Parameters.CreateParameter('@RetCode', ftInteger, pdOutput, 0, 0);
Parameters.CreateParameter('@RetMsg', ftString, pdOutput, 200, '');
{ 4. 賦值輸入參數 }
Parameters.ParamByName('@tableName').Value := edtTable.Text;
Parameters.ParamByName('@strDate').Value := edtDate.Text;
try
Open; { 5. 返回結果集用 Open }
{ 或 ExecProc 不返回結果集 }
except
on E: Exception do
begin
ShowMessage('執行失敗: ' + E.Message);
Exit;
end;
end;
{ 6. 讀取輸出參數 }
ShowMessage(Format('RetCode=%d, RetMsg=%s',
[Parameters.ParamByName('@RetCode').Value,
Parameters.ParamByName('@RetMsg').Value]));
{ 7. 綁定網格 (設計時已綁可省略) }
DBGrid1.DataSource := DataModule1.DS_GetAbove;
end;
| 常量 | 含義 | 典型用途 |
|---|---|---|
| pdInput | 僅輸入 | WHERE 條件、分頁參數 |
| pdOutput | 僅輸出 | 返回碼、錯誤信息、新生成 ID |
| pdInputOutput | 輸入輸出 | 累計值、雙向數據 |
| pdReturnValue | 返回值 | SQL Server RETURN @val |
ftString / ftWideString → VARCHAR/NVARCHAR (長度參數必須 ≥ 實際長度)ftInteger / ftSmallint → INT/SMALLINTftLargeint → BIGINTftFloat / ftBCD → FLOAT/DECIMALftDateTime / ftTimeStamp → DATETIME/DATETIME2ftBlob / ftMemo → VARBINARY(MAX)/NVARCHAR(MAX)ftBoolean → BITftGuid → UNIQUEIDENTIFIERSQL Server 2008+ 支援 READONLY 表值參數,Delphi 需構造 TDataSet (如 TClientDataSet) 填充數據,再用 ftDataSet 或 ftCursor 類型參數傳遞。屬進階用法,此處不展開。
Open 返回結果集時,**輸出參數值在結果集關閉前不可讀** (SQL Server 行為)。兩種處理:
Open 讀數據,最後 Close 再讀輸出參數。NextRecordSet 跳過結果集直接讀輸出參數 (ADO 2.6+)。| 現象 | 原因 | 解決 |
|---|---|---|
| 「Parameter '@x' not found」 | 參數名大小寫/拼寫錯誤、或未 CreateParameter | 用 ParamByName 索引,嚴格對應 SP 定義 |
| 「Data type mismatch」 | ftXxx 與 SQL 類型不匹配 (如 ftString 長度不足) | 核對長度、改用 ftWideString/ftLargeint 等 |
| 輸出參數為 Null | 在 Open 結果集未 Close 前讀取 | 移至 Close 後讀取 |
| 「Syntax error near '@tableName'」 | 動態 SQL 拼接表名時未用 QUOTENAME 或參數化失敗 | 表名不可參數化,需白名單驗證後拼接 |
| 多結果集只取第一個 | SP 含多個 SELECT | 循環 while not EOF do ...; NextRecordSet; |
type
TSPParam = record
Name: string;
DataType: TFieldType;
Direction: TParameterDirection;
Size: Integer;
Value: Variant;
end;
function ExecStoredProc(ADOConn: TADOConnection; const SPName: string;
const InputParams: array of TSPParam;
out OutputParams: array of Variant;
out DataSet: TADODataSet): Boolean;
var
SP: TADOStoredProc;
i: Integer;
begin
Result := False;
SP := TADOStoredProc.Create(nil);
try
SP.Connection := ADOConn;
SP.ProcedureName := SPName;
SP.Parameters.Clear;
for i := Low(InputParams) to High(InputParams) do
with InputParams[i] do
SP.Parameters.CreateParameter(Name, DataType, Direction, Size, Value);
SP.Open;
DataSet := TADODataSet.Create(nil);
DataSet.Connection := ADOConn;
DataSet.CommandText := SPName;
DataSet.CommandType := cmdStoredProc;
DataSet.Open; { 複製結果集 }
SetLength(OutputParams, SP.Parameters.Count);
for i := 0 to SP.Parameters.Count - 1 do
if SP.Parameters[i].Direction in [pdOutput, pdInputOutput, pdReturnValue] then
OutputParams[i] := SP.Parameters[i].Value;
Result := True;
except
SP.Free;
raise;
end;
end;
掌握 CreateParameter 手動定義、方向/類型精確映射、結果集與輸出參數生命週期協同,即可構建健壯的存儲過程調用層。建議封裝通用 Helper 統一異常處理、日誌記錄、參數驗證,減少業務代碼樣板碼。