本文适用:T-SQL(SQL Server)

先看这个语句:

DECLARE @i INT = 0
WHILE @i < 3 --跑3圈
BEGIN
--每圈都定义一个表变量,并插入一行
DECLARE @t TABLE(Col INT PRIMARY KEY) --主键唯一约束
INSERT @t VALUES (1) SET @i += 1
END

如果你认为这个语句跑起来没问题,那你值得看下去,会避免以后踩到【SQL变量作用域】的坑。

事实上这个语句会报2次“违反了PRIMARY KEY约束…”,原因是@t这个表变量,并不是在每一圈都重新声明一个新的,而是声明1次后就一直沿用,由于该表具有主键约束,所以之后的两圈在插入的时候,由于已经存在相同主键,于是报上述错误。

换成普通变量也一样:

DECLARE @i INT = 0
WHILE @i < 3 --跑3圈
BEGIN
--同样,该变量也只会声明1次,之后沿用
DECLARE @s VARCHAR(20) IF @s IS NULL --所以第1圈会进入该分支
SET @s = 's'
ELSE --之后的圈则进入该分支
SET @s += 's' PRINT @s SET @i += 1
END --执行结果:
s
ss
sss

所以到这里能得出一个结论:

循环中的变量只会声明一次,并在之后一直沿用。

理解这一点很重要,因为这与C#等编译语言非常不同,C#中每一圈声明的变量都相当于重新建一个,与上一圈的毫无关系,但在sql中不能这么思考。

尝试把上面的语句小改一下:

DECLARE @i INT = 0
WHILE @i < 3 --跑3圈
BEGIN
DECLARE @s VARCHAR(20) = 's' --声明并赋值
SET @s += 's' PRINT @s SET @i += 1
END

这次得到的结果会是3个ss,看起来是@s在每一圈得到了重建,那这似乎与上面的结论有悖,不是只会声明1次吗?其实并没有矛盾,而是【declare @s xxx = 's'】相当于【declare @s xxx】+【set @s = 's'】俩语句,声明的确只有1次,但稍后的赋值却是每圈都在进行,相当于每圈一开始都把@s重置为's',所以是这个结果。这也提醒:见到declare @x xxx = xxx时,要看成两个动作

其实这个问题本质上是一个变量作用域问题,只不过SQL中的变量作用域,与C#等语言按语句块划分不一样,SQL的变量作用域是【批】,这一点在MSDN中有说。比如下面的语句:

IF 1 = 2
DECLARE @s VARCHAR(20) SELECT @s

按说declare @s并不会得到执行,@s并没有声明,但事实上这个语句一切正常,不会报错。原因就在于声明语句比较特殊,它并不依赖位置,系统“见到”就算数,所以不管变量在多深的语句块中声明,它在本批接下来的语句中都是有效的。印象中某种SQL的写法是声明在一个区,逻辑在一个区,既然你t-sql的声明具有“提升”这种特点,我认为做成那种比较好,而不是混在逻辑语句中搞特殊。

回到开头的问题,现在我们清楚,虽然变量在循环中声明,但它并不会被多次执行,甚至不是在第1圈的时候执行,而是在某个时机由系统将所有声明统一执行,大概类似C#的静态字段,不管定义在哪里,CLR会确保在使用该类前完成初始化。

至于什么叫一【批】SQL,我没有找到很正式的定义,根据所学,我的理解是:没GO就是一批;有GO的话,GO之间算一批;exec、sp_executesql算一批;ssms中选中执行的部分算一批(前提是选中部分不含上述划分点)。如有错漏还请指正,感谢。

- EOF -

