校验必须前置,SQL Server用IF+THROW(错误号50000–59999、状态值1),MySQL需DECLARE EXIT HANDLER配SIGNAL,字符串判空用IS NULL或TRIM,数值范围直写比较,外键检查用NOT EXISTS加NOLOCK。
校验必须放在 INSERT/UPDATE 之前,否则事务已部分生效,回滚成本高、数据可能污染。
所有业务规则检查(非空、长度、数值范围、外键存在性)必须在任何写操作前完成。IF 判断不满足就立刻 THROW,SQL Server 会中止批处理并触发客户端异常捕获。
THROW 50000, '订单金额超出单笔限额', 1 是推荐写法,错误号限定在 50000–59999,状态值固定填 1@param = '',要写成 @param IS NULL OR LTRIM(RTRIM(@param)) = ''
IF @amount 1000000,避免嵌套 CASE 或隐式转换IF NOT EXISTS (SELECT 1 FROM users WITH (NOLOCK) WHERE id = @user_id),加 WITH (NOLOCK) 防阻塞MySQL 的 SIGNAL 不会自动终止后续语句,没配 handler 就等于白写——过程继续执行,可能造成部分写入。
DECLARE EXIT HANDLER FOR SQLEXCEPTION,并在其中 ROLLBACK
IF @param IS NULL OR TRIM(@param) = '' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '用户名不能为空'; END IF;
IF @id IS NULL OR @id ,别对 INT 字段做 <code>= '',会触发隐式转换报错MESSAGE_TEXT 控制在 128 字符内,避免被截断;统一用 '45000' 表示通用业务错误标量 UDF 在 SQL Server 里批量调用性能极差,MySQL 根本不支持函数内抛异常——所谓“复用”,本质是复用可预测的 SQL 片段。
dbo.tvf_validate_amount(@amount),返回 is_valid BIT 和 error_message NVARCHAR(256)
SELECT * FROM dbo.tvf_validate_amount(@input),不是 SELECT dbo.fn_check_amount(@input)
RAISE EXCEPTION + BEGIN ... EXCEPTION 块,但函数定义里不能查表做存在性判断,得靠调用方传入预检结果数据库约束是第一道防线,存储过程校验是最后一道——二者不互斥,但职责分明。
NOT NULL、CHECK (age BETWEEN 0 AND 150) 这类基础规则优先走 DDL 约束,SQL Server/PG/MySQL 8.0.16+ 都支持CASE @col_name WHEN 'user_id' THEN 'user_id' ELSE SIGNAL ... END
最容易被忽略的是校验与事务边界的耦合——哪怕写了完整 IF 块,如果没把整个校验+写入包在同一个显式事务里,或者 MySQL 没配 EXIT HANDLER,错误发生时仍可能留下脏数据。