包含逗号分隔值的列中的值

Posted

技术标签:

【中文标题】包含逗号分隔值的列中的值【英文标题】:Where value in column containing comma delimited values 【发布时间】:2011-08-02 11:22:34 【问题描述】:

我希望为 SQL Server 2008 编写一个 SQL 语句,该语句选择列包含值的条目,现在列中的值是逗号分隔的列表(通常 - 只能有一个条目(并且没有前导逗号) ) 那么检查的是“这个值是否包含在列表中的某处?”,例如:

COLUMN = Cat, Dog, Sparrow, Trout, Cow, Seahorse
Does COLUMN contain Cat? YES
Does COLUMN contain horse? NO
Does COLUMN contain Sheep? NO

COLUMN = Mouse
Does COLUMN contain Hare? NO
Does COLUMN contain Mouse? YES

我在想我可以这样使用“IN”关键字

SELECT id_column FROM table_name WHERE 'Cat' IN COLUMN

但这不起作用,因为您似乎只能使用它来检查列是否包含一系列逗号分隔值中的一个。

我也不能使用 CONTAINS() OR 'LIKE',因为在上面的示例中,将返回 'horse' 的值,因为整个字符串包含 'Seahorse' 中的 horse,并且我无法搜索 needle 加上逗号(如果我正在寻找“马”,则搜索将是“马”)如果条目位于列表的末尾怎么办?而且我无法搜索逗号加针(如果我正在寻找“马”,则搜索将是“马”) 如果条目是列表中的第一个呢?如果条目是唯一的(单个)条目,我不能同时使用两者?

【问题讨论】:

使用单独的表,每个条目一行,而不是逗号分隔的多值列表。 很想这样做,如果我有选择的话,我会这样做,但不幸的是我没有。我正在使用现有的数据库 - 这是我必须使用的...... 【参考方案1】:

有一个棘手的场景。如果我在列表 '17,34,400,12' 中寻找 '40',那么它会找到“,40”并返回那个不正确的条目。这会处理所有解决方案:

WHERE (',' + RTRIM(MyColumn) + ',') LIKE '%,' + @search + ',%'

【讨论】:

我知道这个问题很久以前就被问过了,但是很好的捕捉和减少检查的好方法。我现在已将您的答案标记为正确答案。 如果 List 以 , 结尾,这不会将空字符串添加到项目中吗?例如Cat, Dog, Sparrow, Trout, Cow, Seahorse, 将匹配@search="" 谢谢你,这太棒了! 这很有趣,值得一提,但我想找出第一个列表中的一个项目是否存在于第二个列表中。 我知道这是一篇旧帖子,但我仍然觉得它很有帮助。通过一些字符串操作的简单解决方案,我喜欢它。现在,如果以前的数据库设计师知道如何正确规范化表,我一开始就不必使用它。【参考方案2】:
WHERE
      MyColumn LIKE '%,' + @search + ',%' --middle
      OR
      MyColumn LIKE @search + ',%' --start
      OR
      MyColumn LIKE '%,' + @search --end
      OR 
      MyColumn =  @search --single (good point by Cheran S in comment)

【讨论】:

MyColumn 只有一个元素时,我相信您还需要第 4 种情况:... OR MyColumn = @search 我遇到了类似的问题,只是我没有获得足够的回报,在 Base 中运行该查询只显示了少数匹配项。我将其更改为“ MyColumn LIKE '%' + @search + '%' ”,效果很好【参考方案3】:
SELECT * FROM TABLENAME WHERE FIND_IN_SET(@search, column)

如果您的列在列表项之间有空格,请使用

SELECT * FROM TABLENAME WHERE FIND_IN_SET(@search, REPLACE(column, ' ', ''))

http://dev.mysql.com/doc/refman/5.0/en/string-functions.html

【讨论】:

问题是关于没有该内置功能的 SQL Server 2008。【参考方案4】:
DECLARE @search VARCHAR(10);
SET @search = 'Cat';

WITH T(C)
AS
(
SELECT 'Cat, Dog, Sparrow, Trout, Cow, Seahorse'
)
SELECT *
FROM T 
WHERE ', ' + C + ',' LIKE '%, ' + @search + ',%'

这当然需要对每次搜索进行全表扫描。

【讨论】:

这可行,但前提是'@search' 中元素的顺序与数据库中的完全相同。搜索“狗,猫”不会带来任何结果。 @edalorzo - OP 根据他们的问题一次只搜索一个元素。【参考方案5】:

我在另一个论坛上找到了这个答案,效果很好。 如果还有 10,那么找到 1 没有问题

WHERE tablename REGEXP "(^|,)@search(,|$)"

I found it here

【讨论】:

【参考方案6】:
select *
from YourTable
where ','+replace(col, ' ', '')+',' like '%,Cat,%'

【讨论】:

【参考方案7】:

在这种情况下,最好的解决方案是规范化您的表以在不同的行中使用逗号分隔值(第一范式 1NF)http://en.wikipedia.org/wiki/First_normal_form

