根据较低级别的结果评估分层查询

时间:2016-09-03 09:20:42

标签: sql oracle oracle11g

我使用的是Oracle Database 11g,而bellow是一个带有虚拟值的虚拟表,我现在试着解释一下:

我有一个表格,用于描述代表"群组的ID之间的关系。和作为群组成员的ID。这些组的成员既可以是简单成员,也可以是某些组本身(没有循环)。因此,一些组是1级的简单组,而有些组可能有很多级别。每组都有一个"最低限度"值和每个组成员具有某种价值,包括当它作为该组成员时的组。

现在,我要做的就是简单地检查一下,组成员值的总和是否等于或大于该组的最小限制值。如果它不是层次结构,那将是相当容易的。问题是如果其中一个成员是一个组,我可以用其成员值 ONLY 来计算,如果它有足够的成员值来满足其最小限制。因此,评估需要从叶到根,以便首先评估最低的组,并根据评估层次结构的较高级别进行评估。

不幸的是我无法解决这个问题。有人可以帮忙吗? 重要提示:如果可能的话,我想在不使用R / CTE的情况下解决这个问题。我无法使用CREATE关键字接受任何答案,因为我限制使用它。

CREATE TABLE "MYGROUPS" 
(   
"MYGROUP_ID" VARCHAR2(20 BYTE), 
"MYGROUP_LIMIT" Number, 
"MEMBER" VARCHAR2(20 BYTE), 
"MEMBER_VALUE" Number
);

insert into mygroups
(Select 'g0'  ,1  ,'00'  ,1 from dual) union
(Select 'g1'  ,5  ,'01'  ,1 from dual) union
(Select 'g1'  ,5  ,'02'  ,1 from dual) union
(Select 'g1'  ,5  ,'03'  ,1 from dual) union
(Select 'g1'  ,5  ,'g2'  ,3 from dual) union
(Select 'g2'  ,3  ,'02'  ,2 from dual) union
(Select 'g2'  ,3  ,'05'  ,2 from dual) union
(Select 'g2'  ,3  ,'g3'  ,2 from dual) union
(Select 'g3'  ,5  ,'03'  ,1 from dual) union
(Select 'g3'  ,5  ,'05'  ,1 from dual)

这组数据应该导致g1 OK,g2 OK,g3 NOT OK。 G1依赖于g2,而g2又不依赖于g3,所以g1也可以。

在另一组数据中:

insert into mygroups
(Select 'g0'  ,1  ,'00'  ,1 from dual) union
(Select 'g1'  ,5  ,'01'  ,1 from dual) union
(Select 'g1'  ,5  ,'02'  ,1 from dual) union
(Select 'g1'  ,5  ,'03'  ,1 from dual) union
(Select 'g1'  ,5  ,'g2'  ,3 from dual) union
(Select 'g2'  ,3  ,'02'  ,1 from dual) union
(Select 'g2'  ,3  ,'05'  ,1 from dual) union
(Select 'g2'  ,3  ,'g3'  ,2 from dual) union
(Select 'g3'  ,5  ,'03'  ,1 from dual) union
(Select 'g3'  ,5  ,'05'  ,1 from dual)

G1不行,因为它取决于g2,这里也依赖于g3,因此这三个都导致NOT OK

这是一个获得想法的查询。 ' OK'值评估该组满足最小限制。 “不行”'恰恰相反。 '不知道'是我不知道如何评估它的问题。

select connect_by_root mygroup_id as root, mygroups.*,level
from ( Select mygroups.*,
             sum(member_value) over (partition by mygroup_id) sum_of_values,
             CASE 
                  WHEN sum (CASE WHEN member like 'g%' THEN 1 END) over (partition by mygroup_id) > 0 THEN 'DUNNO'
                  WHEN sum(member_value) over (partition by mygroup_id) >= mygroup_limit THEN 'OK'
                  WHEN sum(member_value) over (partition by mygroup_id) < mygroup_limit THEN 'NOT OK'
             END eval
        From mygroups ) mygroups
