在SQL Server 2017中的IN子句中使用变量

时间:2018-09-27 21:06:49

标签: sql sql-server tsql

我试图在IN子句中传递一个变量,并在谷歌搜索解决方案中进行了以下操作,但出现错误。任何帮助和/或指示,将不胜感激。

首先列出我的代码,然后在下面找到错误。

我的代码:

DECLARE @OwnerSysID varchar(500)
SET @OwnerSysID = '3, 3476, 3511'

DECLARE @sql nvarchar(4000)
SET @sql = 'Select AllPosiable.UserID, AllPosiable.AgentSysID, 0' + 
            'From (' +
                'Select AgentSysID, UserID' + 
                'From (' +
                    'Select AGT_Agent.AgentSysID' +
                    'From AGT_Agent' +
                    'Where OwnerSysID IN ('+@OwnerSysID+')' +
                    'AND AgentName NOT LIKE %Texas State Low Cost%' +
                    'AND AgentName NOT LIKE %TSLC%' +
                    ') as AgentIDs ' +
                'Full Join (' +
                    'Select DISTINCT IW_UserAgentRelationship.UserID' +
                    'From AGT_Agent' +
                    'Left Join IW_UserAgentRelationship' +
                    'On IW_UserAgentRelationship.AgentID = AGT_Agent.AgentSysID' +
                    'Where OwnerSysID IN ('+@OwnerSysID+')' +
                    'And IW_UserAgentRelationship.isDefault = 1' +
                    'AND AgentName NOT LIKE %Texas State Low Cost%' + 
                    'AND AgentName NOT LIKE %TSLC%' +
                ') As UserIDs On 1=1' +
            ') as AllPosiable' +
            'Left Join IW_UserAgentRelationship' +
            'On IW_UserAgentRelationship.AgentID = AllPosiable.AgentSysID' +
            'And IW_UserAgentRelationship.UserID = AllPosiable.UserID' +
            'Where IW_UserAgentRelationship.AgentID Is NULL' 

EXEC sp_executesql @sql;

错误:

Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'Select'.
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'OwnerSysID'.
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'Join'.

谢谢。

3 个答案:

答案 0 :(得分:3)

这没有将变量传递给IN()子句。这是直接注入易受攻击的字符串连接,因此请非常小心使用它。

但是问题在这里:

SET @sql = 'Select AllPosiable.UserID, AllPosiable.AgentSysID, 0' + 
            'From (' +

第一行结尾0之后的字符串中没有空格,第二行开头的单词From之前没有空格,并且它们之间的换行符不属于结果字符串。您最终会得到以下结果:

Select AllPosiable.UserID, AllPosiable.AgentSysID, 0From (...

尝试一下:

SET @sql = 'Select AllPosiable.UserID, AllPosiable.AgentSysID, 0' + 
            ' From (' +

并确保在每个换行符处都在执行类似的操作。

此外,您需要查看以下摘录:

'AND AgentName NOT LIKE %Texas State Low Cost%' +

表达式%Texas State Low Cost%不会成为结果SQL代码中字符串文字的一部分。您需要这样做:

 'AND AgentName NOT LIKE ''%Texas State Low Cost%''' +

并再次确保SQL语句中的所有字符串文字都得到类似的处理。

答案 1 :(得分:2)

查询中有多个问题。试试这个:

SET @sql = N'  -- strings should nvarchar for sp_executesql
Select AllPosiable.UserID, AllPosiable.AgentSysID, 0
From (Select AgentSysID, UserID
      From (Select AGT_Agent.AgentSysID
            From AGT_Agent
            Where OwnerSysID IN (@OwnerSysID) and
                  AgentName NOT LIKE ''%Texas State Low Cost%'' and
                  AgentName NOT LIKE ''%TSLC%''
           ) AgentIDs Full Join
           (Select DISTINCT IW_UserAgentRelationship.UserID
            From AGT_Agent Left Join
                 IW_UserAgentRelationship
                 On IW_UserAgentRelationship.AgentID = AGT_Agent.AgentSysID
            Where OwnerSysID IN (@OwnerSysID) and
                  IW_UserAgentRelationship.isDefault = 1 and
                  AgentName NOT LIKE ''%Texas State Low Cost%'' and
                  AgentName NOT LIKE ''%TSLC%''
           ) UserIDs
           On 1 = 1
      ) AllPosiable Left Join
      IW_UserAgentRelationship
      On IW_UserAgentRelationship.AgentID = AllPosiable.AgentSysID and
         IW_UserAgentRelationship.UserID = AllPosiable.UserID
Where IW_UserAgentRelationship.AgentID Is NULL
' ;

SET @sql = replace(@sql, n'@OwnerSysID', @OwnerSysID)

EXEC sp_executesql @sql;

您的基本问题是不了解字符串可以跨越SQL Server中的多行。这意味着您不必将一堆不同的线连接在一起。在您的情况下,您将关键字合并在一起,因此SQL是无法理解的。如果您已经打印出@sql,这将是显而易见的。

另一个问题是like模式没有被单引号引起来。在字符串中,这些需要重复。

我没有看查询本身的逻辑,但是我真的怀疑full join是否必要。

答案 2 :(得分:1)

我猜您正在采用动态sql路由,只是因为您需要将@OwnerSysID作为逗号分隔的值传递给IN()

如果是这种情况,请使用TVF传递带有逗号分隔值的变量,而不使用动态sql。

我在this post

中提到了这种方法

基本上,您需要一个表值函数,该函数将处理@OwnerSysID并将其值转换为行(借助XML技术),然后在IN()

中使用它

所需功能:

CREATE FUNCTION Split(@String VARCHAR(MAX))
RETURNS TABLE 
AS
RETURN 
(
    SELECT LTRIM(RTRIM(m.n.value('.[1]','varchar(8000)'))) MyString
    FROM (
        SELECT CAST('<XMLRoot><RowData>' + REPLACE(@String,',','</RowData><RowData>') + '</RowData></XMLRoot>' AS XML) MyString
    ) D
    CROSS APPLY MyString.nodes('/XMLRoot/RowData')m(n)
)

然后您可以像这样在查询中使用它:

OwnerSysID IN (SELECT * FROM dbo.Split(@OwnerSysID))

使用这种方法,您不需要动态sql,只需一个普通的查询就足够了。