【SQL】小心在循环中声明变量——浅析SQL变量作用域
本文适用: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变量作用域的更多相关文章
- 什么是SQL注入以及mybatis中#{}为什么能防止SQL注入而${}为什么不能防止SQL注入
1.什么是SQL注入 答:SQL注入是通过把SQL命令插入到web表单提交或通过页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL指令. 注入攻击的本质是把用户输入的数据当做代码执行. 举例如: ...
- SQL Server 查询处理中的各个阶段(SQL执行顺序)
SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...
- js中当for循环中有事件要使用循环变量时,变量用var声明和let声明的区别
var 声明一个全局变量,声明的变量会变量提升: let 声明一个局部变量: 当页面加载完后,for循环也结束了,如果用var声明的变量此时也随着for循环的结束而自增到满足结束循环的条件, 此时调用 ...
- 11 tensorflow在tf.while_loop循环(非一般循环)中使用操纵变量该怎么做
代码(操纵全局变量) xiaojie=1 i=tf.constant(0,dtype=tf.int32) batch_len=tf.constant(10,dtype=tf.int32) loop_c ...
- let 和 const 在for 循环中的使用
在ES6 的规范中,多了两个声明变量的关键字: let 和const.初次学习的时候,只记住了 let 声明的变量只在for 的循环体中有效,循环结束后 变量就消失了, 同时const 也可以在for ...
- for双重循环中的结构分离(语法结构问题)
//增加搜索列表 function addSearchList(){ $.get("/mall/h5_get_search_list.html","",func ...
- let和var在for循环中的不同表现
var声明变量: var只有函数作用域,没有块级作用域 //函数作用域的表现 function test(){ var i =10; console.log(i); } test(); console ...
- PHP 循环中临时对象不销毁,会保存到一下循环,这是什么坑。。
在C++中,根本存在这种问题,一个对象或结构体在循环中声明赋值了,再一次循环就自动清空.但是在PHP中这个$monthData在下一次循环却不会重新赋值,而是保留了上一次赋值的值,这样的下一次save ...
- SQL WHILE 循环中的游标 用例,SQL中实现循环操作
--声明两个应用变量 declare @USERID_ int declare @ORGANISEUNITID_ int --声明一个变量计数用,开发中可以忽略 declare @i int=0 -- ...
随机推荐
- pyhton 核心编程 正则表达式习题
方案一 import re #1. 识别下列字符串:“bat,” “bit,” “but,” “hat,” “hit,” 或 “hut” import re def test1(self): bt = ...
- ABP框架系列之十一:(AspNet-Core-ASPNET核心)
Introduction This document describes ASP.NET Core integration for ASP.NET Boilerplate framework. ASP ...
- s33 cobbler自动化安装系统
1. Cobbler介绍 参考链接:http://blog.oldboyedu.com/autoinstall-cobbler/ Cobbler是一个Linux服务器安装的服务,可以通过网络启动(PX ...
- Hibernate 映射及查询
实体类和实体之间的关系:一对多,多对多 数据库设计:e_r 一个实体对象就是一个表格, 如果是1对多的关系,将多方的主键拿到1方做外键. 多对多:重新建立一张新的表格,将双方的主键拿到这里做外键 ...
- STM32F1xx寄存器版库
文件下载链接 https://files.cnblogs.com/files/listenscience/STM32F1xx%28MDK5%292018.10.20.rar
- Unix时间戳转日期时间格式,C#、Java、Python各语言实现!
之前有个Q上好友没事问我,怎么自己写Unix时间戳转日期时间?于是我就顺手写了个C#版本给他!最近想起来,就萌发多写几个语言的版本分享,权当练习思路外加熟悉另外两种语言. 先说转换步骤 先处理年份,从 ...
- 利用ArcGIS-Server瓦片制作离线地图包(*.tpk)_详细流程
1.写在前面 本人是综合了好几个资料才最终制作成功,在这个过程中发现网上好多博客写的步骤不是很详细,因此就把自己的详细制作步骤全部分享出来,可以供需要的小伙伴参考. (1)本文档不讨论tpk文件的详细 ...
- maya2013卸载/安装失败/如何彻底卸载清除干净maya2013注册表和文件的方法
maya2013提示安装未完成,某些产品无法安装该怎样解决呢?一些朋友在win7或者win10系统下安装maya2013失败提示maya2013安装未完成,某些产品无法安装,也有时候想重新安装maya ...
- LCA(最近公共祖先)——Tarjan
什么是最近公共祖先? 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. ...
- ElasticSearch权威指南学习(结构化查询)
请求体查询 简单查询语句(lite)是一种有效的命令行adhoc查询.但是,如果你想要善用搜索,你必须使用请求体查询(request body search)API. 空查询 我们以最简单的 sear ...