匹配列中的逗号分隔值
Posted
技术标签:
【中文标题】匹配列中的逗号分隔值【英文标题】:Match comma separated values in column 【发布时间】:2012-02-23 22:07:18 【问题描述】:如果我有一个名为“类别”的列,其中以逗号分隔的行中有 science,maths,english
,如图所示,我将如何将所有行与包含 maths
的类别匹配?
我尝试了一个简单的LIKE
,但它并不十分准确,因为在搜索'%science%'
时可能会匹配两者。
我环顾了 ***,有很多类似的问题,但似乎都想以逗号分隔列表或其他形式返回数据 - 不是我想要的。
我不想使用存储过程,也不能使用全文搜索。我有一个我使用的存储过程,它在每个值周围添加了另一个字符 ('$')
然后会搜索 '$value$'...
这太讨厌了吗?我正在寻求一种更简单的方法。
【问题讨论】:
这就是为什么多值属性是邪恶的。 @Yuck 我认为并发问题的增加是它们邪恶的原因。 我绝不会让这个设计站得住脚。您需要一个用于数据的子表,以便您可以有效地对其进行查询。永远不要存储逗号分隔的列表。 它们是邪恶的,因为您必须使用每行的函数拆分字符串,然后搜索其中的每一个或匹配有时会因特殊字符而分崩离析的确切模式。这很昂贵,你没有得到索引的任何好处,没有标准化,而且,嗯,******* 烦人。对不起,星星里的字符不是脏话,我不小心把密码贴进去了;-) @DavidStratton 您能否详细说明LIKE
子句如何容易受到 SQL 注入的影响?我所能想象的只是有人输入%
来进行表扫描...
【参考方案1】:
免责声明:评论员是对的……单个字段中的 CSV 是一个糟糕的设计,应该重新做。
话虽如此,以下是解决问题的方法:
在Categories
前面加上,
,这样您就可以将它们包含在通配符搜索中:
WHERE (',' + Categories + ',') LIKE '%,science,%'
【讨论】:
我修复了一个微小的语法错误。此解决方案将起作用,并且比 XML 解决方案更快。 (当然,它仍然需要表扫描或索引扫描)。【参考方案2】:使用 FIND_IN_SET(,)
SQL:
SELECT name FROM orders,company
WHERE orderID = 1
AND
FIND_IN_SET(companyID, attachedCompanyIDs)
或 可以查看这个链接FIND_IN_SET() vs IN()
【讨论】:
哇!逗号分隔列的绝佳解决方案!也处理所有特殊情况。应该是公认的答案之一! FIND_IN_SET 仅适用于 mysql。很好的解决方案,但不在 SQL Server 上。【参考方案3】:这个问题在google上是可见的,有很多观点,所以我想分享我对这个问题的处理方法。我不得不处理这样一个糟糕的设计,因为逗号分隔的值也存储为字符串。我在调整负责标签的 CMS 插件时遇到了这个问题。
是的,与网站文章相关的标签是这样存储的:“tag1,tag2,...,tagN”。因此,获得精确匹配并不像最初看起来那么简单:使用简单的LIKE
,文章标记为“ball”,我也得到了标记为“football”和“ballroom”。不重要,但很烦人。
FIND_IN_SET 函数起初看起来很棒,但后来发现它不使用索引并且如果第一个参数包含逗号字符则无法正常工作。
我不想更改插件本身或构建该插件的更深层次的 CMS 核心功能。
另外值得注意的是,需要的标签(子字符串)可以是字符串中的第一个、最后一个元素,也可以是中间的某处,所以这段代码WHERE (',' + Categories + ',') LIKE '%,science,%'
并没有涵盖所有三种情况。
最后,我得到了非常简单的解决方案。它对我有用:
... WHERE tags LIKE 'ball,%' OR tags LIKE '%,ball,%' OR tags LIKE '%,ball'
涵盖所有情况;逗号用作分隔符。希望它可以帮助遇到类似陷阱的其他人。
PS。我根本不是 MySQL/DB 专家,我很想了解这种方法的潜在缺点,尤其是在非常大的表上(这不是我的情况,顺便说一句)。我只是分享了我的小型研究的结果,以及我为解决这个问题所做的努力。
【讨论】:
【参考方案4】:我提出一个 4x WHERE 可以匹配任何可能的情况:单独的值、csv 开头、中间或结尾的值:
WHERE Categories = 'science' /* CSV containing only the one value */
OR Categories LIKE 'science,%' /* value at start of CSV */
OR Categories LIKE '%,science,%' /* value somewhere in the middle */
OR Categories LIKE '%,science' /* value at the end of CSV */
这样应该选择所有“科学”行,但不选择“poo_science”行。
【讨论】:
【参考方案5】:我对您的数据布局做了一些假设。试试这个 - 使用 SQL Server 2K8+ 这应该可以工作:
DECLARE @SearchString NVarChar(100) = 'maths';
SELECT 1 SomeId, 'science,maths,english' Categories
INTO #TestTable;
WITH R AS (
SELECT
X.SomeId,
C.value('@value', 'NVarChar(100)') SomeTagValue
FROM (SELECT SomeId,
CONVERT(XML, '<tag value = "' + REPLACE(Categories, ',', '" /><tag value = "') + '" />') XMLValue
FROM #TestTable) X CROSS APPLY X.XMLValue.nodes('//tag') T(C)
)
SELECT *
FROM R
WHERE SomeTagValue = @SearchString;
DROP TABLE #TestTable;
它肯定不会是超高效或非常可扩展的,但是处理非规范化数据往往会固有地存在这些问题。
【讨论】:
【参考方案6】:使用 FIND_IN_SET() mysql 函数
语法
SELECT * FROM as a WHERE FIND_IN_SET(value to search in string,comma separated string);
例子
SELECT * FROM as a WHERE FIND_IN_SET(5,"1,2,3,4,5,6");
更多信息请点击以下链接:
http://blog.sqlauthority.com/2014/03/21/mysql-search-for-values-within-a-comma-separated-values-find_in_set/
【讨论】:
以上是关于匹配列中的逗号分隔值的主要内容,如果未能解决你的问题,请参考以下文章