T-SQL常见基础疑点问答总结(1)
来源:互联网 发布:蔚华 知乎 编辑:程序博客网 时间:2024/06/05 06:26
贴子只代表个人看法和观点,仅作交流使用,如有错误,敬请指正。
(1)字串变量当数据库对象用
--为什么我执行下面语句报错
说明:
在这二个存储过程中,@table,@field,@value都被定义为varchar
第一个实际上执行的是两个变量的比较,它的作用相当于
IF @field=@value
SELECT * FROM tb
语法未错,意思上却大错特错
第二个实际上执行的是
SELECT * FROM 一个字串 WHERE v=@value
如何能从一个字串中查询结果集呢?错误的把字串当成表对象来理解.
请记住@table是个表名,它是个字段,而非表对象,不是object
可以用EXEC执行动态语句来解决,比如
(2)为什么我在执行一个批语句(可能是存储过程,
也可能是个FUNCTION,也可能只是几条语句的组合)时,
提示错误,我照着提示的错误,检查,但是那里没有报错啊
--=**********************************************
比如,上面的第二个存储过程,@table明明是存储过程的输入参数,它为什么提示我@table未定义
在上面,我已经讲了这句为什么出错的原因,
当然@table如果是表变量的话,那么那句select是不会有问题的,
但表变量不能用做输入参数。但它为什么这样提示呢?
这与sql内部机制有关,sql查询语句执行前先由命令解析器进行语法检查,如果语法检查未通过,
会扔出错误信息(通常这里的提示是精确的),
当语法检查通过,则将其编译为可执行的内部格式(查询树),
而非语法错误时,因为是执行时报错,执行期间是内部格式代码,只能扔出个大致错误信息.
了解了这一点,当您的sql语句报错后,先检查是否语法错误,
如果不是,那么需要仔细检查了,因为按着错误提示去找,很有可能兜圈子。
--=**********************************************
(3)为什么我明明定义了@n变量,却提示我变量不存在?
上面有三条语句:
rows 语句
1 定义两变量
2 给@field赋值
3 执行动态语句
那么这三条语句,在执行时进行语法检查和编译,大至为:
这是编译后要执行三步操作,它们在一进程空间中进行编译。
随着语句的运行,exec内部的语句被购造成'select @n=count(*) from tb where v=''a'''
这时,EXEC要执行的这条语句,继续要被命令解析器进行检查和编译,
它的编译内存空间与外部这个批不同,术语我可能表达不太清楚,
只想说明一点,动态语句在执行过程中才被首次编译,所以在这个空间中,提示@n未定义.
解决方式,sqlserver为我们提供了sp_executesql来完成这个操作。
上面要完成的操作,可以改写为:
(4)以下语句为什么提示我临时表不存在,明明我生成了临时表的
--=**********************************************
说明:
在上面解释@n为何未定义时说了关于编译时进程内空间的问题。
我们再联合局部临时表的生名周期,局部临时表在当间会话进程中有效
EXEC内部的语句是新开辟的一个进程空间执行的,所以当它执行完毕后,
生命周期结束,因此外部无法再访问
那为什么下面的又可以呢?
这个很明显,上面执行了三行,在一个批中,直到PRINT 'aa'结束后,
进程结束,#临时表才会被销毁,因为它的生命周期在整个会话中.
所以当EXEC执行时它仍存在
(5)为什么我给一个变量赋值,得到的不是我想要的,即为什么@v的值不是a,而是z
说明
SELECT 变量=字段 FROM tb
这种赋值与SET赋值的主要区别:
a, SET赋值,一次只能给一个变量赋值,而SELECT 则可多个
b, SET赋值语义更明确,是赋值。而SELECT 可能是赋值也可能是数据查询
c, 最重要的一个,SELECT 可以从表中取值,而SET不能。
说到这有人会说 SET @v=(SELECT TOP 1 v FROM tb) 也可以,
这样确实可以,但实际上它还是调用SELECT来完成.
d, SELECT赋值时是滚动赋值(或许用词不科学),我来说明一下我的'滚动赋值'指的什么
即,当SELECT v FROM tb有多个结果时,
SELECT @v=v FROM tb 在产生结果集的过程中,每得到一条记录,
@v都被赋一次值,也就是说,语句会滚动结果集,每次都对@v赋值。
这样,也就产生了递规查询变量:"
这样就解释了为什么您的语句得到的结果为z而不是a
try:
(6)我要获得tb上新插入记录的标识值,为什么我的@@identity全局函数取的值不对?
说明:
@@identity是个全局函数,返回当前会话所有作用域最后产生的标识值(执行SELECT @@identity之前的最后)
我们可以看到ta上有insert触发器 t,触发器里对ta进行了insert,所以这里得到的是ta中新增的标识值
建议使用 SCOPE_IDENTITY
(7)为什么我执行distinct去重复后没有效果?如下,我想得到v值不同的记录,也即结果集中每个v值只出现一次
出现这个意外,您需要注意两点:
1,DISTINCT 有效范围是后面的字段列表,而非紧告其的第一个字段
2,为什么DISTINCT 会有这么个奇怪的特点?这与二维关系统有关
就说tb中v=a的记录id有1,7
DISTINCT v,id 如果仅对v有效,那么引id有1,7,在一行一列中如何填充数据?是用1还是用7?
您太难为它了,因为它不知道如何为您将id填充到结果集中.
其实这个问题就是常见的同组取一条,那么你需要指定多一个条件,
每组(v相同的记录为一组)取id最大的或最小的,那么这样的语句,写的很多,论坛上问的也很多了,我就不再累赘了。
先整到这,明天继续
P.S:帮你整理了一下排版,希望没有曲解你的意思哈(hery2002):)
- SQL code
- --建立测试环境IF object_id('tb') IS NOT NULL DROP TABLE tbGOCREATE TABLE tb(id INT IDENTITY(1,1),v VARCHAR(10))GOINSERT tb SELECT 'a'UNION ALL SELECT 'b'INSERT tb SELECT 'x'UNION ALL SELECT 'z'GO
(1)字串变量当数据库对象用
- SQL code
- --=**********************************************--为什么我执行下面的语句选不到正确数据CREATE PROC p(@field VARCHAR(10),--字段名@value VARCHAR(10) --值)AS SELECT * FROM tb WHERE @field=@valueGOEXEC p 'v','a'GODROP PROC pGO
--为什么我执行下面语句报错
- SQL code
- CREATE PROC p(@table VARCHAR(10),--表名@value VARCHAR(10) --值)AS SELECT * FROM @table WHERE v=@value
说明:
在这二个存储过程中,@table,@field,@value都被定义为varchar
第一个实际上执行的是两个变量的比较,它的作用相当于
IF @field=@value
SELECT * FROM tb
语法未错,意思上却大错特错
第二个实际上执行的是
SELECT * FROM 一个字串 WHERE v=@value
如何能从一个字串中查询结果集呢?错误的把字串当成表对象来理解.
请记住@table是个表名,它是个字段,而非表对象,不是object
可以用EXEC执行动态语句来解决,比如
- SQL code
- EXEC('SELECT * FROM [' + @table + '] WHERE v=''' + @value + '''')
(2)为什么我在执行一个批语句(可能是存储过程,
也可能是个FUNCTION,也可能只是几条语句的组合)时,
提示错误,我照着提示的错误,检查,但是那里没有报错啊
--=**********************************************
比如,上面的第二个存储过程,@table明明是存储过程的输入参数,它为什么提示我@table未定义
在上面,我已经讲了这句为什么出错的原因,
当然@table如果是表变量的话,那么那句select是不会有问题的,
但表变量不能用做输入参数。但它为什么这样提示呢?
这与sql内部机制有关,sql查询语句执行前先由命令解析器进行语法检查,如果语法检查未通过,
会扔出错误信息(通常这里的提示是精确的),
当语法检查通过,则将其编译为可执行的内部格式(查询树),
而非语法错误时,因为是执行时报错,执行期间是内部格式代码,只能扔出个大致错误信息.
了解了这一点,当您的sql语句报错后,先检查是否语法错误,
如果不是,那么需要仔细检查了,因为按着错误提示去找,很有可能兜圈子。
--=**********************************************
(3)为什么我明明定义了@n变量,却提示我变量不存在?
- SQL code
- --=**********************************************DECLARE @field VARCHAR(10),@n INTSET @field='v'EXEC('SELECT @n=COUNT(*) FROM tb WHERE [' + @field + ']=''a''')
上面有三条语句:
rows 语句
1 定义两变量
2 给@field赋值
3 执行动态语句
那么这三条语句,在执行时进行语法检查和编译,大至为:
- SQL code
- DECLARESETEXEC
这是编译后要执行三步操作,它们在一进程空间中进行编译。
随着语句的运行,exec内部的语句被购造成'select @n=count(*) from tb where v=''a'''
这时,EXEC要执行的这条语句,继续要被命令解析器进行检查和编译,
它的编译内存空间与外部这个批不同,术语我可能表达不太清楚,
只想说明一点,动态语句在执行过程中才被首次编译,所以在这个空间中,提示@n未定义.
解决方式,sqlserver为我们提供了sp_executesql来完成这个操作。
上面要完成的操作,可以改写为:
- SQL code
- DECLARE @field VARCHAR(10),@n INT,@sql NVARCHAR(4000)SET @field='v'SET @sql=N'SELECT @n=COUNT(*) FROM tb WHERE [' + @field + ']=''a'''EXEC sp_executeSQL @sql,N'@n INT OUTPUT',@n OUTPUTSELECT @n--=**********************************************
(4)以下语句为什么提示我临时表不存在,明明我生成了临时表的
--=**********************************************
- SQL code
- EXEC('SELECT * INTO # FROM tb')SELECT * FROM #
说明:
在上面解释@n为何未定义时说了关于编译时进程内空间的问题。
我们再联合局部临时表的生名周期,局部临时表在当间会话进程中有效
EXEC内部的语句是新开辟的一个进程空间执行的,所以当它执行完毕后,
生命周期结束,因此外部无法再访问
那为什么下面的又可以呢?
- SQL code
- SELECT * INTO # FROM tbEXEC('SELECT * FROM #')PRINT 'aa'DROP TABLE #--=********************************************
这个很明显,上面执行了三行,在一个批中,直到PRINT 'aa'结束后,
进程结束,#临时表才会被销毁,因为它的生命周期在整个会话中.
所以当EXEC执行时它仍存在
(5)为什么我给一个变量赋值,得到的不是我想要的,即为什么@v的值不是a,而是z
- SQL code
- --=**********************************************DECLARE @v VARCHAR(10)SELECT @v=v FROM tbSELECT @v
说明
SELECT 变量=字段 FROM tb
这种赋值与SET赋值的主要区别:
a, SET赋值,一次只能给一个变量赋值,而SELECT 则可多个
b, SET赋值语义更明确,是赋值。而SELECT 可能是赋值也可能是数据查询
c, 最重要的一个,SELECT 可以从表中取值,而SET不能。
说到这有人会说 SET @v=(SELECT TOP 1 v FROM tb) 也可以,
这样确实可以,但实际上它还是调用SELECT来完成.
d, SELECT赋值时是滚动赋值(或许用词不科学),我来说明一下我的'滚动赋值'指的什么
即,当SELECT v FROM tb有多个结果时,
SELECT @v=v FROM tb 在产生结果集的过程中,每得到一条记录,
@v都被赋一次值,也就是说,语句会滚动结果集,每次都对@v赋值。
这样,也就产生了递规查询变量:"
- SQL code
- SELECT @v=@v+v FROM tb
这样就解释了为什么您的语句得到的结果为z而不是a
try:
- SQL code
- SELECT TOP 3 @v=v FROM tb --滚动到结果集的第三行,或者说,结果集中只有三行,取最后一行--=**********************************************
(6)我要获得tb上新插入记录的标识值,为什么我的@@identity全局函数取的值不对?
- SQL code
- --=**********************************************IF object_id('ta') IS NOT NULL DROP TABLE taCREATE TABLE ta(id INT IDENTITY(1,1),b_id INT,logTime DATETIME NOT NULL DEFAULT GETDATE())GOCREATE TRIGGER t ON tbFOR INSERTAS INSERT ta (b_id) SELECT id FROM INSERTEDGOINSERT tb SELECT 'dd'SELECT @@identity --为什么这里得到的不是5,而是1?
说明:
@@identity是个全局函数,返回当前会话所有作用域最后产生的标识值(执行SELECT @@identity之前的最后)
我们可以看到ta上有insert触发器 t,触发器里对ta进行了insert,所以这里得到的是ta中新增的标识值
建议使用 SCOPE_IDENTITY
- SQL code
- INSERT tb SELECT 'ww'SELECT SCOPE_IDENTITY() --这里得到6,因为它返回当前会话,当前作用域最后产生的标识值--=**********************************************
(7)为什么我执行distinct去重复后没有效果?如下,我想得到v值不同的记录,也即结果集中每个v值只出现一次
- SQL code
- --=**********************************************INSERT tb SELECT 'a'UNION ALL SELECT 'b'SELECT * FROM tb -- 8 rowsSELECT DISTINCT v,id FROM tb --8 rows--=**********************************************
出现这个意外,您需要注意两点:
1,DISTINCT 有效范围是后面的字段列表,而非紧告其的第一个字段
2,为什么DISTINCT 会有这么个奇怪的特点?这与二维关系统有关
就说tb中v=a的记录id有1,7
DISTINCT v,id 如果仅对v有效,那么引id有1,7,在一行一列中如何填充数据?是用1还是用7?
您太难为它了,因为它不知道如何为您将id填充到结果集中.
其实这个问题就是常见的同组取一条,那么你需要指定多一个条件,
每组(v相同的记录为一组)取id最大的或最小的,那么这样的语句,写的很多,论坛上问的也很多了,我就不再累赘了。
先整到这,明天继续
P.S:帮你整理了一下排版,希望没有曲解你的意思哈(hery2002):)
- T-SQL常见基础疑点问答总结
- T-SQL常见基础疑点问答总结(1)
- T-SQL常见基础疑点问答总结(1)
- T-SQL常见基础疑点问答整理(2)
- Spring基础问答总结
- java基础疑点分析
- python学习疑点总结
- 面试问题疑点总结
- SEO常见问答—基础问题
- T-SQL程序设计基础
- T-SQL查询基础
- T-SQL程序设计基础
- T-SQL程序设计基础
- T-SQL 程序设计基础
- T-SQL程序设计基础
- t-sql编程基础
- T-SQL语言基础
- T-sql 语言基础
- 80后做到了,你就成熟了!
- c++相关概念
- 網頁刷新
- Hitwise 6月旅游类网站分类报告
- ASP.NET中实现MSN通知消息功能
- T-SQL常见基础疑点问答总结(1)
- JBoss部署Web 程序
- Visual Studio 2008下载地址汇总及相关注册破解方法
- C++模板元程序 Template Metaprograms
- 不变应万变
- SQLServer数据库设计规范
- Berkeley DB Windows应用
- CTI 技术步入
- 水晶报表中数字转大写金额的源码