为此,您可以使用 CLR http://bi-tch.blogspot.com/2007/10/sql-clr-net-function-split.html 或使用普通 SQL 在 SQL 中实现一个不错的拆分表值函数。

CREATE FUNCTION dbo.Split
(
    @RowData nvarchar(2000),
    @SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
    Id int identity(1,1),
    Data nvarchar(100)
) 
AS  
BEGIN 
    Declare @Cnt int
    Set @Cnt = 1

    While (Charindex(@SplitOn,@RowData)>0)
    Begin
        Insert Into @RtnValue (data)
        Select 
            Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))

        Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
        Set @Cnt = @Cnt + 1
    End

    Insert Into @RtnValue (data)
    Select Data = ltrim(rtrim(@RowData))

    Return
END

然后可以使用cross apply查询归一化的输出

select distinct a.id_column
from   MyTable a cross apply
       dbo.Split(A.MyCol,',') b
where  b.Data='Cat'

【讨论】:

【参考方案8】:
SELECT * FROM TABLE_NAME WHERE
        (
            LOCATE(',DOG,', CONCAT(',',COLUMN,','))>0 OR
            LOCATE(',CAT,', CONCAT(',',COLUMN,','))>0
        );

【讨论】:

出色而完美的解决方案。谢谢老兄。【参考方案9】:

我在寻找类似问题的解决方案时才知道这一点。 SQL 有一个名为 CONTAINS 的新关键字,您可以使用它。 更多详情见http://msdn.microsoft.com/en-us/library/ms187787.aspx

【讨论】:

这仅适用于 SQL Server?据我所知,例如 MySQL 不提供此功能(只是想向像我这样的人澄清,即使我们没有使用 SQLServer,也找到了这个答案)【参考方案10】:

由于您不知道可以找到多少逗号分隔的条目,您可能需要创建一个带有“charindex”和“substring”SQL Server 函数的函数。函数返回的值可以在“in”表达式中使用。

您的函数可以被递归调用,也可以创建循环,搜索条目,直到字符串中没有更多条目。对函数的每次调用都使用先前找到的索引作为下一次调用的起点。第一次调用从 0 开始。

【讨论】:

【参考方案11】:

如果您知道 ID 而不是字符串,请使用以下方法:

where mylookuptablecolumn IN (myarrayorcommadelimitedarray)

只要确保 myarray 或commadelimitedarray 没有放在字符串引号中。

如果你想要 A OR B,但不是 AND。

【讨论】:

【参考方案12】:

虽然@tbaxter120 建议的棘手解决方案很好,但我使用这个函数并且工作起来很神奇,pString 是一个分隔字符串,pDelimiter 是一个分隔符:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER FUNCTION [dbo].[DelimitedSplit]
--===== Define I/O parameters
        (@pString NVARCHAR(MAX), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL -- does away with 0 base CTE, and the OR condition in one go!
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ---ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,50000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l

;

然后,例如,您可以在 where 子句中调用它,如下所示:

WHERE [fieldname] IN (SELECT LTRIM(RTRIM(Item)) FROM [dbo].[DelimitedSplit]('2,5,11', ','))

希望对您有所帮助。

【讨论】:

【参考方案13】:

包含逗号分隔值的列中的值搜索多个逗号分隔

            declare @d varchar(1000)='-11,-12,10,121'

            set @d=replace(@d,',',',%'' or '',''+a+'','' like ''%,')

            print @d
            declare @d1 varchar(5000)=
            'select * from (
            select ''1,21,13,12'' as a
            union
            select ''11,211,131,121''
            union
            select ''411,211,131,1211'') as t
             where '',''+a+'','' like ''%,'+@d+ ',%'''

             print @d1
             exec (@d1)

【讨论】:

【参考方案14】:

tbaxter120 建议的解决方案对我有用,但我需要在 MySQL、Oracle 和 MSSQL 中都支持的东西,这里是:

WHERE (CONCAT(',' ,CONCAT(RTRIM(MyColumn), ','))) LIKE CONCAT('%,' , CONCAT(@search , ',%'))

【讨论】:

【参考方案15】:

仅适用于 SQL Server 2016 或更高版本 - 使用 STRING_SPLIT

SELECT Column From Table 
WHERE EXISTS (SELECT *  FROM STRING_SPLIT(Column , ',') WHERE value IN ('Cat'));

【讨论】:

以上是关于包含逗号分隔值的列中的值的主要内容,如果未能解决你的问题,请参考以下文章

sql SQL - 在以逗号,管道或分号或任何其他字符分隔的列中获取多个值或连接值的值

TSQL 将列中的逗号分隔值与逗号分隔参数进行比较

匹配列中的逗号分隔值

无法在数据准备中的列中转义逗号

使用 XLRD 从 excel 表中的列中读取 int 值

在 Laravel 的列中使用连接和逗号分隔值执行查询