是否有可能查询逗号分隔列的特定值?
我有(并没有拥有,所以我不能改变)一个布局类似于这个表。
ID | CATEGORIES --------------- 1 | c1 2 | c2,c3 3 | c3,c2 4 | c3 5 | c4,c8,c5,c100
我需要返回包含特定类别ID的行。 我通过用LIKE语句编写查询开始,因为值可以在string中的任何位置
SELECT id FROM table WHERE categories LIKE '%c2%';
会返回第2和第3行
SELECT id FROM table WHERE categories LIKE '%c3%' and categories LIKE '%c2%';
再次让我行2和3,但不是第4行
SELECT id FROM table WHERE categories LIKE '%c3%' or categories LIKE '%c2%';
再次让我第2,3和4行
我不喜欢所有的LIKE
语句。 我在Oracle文档中find了FIND_IN_SET()
,但它在10g中似乎不起作用。 我得到以下错误:
ORA-00904: "FIND_IN_SET": invalid identifier 00904. 00000 - "%s: invalid identifier"
当运行这个查询时: SELECT id FROM table WHERE FIND_IN_SET('c2', categories);
(例如从文档)或此查询: SELECT id FROM table WHERE FIND_IN_SET('c2', categories) <> 0;
(来自Google的示例)
我期望它返回第2和第3行。
有没有更好的方式来写这些查询,而不是使用大量的LIKE
语句?
你可以使用LIKE。 你不想匹配部分值,所以你必须在search中包含逗号。 这也意味着您将不得不提供额外的逗号来search文本开头或结尾的值:
select * from YourTable where ',' || CommaSeparatedValueColumn || ',' LIKE '%,SearchValue,%'
但是这个查询将会很慢,所有使用LIKE的查询都会比较慢,特别是使用前导通配符。
总是有风险。 如果值周围有空格,或者值本身可以包含逗号(在这种情况下,它们被引号括起来,就像在csv文件中一样),这个查询将不起作用,您将不得不添加更多的逻辑,减慢查询速度更。
更好的解决scheme是为这些类别添加一个子表。 或者甚至是一个单独的表格,以及将它们交叉链接到YourTable的表格。
你可以写一个PIPELINED表函数,返回一个1列表。 每行都是逗号分隔的string中的一个值。 使用这样的东西从列表中pop
一个string, put
其作为一行放入表中:
PIPE ROW(ltrim(rtrim(substr(l_list, 1, l_idx - 1),' '),' '));
用法:
SELECT * FROM MyTable WHERE 'c2' IN TABLE(Util_Pkg.split_string(categories));
在这里看到更多: Oracle文档
是和否…
“是”:
规范化数据(强烈推荐) – 即分类的列,以便你有每个分类在一个单独的…那么你可以只是在一个正常的faschion查询…
“没有”:
只要你保持这个“伪结构”就会有几个问题(性能和其他问题),你将不得不做类似的事情:
SELECT * FROM MyTable WHERE categories LIKE 'c2,%' OR categories = 'c2' OR categories LIKE '%,c2,%' OR categories LIKE '%,c2'
如果您绝对必须定义一个名为FIND_IN_SET的函数,如下所示:
CREATE OR REPLACE Function FIND_IN_SET ( vSET IN varchar2, vToFind IN VARCHAR2 ) RETURN number IS rRESULT number; BEGIN rRESULT := -1; SELECT COUNT(*) INTO rRESULT FROM DUAL WHERE vSET LIKE ( vToFine || ',%' ) OR vSET = vToFind OR vSET LIKE ('%,' || vToFind || ',%') OR vSET LIKE ('%,' || vToFind); RETURN rRESULT; END;
然后你可以使用这个function:
SELECT * FROM MyTable WHERE FIND_IN_SET (categories, 'c2' ) > 0;
为了将来的search者,不要忘记正则expression式的方式:
with tbl as ( select 1 ID, 'c1' CATEGORIES from dual union select 2 ID, 'c2,c3' CATEGORIES from dual union select 3 ID, 'c3,c2' CATEGORIES from dual union select 4 ID, 'c3' CATEGORIES from dual union select 5 ID, 'c4,c8,c5,c100' CATEGORIES from dual ) select * from tbl where regexp_like(CATEGORIES, '(^|\W)c3(\W|$)'); ID CATEGORIES ---------- ------------- 2 c2,c3 3 c3,c2 4 c3
这匹配在一个字的边界上,所以即使逗号后面跟着一个空格,它仍然可以工作。 如果您希望更加严格并且只匹配逗号分隔的值,请用逗号replace“\ W”。 无论如何,阅读正则expression式如下:匹配行的开始或词边界的一组,然后是目标search值,然后是一组字边界或行的结尾。
只要逗号分隔的列表不REGEXP_LIKE()
512个字符,就可以在这个实例中使用正则expression式(Oracle的正则expression式函数,例如REGEXP_LIKE()
限制为512个字符):
SELECT id, categories FROM mytable WHERE REGEXP_LIKE('c2', '^(' || REPLACE(categories, ',', '|') || ')$', 'i');
在上面我用正则expression式replace运算符|
replace逗号 。 如果您的分隔值列表已经|
– 有限,好多了。