使用CONNECT BY获取分层查询的每个级别的计数/总计

时间:2012-09-10 22:11:45

标签: sql oracle

我对此感到很沮丧。我正在尝试针对具有递归关系(分层)的表编写查询(使用Oracle),并获取存储在树中每个节点下方的另一个表中的记录总数。另一个表只有与叶节点相关的记录。但是,我希望得到树中每个节点及其下方的总计。例如,假设我有两张桌子。 DIRS包含目录名称和标识目录结构的递归关系,FILES包含带有DIRS外键的文件信息,指示文件所在的目录:

DIRS
====
DIR_ID 
PARENT_DIR_ID
DIR_NAME

FILES
=====
FILE_ID
FILE_NAME
DIR_ID
FILE_SIZE

如果DIRS包含:

DIR_ID   PARENT_DIR_ID   DIR_NAME
======   =============   ========
1                        ROOT
2        1               DIR1_1
3        1               DIR1_2
4        2               DIR2_1
5        2               DIR2_2

和FILES包含

FILE_ID   FILE_NAME   DIR_ID   FILE_SIZE
=======   =========   ======   =========
1         test1.txt   5        100
2         test2.txt   5        200
3         test5.txt   5         50 
4         test3.txt   3        300
5         test4.txt   3        300
6         test6.txt   4        100

我想要一个返回路径的查询以及层次结构中每个节点中或下面的文件数。基本上是文件数量的汇总。因此查询结果如下所示:

Path                    File_Count
=====                   ===========
/ROOT                   6
/ROOT/DIR1_1            4
/ROOT/DIR1_1/DIR2_1     1
/ROOT/DIR1_1/DIR2_2     3
/ROOT/DIR1_2            2

更新 SQL脚本,用于创建包含与上述内容匹配的示例数据的表:

create table DIRS (dir_id number(38) primary key
    , parent_dir_id number(38) null references DIRS(dir_id)
    , dir_name varchar2(128) not null);

create table FILES (file_id number(38) primary key
    , file_name varchar2(128) not null
    , dir_id number(38) not null references DIRS(dir_id)
    , file_size number not null
    , unique (dir_id, file_name));

insert into DIRS 
select 1, null, 'ROOT' from dual
union all select 2, 1, 'DIR1_1' from dual 
union all select 3, 1, 'DIR1_2' from dual 
union all select 4, 2, 'DIR2_1' from dual 
union all select 5, 2, 'DIR2_2' from dual;

insert into files
select 1, 'test1.txt', 5, 100 from dual
union all select 2, 'test2.txt', 5, 200 from dual
union all select 3, 'test5.txt', 5, 50 from dual
union all select 4, 'test3.txt', 3, 300 from dual
union all select 5, 'test4.txt', 3, 300 from dual
union all select 6, 'test6.txt', 4, 100 from dual;

commit;

2 个答案:

答案 0 :(得分:4)

这个很简单:

09:38:54 HR@vm_xe> l                                      
  1  select sys_connect_by_path(dp.dir_name, '/') path    
  2         ,(select count(file_id)                       
  3             from dirs dc                              
  4                  ,files f                             
  5            where f.dir_id(+) = dc.dir_id              
  6          connect by prior dc.dir_id = dc.parent_dir_id
  7            start with dc.dir_id = dp.dir_id           
  8          ) count                                      
  9    from dirs dp                                       
 10    connect by prior dp.dir_id = dp.parent_dir_id      
 11*   start with dp.parent_dir_id is null                
09:38:55 HR@vm_xe> /                                      

PATH                                COUNT                 
------------------------------ ----------                 
/ROOT                                   6                 
/ROOT/DIR1_1                            4                 
/ROOT/DIR1_1/DIR2_1                     1                 
/ROOT/DIR1_1/DIR2_2                     3                 
/ROOT/DIR1_2                            2                 

5 rows selected.                                          

Elapsed: 00:00:00.02                                      

答案 1 :(得分:1)

select sys_connect_by_path(D.dir_name, '/'), S.count_distinct_file_id
from DIRS D
inner join (select subtree_root_dir_id
            , count(distinct file_id) count_distinct_file_id
        from (select distinct connect_by_root D.DIR_ID subtree_root_dir_id
                    , F.file_id 
                from DIRS D
                left outer join FILES F on F.dir_id = D.dir_id
                start with 1=1 connect by prior D.dir_id = D.parent_dir_id)
        group by subtree_root_dir_id) S
    on D.dir_id = S.subtree_root_dir_id
start with D.dir_id = 1 connect by prior D.dir_id = D.parent_dir_id

给出你要求的结果,但我的直觉说我没有看到什么,而且查询可以更简单。 (请接受这个答案,直到几天过去,希望有人提出更好的答案。)