如何编写一个程序来查找这样的一组行?

时间:2010-10-19 10:42:33

标签: sql oracle plsql

对于垃圾问题标题感到抱歉。我有一张SET_DEFINITIONS这样的表:

SETKEY      NOT NULL    NUMBER(38)
SETENTRY    NOT NULL    NUMBER(38)

其中的想法是行定义了数字集。例如,该表可以包含行:

1 2
1 4
2 1
2 2

这意味着集合1是{2,4},集合2是{1,2}。我想写一个函数

function selectOrInsertSet(table of number(38) numbers) return number(38)

将返回一个集合的密钥,该集合的密钥与传入的表中的成员相同(或者如果它不存在则创建这样的集合)。在PL / SQL中执行此操作的好方法是什么?

编辑:我目前正在处理的解决方案是这样的(我不确定它是否可行):

  1. 选择具有第一个元素的所有键到某个集合c
  2. 通过与包含其他元素的连续键集相交来细化集合c

3 个答案:

答案 0 :(得分:2)

您可以在每个集合和数字集合之间使用完全外部联接,以查看它们是否相同。这个功能可以做到:

function selectOrInsertSet(numbers number_tt) return number
is
  l_diff number;
  l_retval number;
begin
  for r in (select distinct setkey from set_definitions)
  loop
     with d as (select column_value from table(numbers)),
          s as (select setentry from set_definitions where setkey=r.setkey)
     select count(*)
     into   l_diff
     from   s
     full outer join d on d.column_value = s.setentry
     where s.setentry is null or d.column_value is null;

     if l_diff = 0 then
        l_retval := r.setkey;
        exit;
     end if;
  end loop;

  return l_retval;
end;

如果找到则返回setkey,否则返回null。

如果没有找到,我还没有实现关于创建新集的部分,但这应该很容易。我个人并不喜欢有副作用的函数(在这种情况下,将行插入表中)。

答案 1 :(得分:0)

查找交集的快速方法可能是创建一个全局临时表,并使用传入的数字表填充它。然后,您可以使用SET_DEFINITIONS连接此表以查找所有可能的匹配项。您需要检查每个匹配集中的总数以消除超集。

创建一些基表......

create table set_definitions (
    setkey number,
    setentry number,
    constraint pk_set_definitions primary key (setkey, setentry)
    );

insert into set_definitions values (1,2);
insert into set_definitions values (1,4);
insert into set_definitions values (2,1);
insert into set_definitions values (2,2);
insert into set_definitions values (3,1);
insert into set_definitions values (3,2);
insert into set_definitions values (3,3);

创建一个全局临时表来保存传递的值:

create global temporary table tmp_setentry (
    setentry number, 
    constraint pk_tmp_setentry primary key (setentry));

insert into tmp_setentry values (1);
insert into tmp_setentry values (2);

加入set_definitions以查找匹配的集合:

select
    setkey
from
    (
    select
        setkey,
        count(*) num_matches,
        (select count(*) from set_definitions where setkey = s.setkey)
            num_set_entries,
        (select count(*) from tmp_setentry) num_entries
    from
        set_definitions s
            inner join tmp_setentry t on t.setentry = s.setentry
    group by
        setkey
    )
where
    num_matches = num_entries
and num_set_entries = num_entries

--> 2 (3 is dropped as a superset)

希望这有帮助。

答案 2 :(得分:0)

Oracle 11g引入了LISTAGG功能,可以用于您需要的功能。 以下面的例子作为想法,因为我对oracle并不熟悉,但它可能适用于一些小的修正):

Create table set_definitions (setkey int, setentry int);
Create table searchFor (setentry int);

insert into set_definitions values (1,4);
insert into set_definitions values (2,1);
insert into set_definitions values (2,2);
insert into set_definitions values (3,1);
insert into set_definitions values (3,2);
insert into set_definitions values (3,3);

Insert Into searchFor Values (1);
Insert into searchFor Values (2);

With Prepare as 
(
Select setkey, LISTAGG(setentry, ',') WITHIN GROUP (ORDER BY setentry) as EntryList
  From set_definitions       
 Group by setkey
 Having Count(*)=(Select Count(*) From searchFor) -- Just to eliminate obvious ones 
)
Select setkey
  from Prepare 
 Where EntryList = (Select LISTAGG(setentry, ',')  WITHIN GROUP (ORDER BY setentry) From searchFor);