在Oracle中的每个级别计算最小值的分层查询

时间:2014-03-07 16:23:52

标签: sql oracle hierarchy hierarchical-data

我已经开始和关闭这个问题几天了,而且我无处可去。

我有一个类似于此的数据集:

NAME                EXPIRATION          PARENT_ID    CLASS_ID           
Master Class           365                              1               
Second Class           366                  1           2                 
Third Class            355                  2           3                 
Fourth Class           1001                 2           4                 
Fifth Class            1000                 4           5
Sixth Class            999                  4           6  

等。等。

我可以使用分层查询来查看某个类下所需的类。

我真正想知道的是层次结构的当前级别和子级别的最小到期日期。到期是其下任何事情的最短期满。

如果我想以强力方式执行此操作,我可以获取分层查询的结果,然后为每一行运行一个如下所示的查询:

select min(expiration_date)
from ( start with class_id = $EACH_CLASS_ID_FROM_PREVIOUS_QUERY
connect by prior parent_id = class_id);

我正在想象这样的结果:

NAME             EXPIRATION                           CLASS_ID
Master Class      355 (Min of it or anything under it)     1
Second Class      355  ""                                  2
Third Class       355  ""                                  3
Fourth Class      999  ""                                  4
Fifth Class       1000 ""                                  5
Sixth Class       999  ""                                  6

我假设有更好的方法吗?可能?

感谢您的帮助,我几天来一直在为此感到困惑。

2 个答案:

答案 0 :(得分:2)

你的桌子:

SQL> create table mytable (name,expiration,parent_id,class_id)
  2  as
  3  select 'Master Class', 365, null, 1 from dual union all
  4  select 'Second Class', 366, 1, 2 from dual union all
  5  select 'Third Class', 355, 2, 3 from dual union all
  6  select 'Fourth Class', 1001, 2, 4 from dual union all
  7  select 'Fifth Class', 1000, 4, 5 from dual union all
  8  select 'Sixth Class', 999, 4, 6 from dual
  9  /

Table created.

使用良好的旧连接语法:

SQL> with t as
  2  ( select connect_by_root class_id as class_id
  3         , connect_by_root name as name
  4         , expiration
  5      from mytable
  6   connect by parent_id = prior class_id
  7  )
  8  select class_id
  9       , name
 10       , min(expiration)
 11    from t
 12   group by class_id
 13       , name
 14   order by class_id
 15  /

  CLASS_ID NAME         MIN(EXPIRATION)
---------- ------------ ---------------
         1 Master Class             355
         2 Second Class             355
         3 Third Class              355
         4 Fourth Class             999
         5 Fifth Class             1000
         6 Sixth Class              999

6 rows selected.

如果您使用的是11g Release 2或更高版本,则可以使用递归子查询因子分析:

SQL> with all_paths (root_class_id,root_name,class_id,expiration) as
  2  ( select class_id
  3         , name
  4         , class_id
  5         , expiration
  6      from mytable
  7     union all
  8    select ap.root_class_id
  9         , ap.root_name
 10         , t.class_id
 11         , t.expiration
 12      from mytable t
 13           inner join all_paths ap on (t.parent_id = ap.class_id)
 14  )
 15  select root_class_id as class_id
 16       , root_name as name
 17       , min(expiration)
 18    from all_paths
 19   group by root_class_id
 20       , root_name
 21   order by class_id
 22  /

  CLASS_ID NAME         MIN(EXPIRATION)
---------- ------------ ---------------
         1 Master Class             355
         2 Second Class             355
         3 Third Class              355
         4 Fourth Class             999
         5 Fifth Class             1000
         6 Sixth Class              999

6 rows selected.

答案 1 :(得分:2)

如果您想在分层查询中找到最短到期日期,那么您可以尝试这样的事情:

select t.*,
       min(t.expiration) 
       over(partition by connect_by_root class_id) min_expiration_date
  from your_table t
connect by prior t.class_id = t.parent_id;

要获取不同的记录,请使用其他查询进行包装:

select q.*
  from (select t.*,
               min(t.expiration) 
               over(partition by connect_by_root class_id) min_expiration_date,
               level lvl
          from your_table t
        connect by prior t.class_id = t.parent_id) q
 where q.lvl = 1;

此方法的好处是您可以在外部查询中使用不同的WHERE条件来获得任何所需的结果。在上面的示例中,结果集只是根节点。如果你稍微修改它,你可以得到一个完整的树(同时保留最小的到期值):

select q.*
  from (select t.*,
               min(t.expiration) 
               over(partition by connect_by_root class_id) min_expiration_date,
               connect_by_root class_id root_node
          from your_table t
        connect by prior t.class_id = t.parent_id) q
 where q.root_node = 2;