预先格式化的 varchar 变量在选择查询中不起作用

Fra*_*sco 1 sql-server-2008

我正在尝试运行一个选择查询,该查询在 where 子句中接收@Sites 变量。目前的查询返回空,因为以下行

AND S.[Name] IN (@Sites)
Run Code Online (Sandbox Code Playgroud)

未按预期处理。下面是查询的摘录:

SET QUOTED_IDENTIFIER OFF

DECLARE @Month int
, @Sites varchar(MAX) = null

SET @Month = 8
SET @Sites = 'Bayside;Collaborative'
SET @Sites = "'" + REPLACE(@Sites,';',"','") + "'"


SELECT 
    CV.[Id] AS Id
    , ...
    , S.[Name] AS [Site]
    , ...
  FROM 
    [CalendarViews] CV
    INNER JOIN [Sites] S ON S.[Id] = CV.[SiteId]
  WHERE
    MONTH(CV.[DateAllocation]) = @Month
    AND S.[Name] IN (@Sites)
  ORDER BY 
     S.[Name]
Run Code Online (Sandbox Code Playgroud)

当我运行查询替换

AND S.[Name] IN (@Sites)
Run Code Online (Sandbox Code Playgroud)

为了

AND S.[Name] IN ('Bayside','Collaborative')
Run Code Online (Sandbox Code Playgroud)

查询按预期工作。有人可以告诉我我缺少什么吗?谢谢你。

Aar*_*and 7

@Sites是一个字符串,而不是一个数组。所以你实际上是在寻找:

S.[Name] IN ('''Bayside'',''Collaborative''')
-------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- this is one single string
Run Code Online (Sandbox Code Playgroud)

我强烈建议,不要传入逗号或分号分隔的字符串,而是传入表值参数。首先,在您的数据库中创建它:

CREATE TYPE dbo.Sites AS TABLE(Site NVARCHAR(255));
Run Code Online (Sandbox Code Playgroud)

然后你的程序会说(省略不太相关的位):

ALTER PROCEDURE dbo.ProcedureName
  @Sites dbo.Sites READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT ...
  FROM dbo.Sites AS s
  INNER JOIN @Sites AS tvp
    ON s.[Name] = tvp.Site;
END
GO
Run Code Online (Sandbox Code Playgroud)

然后您的代码将传递一个结构化参数(我假设这组站点最初来自 DataTable 或其他一些集合):

SqlCommand cmd = new SqlCommand("dbo.ProcedureName", conn);
c2.CommandType = CommandType.StoredProcedure;
SqlParameter tvp = cmd.Parameters.AddWithValue("@Sites", DataTableName);
tvp.SqlDbType = SqlDbType.Structured;
...
Run Code Online (Sandbox Code Playgroud)

如果您真的想以缓慢、老式的方式执行此操作,您可以创建一个分割字符串的 UDF:

CREATE FUNCTION dbo.SplitStrings_XML
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );
GO
Run Code Online (Sandbox Code Playgroud)

然后你的查询是(再次省略了不太相关的位):

  INNER JOIN dbo.SplitStrings_XML(@Sites, N';') AS f
    ON s.[Name] = f.Item;
Run Code Online (Sandbox Code Playgroud)

现在,XML 在这里可能不是安全的方法,但它是最简单的复制方法,无需提供更慢的方法或辅助对象(如数字表)。对于其他替代方案,以及更多关于为什么你不想这样做的信息,请参阅: