当前位置:首页 > 问答 > 正文

T-SQL里那些流程控制的玩意儿,怎么用起来更顺手点

那咱们就直接开唠T-SQL里这些流程控制的玩意儿怎么能用得更溜,这些东西就像是你写代码时的方向盘和刹车,用好了能让你的脚本跑得又稳又快,用不好就可能是东一榔头西一棒子,逻辑乱成一团麻。

先从最基础的 BEGIN...END 说起

这哥俩儿基本上形影不离,你可以把它俩想象成一个包装盒或者一对大括号 ,它的核心作用就一个:把好几条T-SQL语句打包成一个逻辑块,很多流程控制语句,比如咱们后面要讲的 IFWHILE,默认只控制紧跟着的一条语句,如果你想让它控制一堆语句,就必须用 BEGIN...END 把这个“一堆”给包起来。

来源参考:在微软官方文档中,BEGIN...END 被定义为定义语句块的关键字。

  • 怎么用更顺手?
    • 养成习惯,哪怕只有一条语句也加上它。 这是个非常好的编程习惯,比如写 IF 语句时,即使你现在判断后只想做一件事,也顺手用 BEGIN...END 把它包住,为什么呢?因为保不齐哪天需求变了,你需要在里面加第二句、第三句代码,如果你一开始没加,到时候很可能忘了补上 BEGIN...END,导致逻辑错误(只有第一条语句受 IF 控制,后面的语句永远都会执行),一开始就加上,反而是省事的。
    • 保持缩进,让结构一目了然。 BEGINEND 关键字最好单独成行,并且它们包裹的语句统一向右缩进(比如一个Tab键),这样谁和谁是一伙的,看得清清楚楚。

示例:

T-SQL里那些流程控制的玩意儿,怎么用起来更顺手点

-- 不推荐的写法(容易埋坑)
IF @Count > 10
    PRINT 'Count is large.'
    SET @Status = 1 -- 这行永远都会执行,不受IF控制!
-- 推荐的顺手写法
IF @Count > 10
BEGIN
    PRINT 'Count is large.'
    SET @Status = 1 -- 这行只在 IF 条件成立时执行
END

条件判断的大佬:IF...ELSE

这个没啥稀奇,如果.....否则...”,但在T-SQL里用顺手,有几个小窍门。

来源参考:微软官方文档将 IF...ELSE 描述为基于条件强制执行 Transact-SQL 语句的条件。

  • 怎么用更顺手?
    • 处理“没有找到”的情况时,优先用 IF NOT EXISTS 比如你想检查一个表里存不存在某条记录,然后决定是插入还是更新,用 IF NOT EXISTS (...) 的查询性能,通常比先 SELECT COUNT(*) 再判断计数是否大于0要好得多,因为 EXISTS 遇到第一条匹配的记录就返回了,而 COUNT(*) 需要扫描所有匹配的记录。
    • ELSE IF 处理多条件分支。 当你的判断条件不止一个的时候,别用一堆嵌套的 IF...ELSE,可以用 ELSE IF 来让结构更清晰,这跟其他编程语言里的 else if 一样。
    • 别忘了 BEGIN...END 重申一遍,每个分支如果有多条语句,老老实实包起来。

示例:

T-SQL里那些流程控制的玩意儿,怎么用起来更顺手点

-- 检查是否存在,然后决定插入还是更新
IF NOT EXISTS (SELECT 1 FROM Users WHERE UserId = @UserId)
BEGIN
    INSERT INTO Users (UserId, UserName) VALUES (@UserId, @UserName)
    PRINT 'New user added.'
END
ELSE
BEGIN
    UPDATE Users SET UserName = @UserName WHERE UserId = @UserId
    PRINT 'User updated.'
END
-- 使用 ELSE IF 处理多分支
IF @Score >= 90
BEGIN
    SET @Grade = 'A'
END
ELSE IF @Score >= 80
BEGIN
    SET @Grade = 'B'
END
ELSE
BEGIN
    SET @Grade = 'C'
END

循环利器:WHILE

当你需要重复干某件事,直到满足某个条件为止,就得请出 WHILE 循环了。

