【问题标题】:how to select data row from a comma separated value field如何从逗号分隔值字段中选择数据行
【发布时间】:2016-12-05 10:56:30
【问题描述】:

我的问题与这个问题不完全一样,但类似

How to SELECT parts from a comma-separated field with a LIKE statement

但我在那里没有看到任何答案。所以我再次发布我的问题。

我有下表

╔════════════╦═════════════╗
║ VacancyId  ║  Media      ║
╠════════════╬═════════════╣
║     1      ║ 32,26,30    ║
║     2      ║ 31, 25,20   ║
║     3      ║ 21,32,23    ║
╚════════════╩═════════════╝

我想选择媒体 id=30 或 media=21 或 media= 40 的数据

所以在这种情况下,输出将返回第一行和第三行。

我该怎么做?

我尝试过像“30”这样的媒体,但它没有返回任何值。另外,我只是不需要在该字段中搜索一个字符串。

我的数据库是 SQL Server

谢谢

【问题讨论】:

  • 通过添加预期输出来编辑您的问题。
  • 感谢您的回复。我为我的预期输出添加了行。
  • 您是否尝试过使用 WHERE Media LIKE '%30%' OR Media LIKE '%21%' 或 OR Media LIKE '%40%'
  • 要匹配的值的数量是否会保持不变?
  • Coder of Code:值将是相同的。

标签: sql-server sql-server-2008 ssms


【解决方案1】:

如果可行,最好使用逗号分隔的值存储在数据库中,尝试创建单独的表来存储它们,因为这很可能是1:n 关系。

如果这不可行,那么有以下可能的方法可以做到这一点, 如果您要匹配的值的数量保持不变,那么您可能需要根据您的要求执行一系列Like 语句和OR/AND

例如-

WHERE
    Media LIKE '%21%'
    OR Media LIKE '%30%'
    OR Media LIKE '%40%' 

但是,上面的查询可能会捕获所有包含 21 的值,因此即使具有 1210210 等值的列也会被返回。为了克服这个问题,您可以执行以下技巧,这会妨碍性能,因为它使用 where 子句中的函数,并且不利于进行 Seargable 查询。 不过就这样吧,

--Declare valueSearch variable first to value to match for you can do this for multiple values using multiple variables.

Declare @valueSearch = '21'

-- Then do the matching in where clause
WHERE 
    (',' + RTRIM(Media) + ',') LIKE '%,' + @valueSearch + ',%'

如果要匹配的值的数量会发生变化,那么您可能需要查看FullText Index 并且您应该考虑相同的问题。 如果您决定在Fulltext Index 之后使用此功能,您可以执行以下操作以获得您想要的,

例如-

WHERE 
     CONTAINS(Media, '"21" OR "30" OR "40"')

【讨论】:

  • 有问题,当搜索'4'时,你也会匹配到'3,49,69'。为了解决这个问题,列表本身应该以',' 开始和结束,搜索值也应该这样做......',3,48,69,' LIKE '%,4,%'。也就是说,你说得对,数据结构很差,“应该”被规范化。
  • @MatBailie 刚刚更新了答案。第一眼就错过了。
  • 同样适用于CONTAINS
【解决方案2】:

我可以建议的最好方法是首先使用This link 对表格进行逗号分隔值,您最终会得到如下所示的表格。

SELECT * FROM Table
WHERE Media in('30','28')

它肯定会起作用。

【讨论】:

  • 建议的结构是正确的,这就是它应该被建模的方式(虽然我也会规定媒体字段是一个INT)。但是生成的查询不再需要使用 LIKE...media IN (21,30,40) 就足够了。
【解决方案3】:

你可以使用它,但性能不可避免地很差。正如其他人所说,你应该规范化这个结构。

WHERE
       ',' + media + ',' LIKE '%,21,%'
    OR ',' + media + ',' LIKE '%,30,%'
    Etc, etc...

