You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi调用SQL Server存储过程时多列唯一索引违例未捕获异常

问题:Delphi调用SQL Server存储过程时,违反多列唯一索引的异常无法捕获

环境与代码

Delphi端调用代码

使用Delphi 10.1 Berlin,通过TADOQuery组件调用SQL Server存储过程,代码如下:

try
  DM.OmegaCA_SS.BeginTrans;
  DM.Exec_SQL.Close;
  DM.Exec_SQL.SQL.Clear;
    
  Exec_SQL_Str := 'execute OMEGACA.P_ACC_POL_RULE_INS '
  ... params ... ;
    
  DM.Exec_SQL.SQL.Add(Exec_SQL_Str);
    
  DM.Exec_SQL.ExecSQL;
  DM.OmegaCA_SS.CommitTrans;    
except
  on E:EAdoError do
  begin
    DM.OmegaCA_SS.RollbackTrans;
    Messagedlg_alb(E.Message, 'Error', mtError, [mbYes], 0);
    statusbar1.SimpleText := '';
    Exit;
  end;
    
  on E:EDatabaseError do
  begin
    DM.OmegaCA_SS.RollbackTrans;
    Messagedlg_alb(E.Message, 'Error', mtError, [mbYes], 0);
    statusbar1.SimpleText := '';
    Exit;
  end;
    
  on E:Exception do
  begin
    DM.OmegaCA_SS.RollbackTrans;
    Messagedlg_alb(E.Message, 'Error', mtError, [mbYes], 0);
    statusbar1.SimpleText := '';
    Exit;
  end;
end;

SQL Server端存储过程

CREATE PROCEDURE [OMEGACA].[P_ACC_POL_RULE_INS] 
  (@p_policy_id int,
   @p_rule_name nvarchar(100),
   @p_rule_desc nvarchar(1000),
   @p_cond_eval int,
   @p_status_id int)
AS
BEGIN
     INSERT INTO OMEGACA.ACC_POL_RULE (policy_id, 
                                       rule_name, rule_desc, 
                                       cond_eval, status_id)
     VALUES (@p_policy_id,
             @p_rule_name, @p_rule_desc,
             @p_cond_eval, @p_status_id);
END;

多列唯一索引定义

CREATE UNIQUE NONCLUSTERED INDEX [ACC_POL_RULE_UN] 
ON [OMEGACA].[ACC_POL_RULE] ([POLICY_ID] ASC, [RULE_NAME] ASC)
       WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
             SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, 
             DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, 
             ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]

问题现象

  • 输入值违反上述多列唯一索引时,Delphi代码中的异常未被捕获;向非空列插入NULL时也存在同样问题。
  • 违反多列主键或单列唯一索引时,异常能被正常捕获。

解决方案

1. 修改存储过程,显式抛出致命错误

SQL Server对多列唯一索引违反默认返回警告级别错误,ADO不会触发异常。需在存储过程中捕获约束错误并抛出严重级别16的致命错误:

CREATE PROCEDURE [OMEGACA].[P_ACC_POL_RULE_INS] 
  (@p_policy_id int,
   @p_rule_name nvarchar(100),
   @p_rule_desc nvarchar(1000),
   @p_cond_eval int,
   @p_status_id int)
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRY
    INSERT INTO OMEGACA.ACC_POL_RULE (policy_id, rule_name, rule_desc, cond_eval, status_id)
    VALUES (@p_policy_id, @p_rule_name, @p_rule_desc, @p_cond_eval, @p_status_id);
  END TRY
  BEGIN CATCH
    -- 捕获唯一约束/索引重复错误(2601为唯一索引重复,2627为主键/唯一约束重复)
    IF ERROR_NUMBER() IN (2601, 2627)
    BEGIN
      RAISERROR('同一策略下规则名称不能重复', 16, 1);
      RETURN;
    END
    -- 重新抛出其他类型错误
    THROW;
  END CATCH
END;

2. 改用参数化调用存储过程(推荐)

拼接SQL字符串易引发注入问题,且参数化调用的ADO错误反馈更可靠。修改Delphi代码如下:

try
  DM.OmegaCA_SS.BeginTrans;
  DM.Exec_SQL.Close;
  DM.Exec_SQL.SQL.Clear;
  
  -- 定义参数化存储过程调用语句
  DM.Exec_SQL.SQL.Add('EXEC OMEGACA.P_ACC_POL_RULE_INS :p_policy_id, :p_rule_name, :p_rule_desc, :p_cond_eval, :p_status_id');
  
  -- 绑定参数值
  DM.Exec_SQL.Parameters.ParamByName('p_policy_id').Value := ...;
  DM.Exec_SQL.Parameters.ParamByName('p_rule_name').Value := ...;
  DM.Exec_SQL.Parameters.ParamByName('p_rule_desc').Value := ...;
  DM.Exec_SQL.Parameters.ParamByName('p_cond_eval').Value := ...;
  DM.Exec_SQL.Parameters.ParamByName('p_status_id').Value := ...;
  
  DM.Exec_SQL.ExecSQL;
  DM.OmegaCA_SS.CommitTrans;    
except
  on E: Exception do
  begin
    DM.OmegaCA_SS.RollbackTrans;
    Messagedlg_alb(E.Message, 'Error', mtError, [mbYes], 0);
    statusbar1.SimpleText := '';
    Exit;
  end;
end;

3. 检查ADO连接属性

确认TADOConnection的Attributes属性包含xaCommitRetaining和xaAbortRetaining,CursorLocation可根据场景设置为clUseServer或clUseClient,确保ADO能正确接收SQL Server的错误信号。

原理说明

SQL Server对主键、单列唯一约束的违反会直接抛出严重级别14以上的错误,ADO会触发Delphi异常;但多列唯一索引违反默认返回低级别警告,ADO不会主动触发异常。通过存储过程的TRY/CATCH捕获错误并重新抛出高严重级别错误,即可让Delphi的异常处理逻辑正常捕获到。

内容的提问来源于stack exchange,提问作者altink

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:25:54