来源参考:微软官方文档指出,WHILE 语句用于设置重复执行 SQL 语句或语句块的条件。

  • 怎么用更顺手?
    • 千万小心无限循环! 这是新手最容易栽跟头的地方,你一定要确保在循环体内部,有某种操作能改变循环条件,让它最终能变成 FALSE,否则循环就会永远停不下来,在循环里对一个变量进行递增 SET @Counter = @Counter + 1,或者遍历游标 FETCH NEXT
    • 配合 BREAKCONTINUE 使用。 BREAK 是立刻踹门而出,结束整个循环。CONTINUE 是跳过本轮循环剩下的代码,直接开始下一轮,这两个家伙要慎用,用多了会破坏代码的正常逻辑流,让程序难读懂,但在某些情况下,比如遇到某种极端错误需要立即退出时,BREAK 就很管用。
    • 性能警告: 在数据库里搞循环(尤其是基于游标的循环)通常不是高性能的做法,数据库擅长的是基于集合的操作(一句SQL处理一堆数据),如果可能,尽量用 UPDATEINSERT ... SELECT 这类集合操作来代替循环,但如果业务逻辑非常复杂,必须一行一行处理,那该用 WHILE 也得用。

示例:

T-SQL里那些流程控制的玩意儿,怎么用起来更顺手点

-- 一个简单的计数器循环
DECLARE @Counter INT = 1
WHILE @Counter <= 10
BEGIN
    PRINT 'Current count: ' + CAST(@Counter AS VARCHAR)
    SET @Counter = @Counter + 1 -- 这句至关重要,没有它就无限循环了
END
-- 使用 BREAK 提前退出
WHILE 1 = 1 -- 这是一个看似无限循环的条件
BEGIN
    FETCH NEXT FROM MyCursor INTO @SomeVariable
    IF @@FETCH_STATUS != 0
        BREAK -- 如果游标取不到数据了,就跳出循环
    -- ... 处理数据 ...
END

让代码更优雅的 CASE

CASE 表达式严格来说不完全是流程控制语句(它不能像 IF 那样执行语句块),但它绝对是控制数据流向、实现条件判断的超级顺手工具,它能在一条SQL语句里内嵌复杂的条件逻辑。

来源参考:微软官方文档将 CASE 描述为计算条件列表并返回多个可能结果表达式之一。

  • 怎么用更顺手?
    • SELECT 列表里动态生成列值。 这是 CASE 最常用的场景,可以根据不同条件显示不同的计算结果或文本,让你的查询结果更直观。
    • ORDER BY 子句里实现自定义排序。 如果你不想按字段本身的值排序,而是想按某种自定义规则(让状态为‘紧急’的记录排在最前面),CASE 是唯一的选择。
    • WHERE 子句里构建动态条件。 虽然有时可以用 AND/OR 组合替代,但复杂的、互斥的条件用 CASE 写起来可能更清晰。
    • 记住它是个表达式,会返回一个值。 所以它可以用在任何需要值的地方。

示例:

-- 在 SELECT 中使用,给成绩打等级
SELECT
    StudentName,
    Score,
    Grade = CASE
                WHEN Score >= 90 THEN '优秀'
                WHEN Score >= 80 THEN '良好'
                WHEN Score >= 60 THEN '及格'
                ELSE '不及格'
            END
FROM ExamResults
-- 在 ORDER BY 中使用,实现自定义排序(先排‘活跃’用户,再按注册时间倒序)
SELECT UserName, Status, RegTime
FROM Users
ORDER BY
    CASE WHEN Status = 'Active' THEN 1 ELSE 2 END, -- 活跃用户排前面
    RegTime DESC

总结一下怎么才算顺手:

  1. 结构清晰是第一位: 多用 BEGIN...END,规规矩矩地缩进,让你的代码像报纸排版一样容易阅读。
  2. 心里时刻装着性能: 能用 EXISTS 就别用 COUNT,能用集合操作(一句SQL处理大量数据)就别动不动就WHILE循环。
  3. 安全第一:WHILE 时,脑子里第一件事就是想想“退出条件是什么?”,严防死守无限循环。
  4. 善用 CASE 表达式: 它能让你在很多场合避免使用复杂的多重 IF 语句或者额外的应用程序代码,把逻辑消化在数据库层面。

这些东西没啥高深的,核心就是多写、多犯错、多总结,写着写着,怎么用更顺手的感觉自然就出来了。