使用有限的Oracle SQL将逗号分隔列表中的项目与逗号分隔列表中的另一组项目进行比较

时间:2014-10-27 15:12:14

标签: sql oracle csv select oracle10g

我想编写一个简单的Oracle SQL 10g语句,将任意定义的逗号分隔列表中的项(字符串)与包含逗号分隔字符串列表的字段进行比较。定义列表中的项目可以按任何顺序出现在字段中,并且需要找到完全匹配(而不是子字符串)。

我有一个使用手动输入的一系列regexp_like()语句的工作解决方案,但我需要将其交给一个将继续保持此前进的客户端,并且希望能够只更新逗号分隔的字符串直接

我还有一些基于软件GUI的限制,我可以使用Oracle SQL来完成此任务。具体来说,我不能使用任何PL / SQL,这必须写在一个select语句中(没有临时表或任何有趣/有用的东西。)我已经找到了许多我想要完成的解决方案,但是几乎所有人都依赖于能够编写自定义函数。

所以,既然背景/限制已经不在了,那么让我们深入了解细节。

示例任意(客户提供)列表:ItemA,ItemB,ItemC

表项目:
列项(varchar2有些任意但足够长)

  • 意达,ItemB,ItemC
  • ItemC,ItemB,意达
  • ItemX,ItemC,ItemY,意达,ItemB,ItemB
  • ItemX,ItemY,ItemC

我想要一个select语句,它基本上会选择Items包含“ItemA”,“ItemB”和“ItemC”的所有行,但不必手动打破该字符串。在这种情况下,它将匹配第一行,第二行和第三行,但不匹配第四行。

(编辑)我意识到这个表结构设计得很糟糕。在这个时候,我不知道我们是否可以回到客户端来解决这个问题,因为数据可能已经在其他地方使用,因此重新设计将是昂贵且耗时的。我相信很多人都习惯了这种情况。最初的系统设计很差,现在我已经被引进去咨询由于设计不佳引起的困难。我们假设无法对此表进行规范化,并且必须按原样使用。

考虑到我需要使用的界面的限制,完全有可能我想做的事情根本不可能,但我的SQL知识还不足以确定它。

非常感谢你花时间阅读这个问题。如果有任何混淆或需要扩展或澄清,请告诉我。

1 个答案:

答案 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

SQL Fiddle

这是使用两个常见的表表达式(CTE,也称为子查询因子分解)。它们每个都将逗号分隔的列表拆分为伪路径。 list细分非常简单,并使用您似乎熟悉的正则表达式函数。 items一个稍微复杂一点,因为connect by子句通常不能很好地处理多行。这使用了一个技巧,它使用prior子句和任何非确定性函数 - sys_guid(),但你可以使用其他 - 来阻止它循环并混合来自不同原始行的值。我还假设你在桌子上有一个唯一的ID列。

小提琴显示两个单独的分割结果,以及加入这些结果的最终结果。

最后listagg用于将拆分值放回原始订单中,count(distinct i.item)检查仅显示list中所有值匹配的结果。由于distinct出现两次,因此需要itemB才能匹配您的第三行。