【问题标题】:Filter fields in 'IN' clause using a variable使用变量过滤“IN”子句中的字段
【发布时间】:2011-12-15 11:39:37
【问题描述】:

我想做这样的事情:

DECLARE @list nvarhcar(200) SET @list = 'VALUE1,VALUE2,VALUE3'

Select * from foo where field in (@list)

直到今天我解决了使用 sp_executeSQL 过程,我自己的数据库函数 csv2table 和 subselect,但我认为这不是很干净。

不使用动态sql有什么办法解决吗?有没有直接的方法?

我使用的是 Microsoft Sql Server 2005。

谢谢!

【问题讨论】:

  • 一旦您意识到field 的内容实际上可能包含逗号,您就应该明白为什么SQL 不会神奇地将您的单个字符串撕开并将其视为3 个单独的值。
  • 为什么不能使用动态SQL?
  • 动态 SQL 有 2 个主要缺陷,尤其是在存储过程中使用时——考虑到参数的存在——这里可能就是这种情况。 1) 用户需要对 foo 表具有 SELECT 权限(这可以通过 EXECUTE AS 解决,我知道) 2) @list 参数可能包含类似:NULL); <some evil code here>;declare @x int;SELECT @x=(1 的内容。假设 dyn-sql 部分是幼稚的EXEC ('SELECT * FROM foo WHERE field in (' + @list + ')'),这将导致比您讨价还价的麻烦更多..

标签: sql sql-server select csv


【解决方案1】:

请您尝试如下:谢谢

DECLARE @list nvarchar(200) 
SET @list = 'VALUE1,VALUE2,VALUE3'

SELECT * FROM foo WHERE CHARINDEX(',' + field + ',', ',' + @list + ',') > 0 

【讨论】:

    【解决方案2】:

    将 A 部分创建为 UDF

    /* Part A */
    DECLARE @list Varchar(max) = 'VALUE1,VALUE2,VALUE3'
    DECLARE @tmpTbl_Values Table  (ID varchar(50)) 
    
    Set @list =  @list + ',' 
    -- Indexes to keep the position of searching
    Declare @Pos1 Int
    Declare @pos2 Int
    
    -- Start from first character 
    Set @Pos1=1
    Set @Pos2=1
    While @Pos1<Len(@list)
    Begin
    Set @Pos1 = CharIndex(',',@list,@Pos1)
    Insert @tmpTbl_Values Select  Cast(Substring(@list,@Pos2,@Pos1-@Pos2) As varchar(50))
    -- Go to next non comma character
    Set @Pos2=@Pos1+1
    -- Search from the next charcater
    Set @Pos1 = @Pos1+1
    End 
    
    
    /* Part B */
    SELECT      * 
    FROM        foo A
    INNER JOIN  @tmpTbl_Values B
    ON          AB.ID = B.ID
    

    【讨论】:

      【解决方案3】:

      另一种选择:

      Select * from foo where '%,' + field + ',%' like ',' + (@list) + ','
      

      这比现有方法更“脏”。

      理想情况下,我建议将查询字符串更改为:

      Select * from foo where field in ()
      

      然后阅读@list 中的值列表,为每个值插入?(用逗号分隔)并将每个值绑定到该参数标记。但是,我不知道如何仅在动态 SQL 中实现这一点。

      【讨论】:

      • 忘记了这个(LIKE)。为什么这会“更脏”?我过去也使用过它,只要@list 内容中没有前导/尾随空格问题,它就可以相对较好地工作。 (也不是 dyn-sql)
      • 也许不是“更脏”,但可能效率较低 - “in”子句应该能够使用合适的索引,但在选择值之前带有% 的“like”子句不会。
      • 是的。但在小列表和少量记录上可能不是问题。毕竟,UDF 也会有一些开销。但我同意,如果您要使用 2000 个字符的字符串来 LIKE 超过 100 个 mio 行,事情可能会很快变慢。 (为了好玩,我刚刚使用精简(ID + 字段)20 mio 表和 400 char(10 个值)@test 进行了测试,使用 LIKE 花了 23326 毫秒 - 毕竟不是那么糟糕,但仍然不是你想要一遍又一遍地做的事情再次。但是,UDF 也需要 19304 毫秒!但如果我在字段(UQ,集群)上索引 foo,UDF 只需要 218 毫秒,而 LIKE 完全不受影响)
      【解决方案4】:

      如果字段的数据类型是 nchar 或 varchar,IN() 运算符将查找类似的值

      IN('VALUE1','VALUE2','VALUE3', etc..)            
      

      所以你根据那个设置你的@list

      【讨论】:

      • 你能详细说明一下。 AFAIK @list 由用户提供,包含一个逗号分隔的值列表,您希望与列匹配。这无需使用 UDF 或 dyn-sql...
      • 我的意思是使用喜欢 IN(REPLACE(''''+ rtrim(@list) +'''', ',', ''','''))
      • 那你还是需要在动态sql中执行。
      • @dero 你的意思是,SELECT * FROM tblname WHERE fieldname IN(REPLACE(''''+ rtrim(@list) +'''', ',', ''',''' )) 是动态sql。为了处理存储过程中的变量,我们必须根据输入值来处理查询,上面的查询不是一个复杂的动态sql。即使我们不需要运行 EXEC 来运行这个查询
      • 我并不是说它很复杂,只是你需要 EXEC()(或 sp_executesql)来运行它。
      【解决方案5】:

      一种可能的方法是执行以下操作:

      SELECT * FROM foo WHERE CHARINDEX(',' + field + ',', ',' + @list + ',') > 0 
      

      这是假设字段中的数据没有逗号

      【讨论】:

        【解决方案6】:

        如果你处理一个字符串字段,你可以使用这样的东西..

        DECLARE @csv varchar(50) = 'item1,item2, item3, item4 ,item5'
        SELECT * FROM foo WHERE PATINDEX('%' + Field + '%', @csv) > 0
        

        【讨论】:

        • 类似于@Elias Hossain,但我相信更灵活。
        【解决方案7】:

        在 SQL 2005 中,您可以使用自定义 UDF 将分隔列表解析为表 - 请参阅这篇文章 http://www.sommarskog.se/arrays-in-sql-2005.html

        【讨论】:

        • 我不是反对者,但从函数 csv2table 的名称来看,我怀疑这是 OP 已经在做的事情。
        • 同意,但我猜 Dyn-SQL 的答案也应该被否决 =)
        猜你喜欢
        • 1970-01-01
        • 2020-06-06
        • 1970-01-01
        • 2012-11-17
        • 2013-06-25
        • 1970-01-01
        • 2021-04-26
        • 2011-06-25
        • 2019-08-10
        相关资源
        最近更新 更多