connect by prior member = mygroup_id

此外,任何组都可以包含任意数量级别的任意数量的不同组,并且成员组可以在不同的组中拥有不同的member_values。

2 个答案:

答案 0 :(得分:0)

在构建树时,您需要记住MYGROUP_IDMYGROUP_LIMIT,以便您可以重复使用它。您使用CONNECT_BY_ROOT走在正确的轨道上,但您没有存储限制。

如果成员的价值在层次结构中出现多次,是否应该重复计算,目前还不清楚。我假设一个成员可以被重复计算,因为有些人有不同的价值;要删除此项,您需要执行额外步骤,以便在MEMBERSTARTING_GROUPSTARTING_GROUP_LIMIT

上使您的层次结构与众不同
with all_hierarchies as (
 select member_value
      , connect_by_root mygroup_id as starting_group
      , connect_by_root mygroup_limit as starting_group_limit
   from mygroups m
connect by prior member = mygroup_id
        )
select starting_group
  from all_hierarchies
 group by starting_group
having sum(member_value) >= min(starting_group_limit)

它会记住所有必要的信息,然后评估该组成员是否大于或等于该限制。如果要返回所有数据,则只需将此查询加回层次结构,如:

with all_hierarchies as (
 select member_value
      , connect_by_root mygroup_id as starting_group
      , connect_by_root mygroup_limit as starting_group_limit
   from mygroups m
connect by prior member = mygroup_id
        )
, qualifying_groups as (
select starting_group
  from all_hierarchies
 group by starting_group
having sum(member_value) >= min(starting_group_limit)
       )
select a.*
  from all_hierarchies a
  join qualifying_groups q
    on a.starting_group = q.starting_group

这确实使用了CTE,但仅仅是为了整洁。如果你愿意,你可以摆脱它。我会考虑使用递归CTE,因为这是标准SQL,使您的查询更具可移植性。

答案 1 :(得分:0)

我尝试用“connect by”来解决问题。但我找不到将计算结果传递给下一级递归的方法。

随着R / CTE遇到类似的问题。此外,由于无法在R / CTE中使用“组”和“窗口”功能,因此存在很大的局限性。尝试在递归部分中使用“CTE别名”两次(用于连接上一行)会导致ORA-06000(内部服务器错误)并断开与Oracle的连接。

今天,我想在单行的帮助下提供“R / CTE”重复循环(以下简称“光标行”),并在这个“游标行”中保持一个状态(OK组的列表) ”。必要的数据行,按照“从叶子到根”的顺序预先编号,在每次迭代时按行号连接到“光标行”。

with GRP as (
     select A.*, row_number() over (order by L) N
       from (
         select mygroup_id, min(level) L
           from  mygroups m
          start with not exists(select 1 from mygroups m2
                                 where m2.mygroup_id=m.mygroup_id and m2.member like 'g%'
                               )
        connect by member=prior mygroup_id
          group by mygroup_id
       ) A
),
Cursor_tab(mygroup_id,N,list,result) as(
   select NULL,N,lpad(',',2000),0
     from GRP where N=1
 union all
   select G.mygroup_id,Q.N+1,
          ltrim(Q.list)||decode(R.column_value,0,'',G.mygroup_id||','),
          TO_NUMBER(R.column_value)
     from Cursor_tab Q, GRP G,
          table(cast(multiset(
           select decode(sign(sum(member_value)-min(mygroup_limit)),-1,0,1)
             from mygroups m
            where m.mygroup_id=G.mygroup_id
              and (member not like 'g%' or Q.list like '%,'||member||',%')
          ) as sys.odcivarchar2list)) R
    where G.N=Q.N
)
select mygroup_id, decode(result,1,'OK','NOT OK')
  from Cursor_tab
 where mygroup_id is not null