【问题标题】:variable for query string查询字符串的变量
【发布时间】:2017-04-13 06:25:24
【问题描述】:

由于等级较高的论坛用户建议我将我的问题作为与此相关的新问题发布:declare variable for query string

我在这里使用动态查询是因为我希望能够使用变量(我在涉及参数嗅探的实验中需要它)。我的查询如下所示:

DECLARE @i NVARCHAR(10)
SET @i = 'POL'

DECLARE @sql VARCHAR(MAX)
SET @sql = 'SELECT

a.something1,
b.something2,
c.something3

FROM
aaa a WITH(NOLOCK)
LEFT JOIN bbb b WITH(NOLOCK) ON....
LEFT JOIN ccc c WITH(NOLOCK) ON....

WHERE
a.somethingelse = ''aa''
AND
((a.entirelysomethingelse=''aaa'') OR (a.entirelysomethingelse=''aaaa''))
AND
b.anotherdifferentsomething != 41
AND 
c.yetanotherdifferentthing LIKE(''%@%'')
AND
c.datafromvariablewannabe = (@i)
GROUP BY
...
ORDER BY
...'
EXECUTE (@SQL)

当我想执行查询时,出现如下错误:

'Msg 137, Level 15, State 2, Line 28
Must declare the scalar variable "@i".'

当我将变量@i 的声明移到@sql 中时,它就可以工作了。仍然我不认为这是我想要的。我做错了什么还是必须看起来像这样?:

DECLARE @sql VARCHAR(MAX)
SET @sql = '

DECLARE @i NVARCHAR(10)
SET @i = 'POL'

SELECT

a.something1,
b.something2,
c.something3

FROM
aaa a WITH(NOLOCK)
LEFT JOIN bbb b WITH(NOLOCK) ON....
LEFT JOIN ccc c WITH(NOLOCK) ON....

WHERE
a.somethingelse = ''aa''
AND
((a.entirelysomethingelse=''aaa'') OR (a.entirelysomethingelse=''aaaa''))
AND
b.anotherdifferentsomething != 41
AND 
c.yetanotherdifferentthing LIKE(''%@%'')
AND
c.datafromvariablewannabe = (@i)
GROUP BY
...
ORDER BY
...'
EXECUTE (@SQL)

我的代码中是否有任何错误,所以我收到 MSG137 错误,或者这只是我想在这里做的不可能完成的任务。

我不想从此查询创建存储过程。我希望能够使用变量但不依赖存储过程。

对于涉及我之前在超链接问题中的问题的问题,我向管理员/论坛用户道歉。

【问题讨论】:

  • 这是什么意思When I moved declaration of variable @i into the @sql then it sorta works. Still I don't think It's what I wanted
  • 变量的作用域是一个批次,而 exec 在单独的批次中运行,所以你必须将它包含在同一个 @sql 中
  • 我的意思是我得到了很好的结果,但获得这些结果的方法不是我正在寻找的方法。我不知道我必须“打破”查询字符串(正如@mortb 所建议的那样),这样我的变量才能被“加载”?在那里。
  • 离题但值得一读:the downside of using NoLock。我相信您有自己的理由,但值得强调的是,NoLock 可以返回重复项、已删除记录以及您在生产查询中最可能不想要的其他内容。有关将变量传递到动态 SQL 的替代方法,请参阅此 question
  • 我知道 nolock 有它的缺点,但在这里使用它是我的主管的命令,在这种情况下它不会造成任何伤害。不过还是有用的评论。

标签: sql sql-server tsql dynamic-sql


【解决方案1】:

您需要“断开”字符串。 @i 值在执行字符串的范围内不可用,因此您需要使其成为字符串的一部分。 像这样:

DECLARE @tmp NVARCHAR(10)
SET @tmp = 'POL'

DECLARE @sql VARCHAR(MAX)
SET @sql = 'SELECT

a.something1,
b.something2,
c.something3

FROM
aaa a WITH(NOLOCK)
LEFT JOIN bbb b WITH(NOLOCK) ON....
LEFT JOIN ccc c WITH(NOLOCK) ON....

