日期:2014-5-19 浏览次数:21220次 点赞次数:20

T-SQL常见基础疑点问答总结(1)
贴子只代表个人看法和观点,仅作交流使用,如有错误,敬请指正。

SQL code
--建立测试环境
IF object_id('tb') IS NOT NULL
    DROP TABLE tb
GO

CREATE TABLE tb(id INT IDENTITY(1,1),v VARCHAR(10))
GO
INSERT 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=@value
GO

EXEC p 'v','a'
GO
DROP PROC p
GO


--为什么我执行下面语句报错
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 INT
SET @field='v'
EXEC('SELECT @n=COUNT(*) FROM tb WHERE [' + @field + ']=''a''')


上面有三条语句:
rows 语句
1 定义两变量
2 给@field赋值
3 执行动态语句

那么这三条语句,在执行时进行语法检查和编译,大至为:
SQL code

DECLARE
SET
EXEC


这是编译后要执行三步操作,它们在一进程空间中进行编译。
随着语句的运行,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 OUTPUT
SELECT @n
--=**********************************************




(4)以下语句为什么提示我临时表不存在,明明我生成了临时表的
--=**********************************************
SQL code

EXEC('SELECT * INTO # FROM tb')
SELECT * FROM #



说明: