我想编写一个简单的Oracle SQL 10g语句,将任意定义的逗号分隔列表中的项(字符串)与包含逗号分隔字符串列表的字段进行比较。定义列表中的项目可以按任何顺序出现在字段中,并且需要找到完全匹配(而不是子字符串)。
我有一个使用手动输入的一系列regexp_like()语句的工作解决方案,但我需要将其交给一个将继续保持此前进的客户端,并且希望能够只更新逗号分隔的字符串直接
我还有一些基于软件GUI的限制,我可以使用Oracle SQL来完成此任务。具体来说,我不能使用任何PL / SQL,这必须写在一个select语句中(没有临时表或任何有趣/有用的东西。)我已经找到了许多我想要完成的解决方案,但是几乎所有人都依赖于能够编写自定义函数。
所以,既然背景/限制已经不在了,那么让我们深入了解细节。
示例任意(客户提供)列表:ItemA,ItemB,ItemC
表项目:
列项(varchar2有些任意但足够长)
我想要一个select语句,它基本上会选择Items包含“ItemA”,“ItemB”和“ItemC”的所有行,但不必手动打破该字符串。在这种情况下,它将匹配第一行,第二行和第三行,但不匹配第四行。
(编辑)我意识到这个表结构设计得很糟糕。在这个时候,我不知道我们是否可以回到客户端来解决这个问题,因为数据可能已经在其他地方使用,因此重新设计将是昂贵且耗时的。我相信很多人都习惯了这种情况。最初的系统设计很差,现在我已经被引进去咨询由于设计不佳引起的困难。我们假设无法对此表进行规范化,并且必须按原样使用。
考虑到我需要使用的界面的限制,完全有可能我想做的事情根本不可能,但我的SQL知识还不足以确定它。
非常感谢你花时间阅读这个问题。如果有任何混淆或需要扩展或澄清,请告诉我。
答案 0 :(得分:3)
虽然我绝对同意评论员的观点,即数据模型存在缺陷,但有时您必须使用您所提供的内容。如果真的不可能改变数据模型,那么你可以做到这一点,但它并不完全漂亮,并且取决于你的“限制”而不排除使用公用表表达式 - 我看到工具很难解决这些问题。
with items_cte as (
select id, regexp_substr(items, '[^,]+', 1, level) as item, level as pos
from items
connect by level <= regexp_count(items, '[^,]+')
and prior id = id
and prior sys_guid() is not null
),
list_cte as (
select regexp_substr(:list, '[^,]+', 1, level) as item,
count(*) over () as list_length
from dual
connect by level <= regexp_count(:list, '[^,]+')
)
select i.id, listagg(i.item, ',') within group (order by i.pos) as items
from items_cte i
join list_cte l
on l.item = i.item
group by i.id
having count(distinct i.item) = max(l.list_length)
order by i.id;
ID ITEMS
---------- --------------------------------------------
1 ItemA,ItemB,ItemC
2 ItemC,ItemB,ItemA
3 ItemC,ItemA,ItemB,ItemB
这是使用两个常见的表表达式(CTE,也称为子查询因子分解)。它们每个都将逗号分隔的列表拆分为伪路径。 list
细分非常简单,并使用您似乎熟悉的正则表达式函数。 items
一个稍微复杂一点,因为connect by
子句通常不能很好地处理多行。这使用了一个技巧,它使用prior
子句和任何非确定性函数 - sys_guid()
,但你可以使用其他 - 来阻止它循环并混合来自不同原始行的值。我还假设你在桌子上有一个唯一的ID列。
小提琴显示两个单独的分割结果,以及加入这些结果的最终结果。
最后listagg
用于将拆分值放回原始订单中,count(distinct i.item)
检查仅显示list
中所有值匹配的结果。由于distinct
出现两次,因此需要itemB
才能匹配您的第三行。