【问题标题】:Issues with TSQL IN expressionTSQL IN 表达式的问题
【发布时间】:2020-04-24 20:59:51
【问题描述】:

根据我收集到的关于 IN 表达式的信息,这应该可行:

DECLARE @list varchar(255)

SET @list = '''Item1'',''Item2'''

SELECT
   *
FROM
   Table
WHERE
   Item IN (@list)

它应该选择@list 中的那些项目。项目存在于表中。如果我分别为 Item1 和 Item2 执行查询(Item = Item1,然后 Item = Item2),这些单独的查询将起作用。只是不与IN。任何人都可以启发我吗?图这是一个简单的,只是很难找到有关此命令的有用信息。

编辑:

我目前正在使用动态存储过程执行此操作,其中我在字符串中构造查询并执行。例如,此过程有效:

ALTER PROCEDURE [dbo].[TestSproc]
@list varchar(4000)
AS
BEGIN

DECLARE @sql varchar(4000)

SET @sql =
'
SELECT
COUNT(*)
FROM
    Items
WHERE
    Item IN (' + @list + ') '

EXEC (@sql)

但是,此过程不起作用。它返回 0 行。如果我为 Item1 手动运行它,然后是 Item2,它会按预期返回两个计数:

ALTER PROCEDURE [dbo].[TestSproc]
    @list varchar(4000)
AS
BEGIN

SELECT
    COUNT(*)
FROM
    Items
WHERE
    Item IN (@list) 

我使用相同的命令来调用这两个过程:

EXEC    [dbo].[TestSproc]
    @list = N'''Item1'',''Item2'''

