带有IN子句值列表的TSQL变量

时间:2011-03-21 18:09:09

标签: sql-server tsql

我想在select语句中使用“CASE WHEN ... THEN 1 ELSE 0 END”的句子。棘手的部分是我需要它来处理“值IN @List”。

如果我对列表进行硬编码,那么它的工作正常 - 而且效果很好:

SELECT
       CASE WHEN t.column_a IN ( 'value a', 'value b' ) THEN 1 ELSE 0 END AS priority
      , t.column_b
      , t.column_c
  FROM
       table AS t
 ORDER BY
       priority DESC

我想做的是:

-- @AvailableValues would be a list (array) of strings.
DECLARE
        @AvailableValues ???

 SELECT
        @AvailableValues = ???
   FROM
        lookup_table

 SELECT
        CASE WHEN t.column_a IN @AvailableValues THEN 1 ELSE 0 END AS priority
      , t.column_b
      , t.column_c
   FROM
        table AS t
  ORDER BY
        priority DESC

不幸的是,似乎SQL Server没有这样做 - 你不能使用带有IN子句的变量。所以这给我留下了一些其他的选择:

  1. 使'@AvailableValues'成为逗号分隔的字符串并使用LIKE语句。这样做效果不佳。
  2. 对'lookup_table'使用内联SELECT语句代替变量。再次,表现不佳(我认为),因为它必须在每一行上查找表。
  3. 编写一个包裹SELECT语句的函数来代替变量。我还没有尝试过(现在就试试)但似乎它会遇到与直接SELECT语句相同的问题。
  4. ???
  5. 还有其他选择吗?性能非常对于查询非常重要 - 它必须非常快,因为它为网站提供实时搜索结果页面(即没有缓存)。

    这里还有其他选择吗?有没有办法改善上述选项之一的性能以获得良好的性能?

    提前感谢您提供任何帮助!

    更新:我应该提到上例中的'lookup_table'已经是一个表变量。我还更新了示例查询,以更好地演示我如何使用该子句。

    更新II:我发现IN子句在NVARCHAR / NCHAR字段下运行(由于历史表设计原因)。如果我要进行处理整数字段的更改(即通过PK / FK关系约束),这会对性能产生很大影响吗?

4 个答案:

答案 0 :(得分:7)

您可以在IN子句中使用变量,但不能按照您尝试的方式使用。例如,你可以这样做:

declare @i int
declare @j int

select @i = 10, @j = 20

select * from YourTable where SomeColumn IN (@i, @j)

关键是变量不能代表多个值。

要回答您的问题,请使用内联选择。只要您不在查询中引用外部值(可能会逐行更改结果),引擎就不会重复从表中选择相同的数据。

答案 1 :(得分:2)

根据您的更新并假设查找表很小,我建议您尝试以下内容:

DECLARE @MyLookup table
 (SomeValue nvarchar(100) not null)

SELECT
   case when ml.SomeValue is not null then 1 else 0 end AS Priority   
  ,t.column_b
  ,t.column_c
 from MyTable t
  left outer join @MyLookup ml
   on ml.SomeValue = t.column_a
 order by case when ml.SomeValue is not null then 1 else 0 end desc

(您不能在ORDER BY子句中引用列别名“Priority”。或者,您可以使用序号位置,如下所示:

 order by 1 desc

但通常不建议这样做。)

只要查找表很小,这确实应该很快运行 - 但是你的评论意味着它是一个非常大的表,这可能会降低性能。

至于n [Var] char与int,是的,整数会更快,如果只是因为CPU有更少的字节来处理......这只是处理批次时的一个问题 of rows,所以值得尝试。

答案 2 :(得分:1)

这可能与您所需要的一致 请注意,这假定您具有权限并且已对输入数据进行了清理。

来自Running Dynamic Stored Procedures

CREATE PROCEDURE MyProc (@WHEREClause varchar(255))
AS

    -- Create a variable @SQLStatement
    DECLARE @SQLStatement varchar(255)

    -- Enter the dynamic SQL statement into the
    -- variable @SQLStatement
    SELECT @SQLStatement = "SELECT * FROM TableName WHERE " + @WHEREClause

    -- Execute the SQL statement
    EXEC(@SQLStatement)

答案 3 :(得分:1)

我使用CHARINDEX函数解决了这个问题。我想将字符串作为单个参数传递。我创建了一个字符串,其中包含我想要测试的每个值的前导和尾随逗号。然后我将一个前导和尾随逗号连接到我想要查看的字符串是否在“参数”中。最后,我检查了CHARINDEX> 0

DECLARE @CTSPST_Profit_Centers VARCHAR (256) 
SELECT @CTSPST_Profit_Centers = ',CS5000U37Y,CS5000U48B,CS5000V68A,CS5000V69A,CS500IV69A,CS5000V70S,CS5000V79B,CS500IV79B,'

SELECT 
   CASE
       WHEN CHARINDEX(','+ISMAT.PROFIT_CENTER+',' ,@CTSPST_Profit_Centers) > 0 THEN 'CTSPST'  
       ELSE ISMAT.DESIGN_ID  + ' 1 CPG' 
      END AS DESIGN_ID

您也可以在where子句

中执行此操作
WHERE CHARINDEX(','+ISMAT.PROFIT_CENTER+',',@CTSPST_Profit_Centers) > 0

如果您尝试比较数字,则需要将数字转换为文本字符串,以使CHARINDEX函数正常工作。