【讨论】:

    【解决方案4】:

    如果您确定包含字符串 30 的任何 Media 值将是您希望返回的值,则只需在 LIKE 语句中包含通配符:

    SELECT *
    FROM Table
    WHERE Media LIKE '%30%'
    

    请记住,尽管这也会返回 Media 值为 298,300,302 的记录,因此如果这对您有问题,您需要考虑更复杂的方法,例如:

    SELECT *
    FROM Table
    WHERE Media LIKE '%,30,%'
    OR Media LIKE '30,%'
    OR Media LIKE '%,30'
    OR Media = '30'
    

    如果字符串中可能有空格(根据您的问题),您还需要删除它们:

    SELECT *
    FROM Table
    WHERE REPLACE(Media,' ','') LIKE '%,30,%'
    OR REPLACE(Media,' ','') LIKE '30,%'
    OR REPLACE(Media,' ','') LIKE '%,30'
    OR REPLACE(Media,' ','') = '30'
    

    编辑:我实际上更喜欢Coder of Code的解决方案:

    SELECT *
    FROM Table
    WHERE ',' + LTRIM(RTRIM(REPLACE(Media,' ',''))) + ',' LIKE '%,30,%'
    

    您提到希望在该字段中搜索多个字符串,这也是可能的:

    SELECT *
    FROM Table
    WHERE Media LIKE '%30%'
       OR Media LIKE '%28%'
    
    SELECT *
    FROM Table
    WHERE Media LIKE '%30%'
      AND Media LIKE '%28%'
    

    【讨论】:

    • 您好,谢谢您的回复。这行得通。但是有一个限制。如果我写像 '%4%' 这样的媒体,那么它的所有行都会有 4 , 14, 24, 34, 44 。如何克服这个限制?
    • @Bashabi 是的,我在回答中注意到了这一点。我现在将进行更新,以展示一种您可以更复杂并更好地处理此问题的方法。
    • 有第 4 个边缘情况;其中媒体字段只有一个值 ('30'),没有前导或尾随 ','
    • @MatBailie 是的,你是对的,我会为此更新。老实说,我认为Coder of Code 对此的处理更好,只是将整个值包装在一个前导和尾随逗号中。
    • 确实如此,抱歉。我喜欢你的更好地处理这个问题。 ;)
    【解决方案5】:

    我同意这样存储的逗号分隔值不是一个好主意。如果你必须这样做的话;

    我认为使用内联函数会提供更好的性能;

    Select VacancyId, Media from (
      Select 1 as VacancyId, '32,26,30' as Media
      union all 
      Select 2, '31,25,20'
      union all 
      Select 3, '21,32,23'
    ) asa
    CROSS APPLY dbo.udf_StrToTable(Media, ',') tbl
    where CAST(tbl.Result as int) in (30,21,40)
    Group by VacancyId, Media
    

    输出是;

    VacancyId   Media
    ----------- ---------
    1           32,26,30
    3           21,32,23
    

    我们的内联函数脚本是;

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[udf_StrToTable]') and xtype in (N'FN', N'IF', N'TF'))
    drop function [dbo].udf_StrToTable
    GO
    
    CREATE FUNCTION udf_StrToTable (@List NVARCHAR(MAX), @Delimiter NVARCHAR(1))
    RETURNS TABLE
    With Encryption
    AS
    RETURN 
    (   WITH Split(stpos,endpos)
        AS(
            SELECT 0 AS stpos, CHARINDEX(@Delimiter,@List) AS endpos
            UNION ALL
            SELECT CAST(endpos+1 as int), CHARINDEX(@Delimiter,@List,endpos+1)
                FROM Split
                WHERE endpos > 0
        )
        SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as inx, 
               SUBSTRING(@List,stpos,COALESCE(NULLIF(endpos,0),LEN(@List)+1)-stpos) Result
        FROM Split
    )
    GO
    

    【讨论】:

      【解决方案6】:

      此解决方案使用 RECURSIVE CTE 来识别字符串中每个逗号的位置,然后使用 SUBSTRING 返回逗号之间的所有字符串。

      我留下了一些不必要的代码来帮助您了解它正在做什么。您可以将其剥离以提供您真正需要的内容。

      DROP TABLE #TMP
      CREATE TABLE #TMP(ID INT, Vals CHAR(100))
      
      INSERT INTO  #TMP(ID,VALS)
      VALUES
      (1,'32,26,30')
      ,(2,'31, 25,20')
      ,(3,'21,32,23')
      
      ;WITH cte
          AS
          (
          SELECT
              ID
              ,VALS
              ,0 POS
              ,CHARINDEX(',',VALS,0) REM
          FROM
              #TMP
          UNION ALL
              SELECT ID,VALS,REM,CHARINDEX(',',VALS,REM+1)
              FROM
              cte c 
              WHERE CHARINDEX(',',VALS,REM+1) > 0
          UNION ALL
              SELECT ID,VALS,REM,LEN(VALS)
              FROM
              cte c 
              WHERE POS+1 < LEN(VALS) AND CHARINDEX(',',VALS,REM+1) = 0
          )
      ,cte_Clean
          AS
          (
          SELECT ID,CAST(REPLACE(LTRIM(RTRIM(SUBSTRING(VALS,POS+1,REM-POS))),',','') AS INT) AS VAL FROM cte
          WHERE POS <> REM
          )
      SELECT
          ID
      FROM
          cte_Clean
      WHERE
          VAL = 32
      ORDER BY ID 
      

      【讨论】:

        猜你喜欢
        • 2017-05-12
        • 2012-03-20
        • 1970-01-01
        • 1970-01-01
        • 2010-10-18
        • 2020-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多