运行 使用正则表达式对列中以分号分隔的值进行复杂查询

Running a complex query on semi-colon separated values in a column using regexp

我有 table 内容元数据。一些列是用户 ID、内容 ID、角色。角色列是“;”分离值。例如,

在上面的 table 中,结果列指示该行是否应出现在查询中。

管理员角色:管理员、实施者、设置 最终用户角色:最终用户 1、最终用户 2、最终用户 3 等。 (这些是虚拟名称,实际名称有很大不同,这里不能透露)。

进一步展开,条件是查询结果行应至少具有一个非管理员最终用户角色。有五个管理员类型角色和大量非管理员最终用户角色。行可以有尽可能多的角色。

以下是我正在考虑的方法: 1. 创建一个包含所有角色的临时 table 并查询 table in where 角色条件,并使用 INSTR 找出最终用户角色。我不想这样做,因为我试图在查询中避免 pl/sql 。 2. 正则表达式

我一直在试验正则表达式,但没有取得任何进展。

注意:请不要建议更改数据布局,因为我无法控制它!

更新: 好的,这是我正在考虑的另一种方法。 - 从列字符串中删除所有与管理员相关的角色。 - 检查列表的长度。 Select 非零长度。 我猜这将需要 5 个替换函数,一个用于每个管理员类型角色,一个用于计算长度(这实际上是计算分号 +1 的数量)。 我正在尝试这个。如果有效,我会更新我的答案。否则邀请更多建议!

这是一个非常糟糕的数据布局。让我假设你不能改变它。有时我们会被其他人非常非常糟糕的设计决定所困。

我还假设用户的层次结构只有一层。如果是这样,您可以使用单个 join:

select c.*,
       (case when ';' || c.roles || ';' like '%;Admin;%' then 'Yes'
             when ';' || cu.roles || ';' like '%;Admin;%' then 'Yes'
             else 'No'
        end) as Outcome
from content c left join
     content cu
     on ';' || c.roles|| ';' like '%;End User ' || cu.id || ';%';

编辑:

如果您有多个管理员角色,只需使用正则表达式:

select c.*,
       (case when regexp_like(';' || c.roles || ';', ';Admin;|;Implementor;|;Setup;') then 'Yes'
             when regexp_like(';' || cu.roles || ';', ';Admin;|;Implementor;|;Setup;') then 'Yes'
             else 'No'
        end) as Outcome
from content c left join
     content cu
     on ';' || c.roles|| ';' like '%;End User ' || cu.id || ';%';

好的,我尝试了最新的方法(有问题的更新),它有点管用。我实施了 3 个管理员角色。为此必须使用长字符串操作。但是,如果列出了 3 个连续的管理员角色(管理员;设置;实施者;最终用户 1),这将不起作用,因为我将有 3 个“;” .有人可以建议一种用一个分号替换多个分号的更好方法吗?

另请建议是否可以改进此方法。

with new_roles as 
(select id,
 roles,
trim(both ';' from (replace(
    replace(
        replace(
            replace(roles,'Setup',''), 
            'Administrator', ''),
        'Implementor', ''),
    ';;', ';'))) as role_new
 from my_table)

select id, roles, role_new
from new_roles
where (LENGTH(role_new) - LENGTH(REPLACE(role_new,';','')))+1 is not NULL;

更新:我在这里发布我的最终解决方案,以防它可能对其他人有用:

with new_roles as 
(select id,
 roles,
trim(both ';' from (
    regexp_replace(
    regexp_replace(roles, '(Setup)|(Administrator)|(Implementor)','')),
    ';{1;}',';'))) as role_new
 from my_table)

select id, roles, role_new
from new_roles
where (LENGTH(role_new) - LENGTH(REPLACE(role_new,';','')))+1 is not NULL;