我必须在具有varchar列的表上编写查询。此列中的值可能有一个数字作为子字符串
可以说列值为
Data
-----------------------
abc=123/efg=143/ijk=163
abc=123/efg=153/ijk=173
现在我必须查询数据包含数字[123,143,163]的表格,但不应包含任何其他数字。
如何编写此选择查询?
答案 0 :(得分:2)
这看起来像是一个非常糟糕的数据库设计。如果您对存储在字符串中的单独信息感兴趣,则不要将字符串,而是将单独的信息存储在单独的列中。如果可能,请更改此设置,此类查询将变得非常简单。
但是,目前很容易找到所描述的记录,前提是字符串中总有三个数字与样本数据一样。在字符串末尾添加斜杠,因此每个数字都有一个前导=
和一个尾随/
。然后使用LIKE
查找字符串中的数字。
select *
from mytable
where data || `/` like '%=123/%'
and data || `/` like '%=143/%'
and data || `/` like '%=163/%';
如果这三个数字在字符串中,那么所有数字都匹配。因此,没有其他数字不匹配。
如果字符串中可以有更多数字但没有重复项,则计算等号以确定字符串中有多少个数字:
select *
from mytable
where data || '/' like '%=123/%'
and data || '/' like '%=143/%'
and data || '/' like '%=163/%'
and regexp_count(data, '=') = 3;
这是一个查询,甚至接受字符串中的重复数字:
select *
from mytable
where regexp_count(data, '=') >= 3
and regexp_count(data, '=') =
regexp_count(data || '/', '=123/') +
regexp_count(data || '/', '=143/') +
regexp_count(data || '/', '=163/');
答案 1 :(得分:0)
Oracle安装程序:
CREATE TABLE table_name ( data ) AS
SELECT 'abc=123/efg=143/ijk=163' FROM DUAL UNION ALL
SELECT 'abc=123/efg=153/ijk=173' FORM DUAL;
然后,您可以创建一些虚拟列来表示数据:
ALTER TABLE table_name ADD abc GENERATED ALWAYS AS (
TO_NUMBER( REGEXP_SUBSTR( data, '(^|/)abc=(\d+)(/|$)', 1, 1, NULL, 2 ) )
) VIRTUAL;
ALTER TABLE table_name ADD efg GENERATED ALWAYS AS (
TO_NUMBER( REGEXP_SUBSTR( data, '(^|/)efg=(\d+)(/|$)', 1, 1, NULL, 2 ) )
) VIRTUAL;
ALTER TABLE table_name ADD ijk GENERATED ALWAYS AS (
TO_NUMBER( REGEXP_SUBSTR( data, '(^|/)ijk=(\d+)(/|$)', 1, 1, NULL, 2 ) )
) VIRTUAL;
如果需要,可以添加适当的索引:
CREATE INDEX table_name__abc_efg_ijk__idx ON table_name( abc, efg, ijk );
<强>查询强>:
然后,如果你只有这三个键,你可以这样做:
SELECT abc, efg, ijk
FROM table_name
WHERE abc = 123
AND efg = 143
AND ijk = 163;
但是,如果您可以获得三个以上的密钥并且想要忽略其他值,那么您可以这样做:
CREATE TYPE intlist AS TABLE OF INT;
/
SELECT *
FROM table_name
WHERE INTLIST( 143, 123, 163 )
=
CAST(
MULTISET(
SELECT TO_NUMBER(
REGEXP_SUBSTR(
t.data,
'[^/=]+=(\d+)(/|$)',
1,
LEVEL,
NULL,
1
)
)
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.data, '[^/=]+=(\d+)(/|$)' )
)
AS INTLIST
);
这有额外的好处INTLIST(123, 143, 163)
可以passed as a bind parameter(取决于您使用的客户端程序和Oracle驱动程序),这样您就可以简单地更改要过滤的数量和数量for(并且值的顺序无关紧要)。
此外,如果您希望它至少包含这些值,则可以将INTLIST( ... ) =
更改为INTLIST( ... ) SUBMULTISET OF
。