最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何减少SQL存储过程里的重复代码
时间:2026-07-12 09:36:52 编辑:袖梨 来源:一聚教程网
应将校验和计算逻辑封装为标量函数(如IsValidEmail),主存储过程仅调用;通用清理动作需参数化并加白名单与QUOTENAME防护;事务与错误处理应拆入独立包装过程统一管理。
用函数封装校验和计算逻辑
存储过程里反复写
IF @email NOT LIKE '%_@__%.__%'</p><p>这类校验,既难维护又容易漏改。真正该做的,是把邮箱格式、手机号、金额范围这些稳定规则抽成标量函数。</p><p>例如:<code>CREATE FUNCTION IsValidEmail(@input VARCHAR(255)) RETURNS BIT,内部用
CHARINDEX('@', @input)和LEN()组合判断,主过程只调用IF dbo.IsValidEmail(@email) = 0。好处是:一处修复,所有调用自动生效;函数可独立测试;避免在多个过程里散落相同正则或字符串操作。
注意:SQL Server 函数不能执行 DML,所以别试图在里面INSERT日志——那是存储过程的事。
参数化通用清理动作,别硬编码字段名
看到好几个过程都写着UPDATE users SET name = LTRIM(RTRIM(name)) WHERE id = @id?说明你该抽象出一个CleanStringField过程了。
它接受表名、字段名、ID 条件和清理规则(比如去首尾空格、替换全角字符),内部用EXEC sp_executesql拼接并执行——但必须加白名单校验:IF @table_name NOT IN ('users', 'orders'),否则就是 SQL 注入入口。
关键点:
- 字段名和表名不直接拼进 SQL,而是通过
QUOTENAME()包裹 - 清理规则用枚举值控制(如
@mode = 'TRIM'或'NORMALIZE_SPACE'),不用传任意字符串 - 执行前先
SELECT TOP 1验证目标字段是否存在,避免运行时报错中断流程
把事务和错误处理拆成独立模块
每个过程开头都写BEGIN TRY BEGIN TRANSACTION,结尾都COMMIT/ROLLBACK,还重复写RAISERROR?这不是健壮,是复制粘贴陷阱。
更稳妥的做法是:建一个usp_TransactionWrapper,它接收要执行的 SQL 字符串(或过程名)和超时秒数,内部统一管理事务边界、锁等待、死锁重试和错误日志记录。
调用时变成:EXEC @ret = usp_TransactionWrapper @sql = N'EXEC UpdateUserPoints @uid, @pts'。
这样做的代价是失去部分编译期检查,但换来的是错误码统一、重试策略集中、监控埋点一致——尤其适合批量任务或跨库操作。
别忘了:usp_TransactionWrapper自己不能嵌套调用自身,否则会触发保存点混乱。
动态 SQL 必须配白名单和预检
想让一个过程支持按不同字段去重?别写SET @sql = 'DELETE FROM '+@table+' WHERE id IN (SELECT MIN(id) FROM '+@table+' GROUP BY '+@group_cols) + ''——这等于把服务器大门钥匙交给调用方。
正确姿势:
只允许@group_cols从预设列表中选,比如email、phone、user_id, created_date三项,存进配置表valid_de duplication_keys;调用前查表确认,不匹配就RAISERROR退出。
另外,执行前强制加SELECT COUNT(*)预估影响行数,超过阈值(如 1000 行)就拒绝执行,防止误删。
MySQL 用户额外注意:DELETE ... WHERE id IN (SELECT ...)在 8.0+ 仍受 Error 1093 限制,得绕成临时表或 CTE,这点不能靠“通用封装”掩盖。
最常被忽略的其实是权限粒度——把清洗逻辑塞进一个大过程,DBA 就只能给EXECUTE权限,等于开放了背后所有表的读写。拆成小函数+小过程,才能按需授权。
相关文章
- 《背包英雄》可使用道具分享 07-12
- todesk远程控制手机教程 07-12
- 拼课-Java AI 高级全能工程师课程体系 07-12
- 大模型训练阶段有哪些 07-12
- AI产品经理的尴尬和机遇 07-12
- 利用阿里云极速型NAS构建高可用的GitLab 07-12