【SQL】小心在循环中声明变量——浅析SQL变量作用域的更多相关文章

  1. 什么是SQL注入以及mybatis中#{}为什么能防止SQL注入而${}为什么不能防止SQL注入

    1.什么是SQL注入 答:SQL注入是通过把SQL命令插入到web表单提交或通过页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL指令. 注入攻击的本质是把用户输入的数据当做代码执行. 举例如: ...

  2. SQL Server 查询处理中的各个阶段(SQL执行顺序)

    SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...

  3. js中当for循环中有事件要使用循环变量时,变量用var声明和let声明的区别

    var 声明一个全局变量,声明的变量会变量提升: let 声明一个局部变量: 当页面加载完后,for循环也结束了,如果用var声明的变量此时也随着for循环的结束而自增到满足结束循环的条件, 此时调用 ...

  4. 11 tensorflow在tf.while_loop循环(非一般循环)中使用操纵变量该怎么做

    代码(操纵全局变量) xiaojie=1 i=tf.constant(0,dtype=tf.int32) batch_len=tf.constant(10,dtype=tf.int32) loop_c ...

  5. let 和 const 在for 循环中的使用

    在ES6 的规范中,多了两个声明变量的关键字: let 和const.初次学习的时候,只记住了 let 声明的变量只在for 的循环体中有效,循环结束后 变量就消失了, 同时const 也可以在for ...

  6. for双重循环中的结构分离(语法结构问题)

    //增加搜索列表 function addSearchList(){ $.get("/mall/h5_get_search_list.html","",func ...

  7. let和var在for循环中的不同表现

    var声明变量: var只有函数作用域,没有块级作用域 //函数作用域的表现 function test(){ var i =10; console.log(i); } test(); console ...

  8. PHP 循环中临时对象不销毁,会保存到一下循环,这是什么坑。。

    在C++中,根本存在这种问题,一个对象或结构体在循环中声明赋值了,再一次循环就自动清空.但是在PHP中这个$monthData在下一次循环却不会重新赋值,而是保留了上一次赋值的值,这样的下一次save ...

  9. SQL WHILE 循环中的游标 用例,SQL中实现循环操作

    --声明两个应用变量 declare @USERID_ int declare @ORGANISEUNITID_ int --声明一个变量计数用,开发中可以忽略 declare @i int=0 -- ...

随机推荐

  1. web集成高德地图

    1.使用高德地图API需到官网添加一个Key,http://lbs.amap.com/dev/key/app 2.页面头引入 <div id="addressMap"> ...

  2. Linux学习---条件预处理的应用

    预处理的使用: ⑴包含头文件 #include ⑵宏定义 #define    替换,不进行语法检查 ①常量宏定义:#define 宏名 (宏体) (加括号为防止不进行语法检查而出现的错误) eg:# ...

  3. MFC程序执行后台操作时不允许操作界面的一种方法

    在使用MFC编写界面程序时,有时候会遇到像点击按钮后,后台进行大量操作后才显示处理结果这种情况,在后台处理过程中,界面不应该被允许做任何操作,这里介绍一种方法. 解决办法 点击按钮后,弹出一个模态对话 ...

  4. windows下多tomcat部署

    两种方式: 第一种是修改tomcat下很多配置文件,且配置环境变量: 第二种不配tomcat环境变量,通过修改server.xml文件即可: 本篇讲解第二种方式: 首先说下如何修改tomcat控制台标 ...

  5. python模块:hmac

    """HMAC (Keyed-Hashing for Message Authentication) Python module. Implements the HMAC ...

  6. clickhouse安装使用文档

    Clickhouse简介 Clickhouse是什么 1. 开源的列存储数据库管理系统 2. 支持线性扩展 3. 简单方便 4. 高可靠性 5. 容错(支持多主机异步复制,可以跨多个数据中心部署. 单 ...

  7. verilog HDL-并行语句之assign

    线网型数据对象: 是verilog hdl常用数据对象之一,起到电路节点之间的互联作用,类似于电路板上的导线. wire是verilog hdl默认的线网型数据对象. 线网型数据对象的读操作在代码任何 ...

  8. 「WC2018」州区划分(FWT)

    「WC2018」州区划分(FWT) 我去弄了一个升级版的博客主题,比以前好看多了.感谢 @Wider 不过我有阅读模式的话不知为何 \(\text{LATEX}\) 不能用,所以我就把这个功能删掉了. ...

  9. 构建一个Gods Eye Android应用程序:第1部分 – 收集已安装的Android应用程序

    首先问候一下我的黑客伙伴们,在之前的Introduction to Amunet 教程中,我们了解到Amunet可能是一个间谍Android应用程序. 我不浪费太多时间因而直入主题. 在本教程中,我们 ...

  10. 【安全狗SRC】抗D设备哪家强?你来!大佬告诉你答案

    上周,安全狗SRC联合SRC部落,携手推出了爆款话题:传统抗D设备 vs 新兴CDN抗D:抗D效果哪个好? 一经发布简直好评如潮,热评无数,四方雷动(?)原帖在此,错过的吃瓜表哥们可以再围观一下~ht ...