【问题标题】:dynamic SQL query giving type error动态 SQL 查询给出类型错误
【发布时间】:2013-04-07 17:11:38
【问题描述】:

我正在尝试通过构建动态查询并将其存储在变量中并执行该变量来编写一个简单的 SP。

我目前收到以下错误:

消息 206,第 16 级,状态 2,第 16 行
操作数类型冲突:datetime2 与 float 不兼容

对于以下代码:

DECLARE 
@table_Num
@1 varchar(100) = 'boo',
@2 int =2,
@3 varchar(100) ='default',
@4 varchar(50) = NULL,
@5 int =NULL,
@6 float =12,
@7 datetime2(0) ='1970-01-01 00:00:00',
@8 datetime2(0)='1970-01-01 00:00:00',
@9 varchar(50)='',
@10 varchar(50)=NULL,
@11 decimal(18,0)=0000000000000,
@12 int =999999

DECLARE @SQLString NVARCHAR(MAX)

SET @SQLString = 'INSERT INTO abc_'+@table_Num+'(col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12) 
VALUES ('+@1+',2,'+@3+','+@4+','+@5+','+@6+','+@7+','+@8+','+@9+','+@10+','+@11+','+@12+')'

EXEC (@SQLString)

据我所知,变量的类型与表列类型相同。有什么想法吗?

【问题讨论】:

  • 你能把table_1的定义也包括进来吗?
  • 这是构建动态 SQL 字符串的最糟糕的方法。你能解释一下为什么它首先需要动态 SQL 吗?
  • @AaronBertrand 好点子。我只是想当然地认为这是动态 SQL 的原因。
  • 我认为它必须是动态 sql,因为我需要将参数放在插入语句中
  • @Fearghal 您可以直接在插入语句中调用参数/变量。无需使用动态 SQL,除非您每次都构建不同的插入语句(例如省略某些参数等)

标签: sql sql-server sql-server-2008 tsql dynamic


【解决方案1】:

要动态创建正确的插入语句,您至少必须:

  1. 将变量转换为字符串:cast(@2 as varchar(11))
  2. 字符串中的双撇号:replace(@1, '''', '''''')
  3. 用撇号包装字符串(包括日期作为字符串):'''' + replace(@1, '''', '''''') + ''''
  4. 处理空值,因为连接空值会产生空值:coalesce(cast(@5 as varchar(11)), 'null')

这看起来是不是可怕

正如其他人所提到的,这是运行动态 SQL 的最糟糕的方式,并且会打开一个名为 SQL 注入的主要安全漏洞。如果这需要是动态的,请改用带有类型参数的sp_executesql(谢谢@JeffRosenberg)。但是,这个简单的插入也是一个简单的示例,根本不需要动态 SQL(谢谢,@AaronBertrand)。只需按原样运行命令:

INSERT INTO table_1 (col1, col2, /* ... */ col12)
VALUES (@1, @2, /* ... */ @12)

【讨论】:

  • 大家好,对不起,我在我的例子中犯了一个错误,它可能不会产生太大的差异,但这是差异......我希望能够声明表格名称被动态插入,我已经相应地更改了原始问题代码
【解决方案2】:

我猜这与您使用引号有关,但很难理解。我建议使用EXEC sp_executesql 并将您的参数作为实际参数传递。像这样的语句更容易做,而且更安全。

(根据 TimLehner 的评论编辑)

DECLARE @SQLString NVARCHAR(MAX), @ParamString NVARCHAR(MAX), @TableName sysname;
SET @TableName = N'abc_' + REPLACE(@table_num, '''', ''''''); --Escape apostrophes

IF OBJECT_ID(@TableName) IS NOT NULL
BEGIN
  SET @SQLString = 
    N'INSERT INTO ' + QUOTENAME(@TableName) +
    N' (col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12)
       VALUES (@1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12)';
  SET @ParamString = N'@1 varchar(100), @2 int, @3 varchar(100), ... , @12 int)';

  EXEC sp_executesql @SQLString, @ParamString, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12
END

Read about sp_execultesql on MSDN here.

【讨论】:

  • +1 sp_executesql is absolutely the way to execute dynamic SQL,尤其是当大多数参数都可以强类型化时(而不是将它们作为一个大字符串粘在一起)。
  • @AaronBertrand 感谢您的编辑。你是对的,括号是一个错误。
  • 伙计们,我修改了我原来的问题 - 我想动态地声明表名,这在示例中没有!这有什么改变吗 - 例如'....table_'+@table_num+'(....
  • 这仍然适用于 sp_executesql。我已经更新了上面的答案;这只是一个很小的变化。您需要将表名连接到 SQL 命令中,但其他所有内容仍应作为参数传递。
  • 再次感谢。我复制了这个,添加了一些调整以使其工作-'在最后一行的末尾,声明 table_num....得到错误:必须声明标量变量@1。
【解决方案3】:

您的字符串连接存在转换问题。您需要将参数转换为 varchar 以构建字符串。

cast(@7 as varchar(50)).

FWIW,我认为您应该重新考虑如何构建查询。如果可能,请避免使用 EXEC()。您将遇到此解决方案的各种问题。您可能还会遇到 SQL 注入问题。如果黑客设法发送 int SQL 代码,您的插入查询可能会被重写。参数@1

【讨论】:

  • 谢谢,我尝试了 varchar 方法,我想我遇到了另一个问题,但我又检查了一遍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多