WHERE
a.somethingelse = ''aa''
AND
((a.entirelysomethingelse=''aaa'') OR (a.entirelysomethingelse=''aaaa''))
AND
b.anotherdifferentsomething != 41
AND 
c.yetanotherdifferentthing LIKE(''%@%'')
AND
c.datafromvariablewannabe = (@i)
GROUP BY
...
ORDER BY
...
OPTION(OPTIMIZE FOR(@i = ''' + @tmp + '''))'
EXEC SP_EXECUTESQL @SQL, N'@i NVARCHAR(255)', @i = @tmp

【讨论】:

  • 这是我正在寻找的答案。我仍然需要在@i 变量中添加引号。所以它必须看起来像这样:DECLARE @i nvARCHAR(10) SET @i ='''POL'''
  • 还有一件事。如果我想在这里添加option (optimize for @i) 我应该在哪里或如何做?在这种特殊情况下。
  • 在 ORDER BY Ref: msdn.microsoft.com/en-us/library/ms181714.aspx 之后是一个查询语句(你应该把它放在你的 SELECT 语句的最后)
  • (optimize for ('+ @i +') 说明:你已经将值'POL'赋给变量@i。'+@i +'会将@i的值赋给你的字符串
  • 哦,对不起。我编辑了 SQL 语句。您可能应该使用 SP_EXECUTESQL 而不是 EXEC。然后你可以在你的sql语句中输入真实的参数
【解决方案2】:

优化 @variable 的工作解决方案如下所示:

DECLARE @i NVARCHAR(255)
declare @tmp nvarchar(255)
SET @tmp = 'POL'

DECLARE @SQL nVARCHAR(MAX)
SET @sql = 'SELECT

a.something1,
b.something2,
c.something3

FROM
aaa a WITH(NOLOCK)
LEFT JOIN bbb b WITH(NOLOCK) ON....
LEFT JOIN ccc c WITH(NOLOCK) ON....

WHERE
a.somethingelse = ''aa''
AND
((a.entirelysomethingelse=''aaa'') 
OR 
(a.entirelysomethingelse=''aaaa''))
AND
b.anotherdifferentsomething != 41
AND 
c.yetanotherdifferentthing LIKE(''%@%'')
AND
c.datafromvariablewannabe = (@i)

GROUP BY
...
ORDER BY
...

OPTION(OPTIMIZE FOR(@i = ''' + @tmp + '''))'

EXECUTE SP_EXECUTESQL @SQL, N'@i NVARCHAR(255)', @i = @tmp

如果不声明变量 @temp,它就无法工作。之后我遇到了 Exec 的问题。我不能像@mortb 建议的那样使用SP_EXECUTESQL。我必须在SP_EXECUTESQL 之前使用EXECUTE,并且我在该帖子Why do I get "Procedure expects parameter '@statement' of type 'ntext/nchar/nvarchar'." when I try to use sp_executesql? 中发现了一些与下一个错误相关的信息。我必须更改查询变量的类型并去掉括号。

我仍然想知道为什么我必须创建新变量,并且对这部分代码有更多了解会有所帮助:

OPTION(OPTIMIZE FOR(@i = ''' + @tmp + '''))'

EXECUTE SP_EXECUTESQL @SQL, N'@i NVARCHAR(255)', @i = @tmp

它现在有效,但我想知道为什么以及如何?

【讨论】:

  • SP_EXECUTESQL 是一个内置的存储过程,它接受一个 SQL 字符串并使用指定的参数运行它。当您从字符串创建 sql 时,一开始会令人困惑。 N'@i NVARCHAR(255)', @i = @tmp 部分告诉存储过程,在 sql 文本中将有一个名为 @i 的参数,并且 @i(当文本执行时)应该具有相同的值 @tmp 在文本“外部” . OPTION(OPTIMIZE FOR(@i = ''' + @tmp + '''))' 部分表示文本将包含 @tmp 的值(当文本输入SP_EXECUTESQL 时,它不再是变量)。
  • 给它一些时间,你会明白的:)
猜你喜欢
  • 2014-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多