我试图在我最初的问题中进行总结,但我认为这可能会让人们失去基础。也许这将有助于解决我的问题(除非我犯了任何愚蠢的错别字)。

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    IN 关键字不对字符串中的列表进行操作。它对查询中返回的值列表进行操作(或对一组离散值进行操作,例如 ('Item1','Item2'),正如 DaveE 在他的评论中提到的那样)。你可以像这样修改@list

    DECALRE @list TABLE (
        value varchar(MAX))
    
    INSERT INTO @list (value)
    VALUES ('Item1')
    
    INSERT INTO @list (value)
    VALUES ('Item2')
    
    SELECT * FROM Table
    WHERE Item IN (SELECT value FROM @list)
    

    另一个选项是创建一个表值 UDF,它采用逗号分隔的值列表并返回表。我建议不要这样做,因为这种风格的 UDF 通常表现不佳。

    【讨论】:

    • 或者在一组离散值上 a la ...IN ('Item1', 'Item2')... 但绝对不是在 看起来像离散的字符串上值。
    • 我不太确定我是否理解。我以人们所说的方式使用列表不起作用,只是我在动态存储过程中使用它们,而不是在静态 SQL 中。我编辑了我的帖子以显示我在说什么。我可能只是问错了问题,我不确定。我认为这是一个简单的语法问题。
    • 在您的原始代码示例中,您使用了一个看起来像逗号分隔列表的单个字符串,而不是实际的逗号分隔列表。
    【解决方案2】:

    使这项工作的唯一方法是使用动态 sql,这是一个糟糕的主意。最好将这些值放入临时表中并加入其中。

    【讨论】:

    • 哦,这显然是我正在做的。错过了这个回复!
    【解决方案3】:
    【解决方案4】:

    in() 子句需要一组离散的值,而不是字符串。要完成这项工作,您需要将整个 SQL 查询动态创建为字符串,然后执行该字符串。

    试试这样的:

    declare @list varchar(255);
    set @list = '''Item1'',''Item2''';
    
    declare @query = varchar(max);
    set @query = 'SELECT
       *
    FROM
       Table
    WHERE
       Item IN (' + @list + ')';
    
    exec(@query);
    

    【讨论】:

      【解决方案5】:

      您使用的@list 是一个连接字符串,它被视为应逐字匹配的单个值。

      改用这个:

      DECLARE @list TABLE (value VARCHAR(255))
      
      INSERT
      INTO    @list
      VALUES  ('Item1')
      
      INSERT
      INTO    @list
      VALUES  ('Item2')
      
      SELECT  *
      FROM    Table
      WHERE   Item IN
              (
              SELECT  value
              FROM    @list
              )
      

      【讨论】:

        【解决方案6】:

        那你聚错了。

        IN 表达式不理解字符串形式的值列表。它所看到的只是一根弦。因为那是你传入的。

        您必须传入一个实际的值列表,这意味着每个值有一个不同的 SQL 表达式,用实际逗号分隔。

        SELECT *
        FROM   Table
        WHERE  Item IN ('Item1', 'Item2')
        

        或者,您可以本着这种精神做一些事情。

        DECLARE @list TABLE (value varchar(50))
        INSERT @list VALUES ('Item1')
        INSERT @list VALUES ('Item2')
        
        SELECT *
        FROM   [Table] t INNER JOIN @list l ON l.value = t.item
        

        【讨论】:

          【解决方案7】:

          ,我认为你完全错了

          你应该看看

          甚至

          --Split
          DECLARE @textXML XML
          DECLARE @data NVARCHAR(MAX), 
                  @delimiter NVARCHAR(5)
          
          SELECT  @data = 'A,B,C',
                  @delimiter = ','
          
          SELECT    @textXML = CAST('<d>' + REPLACE(@data, @delimiter, '</d><d>') + '</d>' AS XML)
          SELECT  T.split.value('.', 'nvarchar(max)') AS data
          FROM    @textXML.nodes('/d') T(split)
          

          也看看

          【讨论】:

            【解决方案8】:

            我使用可重复使用的自定义表值函数。它派上用场了……尤其是存储过程和 SSRS 的参数:

            CREATE FUNCTION [dbo].[fn_Split]
            (
              @ItemList NVARCHAR(4000),
              @delimiter CHAR(1)
            )
              RETURNS @itemtable TABLE (Item NVARCHAR(50) )
            
            AS
            
            BEGIN
            IF @delimiter IS NULL
            BEGIN 
            set @delimiter = ','
            END
            
            DECLARE @tempItemList NVARCHAR(4000)
            SET @tempItemList = @ItemList
            
            DECLARE @i INT
            DECLARE @Item NVARCHAR(4000)
            
            SET @tempItemList = REPLACE (@tempItemList, @delimiter + ' ', @delimiter)
            SET @i = CHARINDEX(@delimiter, @tempItemList)
            
            WHILE (LEN(@tempItemList) > 0)
            BEGIN
            IF @i = 0
            SET @Item = @tempItemList
            ELSE
            SET @Item = LEFT(@tempItemList, @i - 1)
            
            INSERT INTO @itemtable(Item) VALUES(@Item)
            
            IF @i = 0
            SET @tempItemList = ''
            ELSE
            SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)
            
            SET @i = CHARINDEX(@delimiter, @tempItemList)
            END
            RETURN
            END
            

            那么你可以这样使用它:

            DECLARE @list varchar(255)     
            
            SET @list = '''Item1'',''Item2'''     
            
            SELECT     *     
            FROM     Table     
            WHERE  Item IN (select * from dbo.fn_Split(@list,null))  //Right HERE 
            

            【讨论】:

              【解决方案9】:

              这是最好的来源:

              http://www.sommarskog.se/arrays-in-sql.html

              创建一个拆分函数,并像这样使用它:

              SELECT
                  *
                  FROM YourTable  y
                  INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value
              

              I prefer the number table approach

              要使此方法起作用,您需要进行一次时间表设置:

              SELECT TOP 10000 IDENTITY(int,1,1) AS Number
                  INTO Numbers
                  FROM sys.objects s1
                  CROSS JOIN sys.objects s2
              ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
              

              一旦设置了 Numbers 表,就创建这个函数:

              CREATE FUNCTION [dbo].[FN_ListToTable]
              (
                   @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
                  ,@List     varchar(8000)--REQUIRED, the list to split apart
              )
              RETURNS TABLE
              AS
              RETURN 
              (
              
                  ----------------
                  --SINGLE QUERY-- --this will not return empty rows
                  ----------------
                  SELECT
                      ListValue
                      FROM (SELECT
                                LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                                FROM (
                                         SELECT @SplitOn + @List + @SplitOn AS List2
                                     ) AS dt
                                    INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                                WHERE SUBSTRING(List2, number, 1) = @SplitOn
                           ) dt2
                      WHERE ListValue IS NOT NULL AND ListValue!=''
              
              );
              GO 
              

              您现在可以轻松地将 CSV 字符串拆分为表格并加入表格:

              select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')
              

              输出:

              ListValue
              -----------------------
              1
              2
              3
              4
              5
              6777
              
              (6 row(s) affected)
              

              您可以将 CSV 字符串传入一个过程并只处理给定 ID 的行:

              SELECT
                  y.*
                  FROM YourTable y
                      INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue
              

              这将使用 y.ID 上的索引

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-07-25
                • 2018-03-09
                • 1970-01-01
                • 2010-09-27
                • 2020-02-21
                • 2011-04-12
                • 1970-01-01
                相关资源
                最近更新 更多