通过单个源表中的派生表索引和匹配标头

时间:2015-03-02 15:35:22

标签: oracle

我正在使用隔离的源代码并尝试创建衍生表查询,该查询通过特殊字符标识标头并将标头与其列表项匹配。我的目标是将最终结果纳入Oracle的视图。

我从用户生成的单个表开始:

TASK_ID TASK_NAME
      1 ******HEADER 1******
      2 TASK A
      3 TASK B
      4 TASK C
      5 ******HEADER 2******
      6 TASK D
      7 TASK E
      8 ******HEADER 3******
      9 TASK F
     10 TASK G
     11 ******HEADER 4******
     12 TASK H
     13 TASK I
     14 TASK J

我把它分解为两个派生表:

A.TASK_ID A.TASK_NAME
        1 ******HEADER 1******
        5 ******HEADER 2******
        8 ******HEADER 3******
       11 ******HEADER 4******  

B.TASK_ID B.TASK_NAME
        2 TASK A
        3 TASK B
        4 TASK C
        6 TASK D
        7 TASK E
        9 TASK F
       10 TASK G
       12 TASK H
       13 TASK I
       14 TASK J

但是我被困在试图来到这里:

A.TASK_ID A.TASK_NAME          B.TASK_ID B.TASK_NAME
        1 ******HEADER 1******         2 TASK A
        1 ******HEADER 1******         3 TASK B
        1 ******HEADER 1******         4 TASK C
        5 ******HEADER 2******         6 TASK D
        5 ******HEADER 2******         7 TASK E
        8 ******HEADER 3******         9 TASK D
        8 ******HEADER 3******        10 TASK E
       11 ******HEADER 4******        12 TASK H
       11 ******HEADER 4******        13 TASK I
       11 ******HEADER 4******        14 TASK J

如果我使用'Where'或'Between'命令编写一个小于或大于的SQL,则标题会捕获意外的任务。

所以我不知道该怎么做。如果我必须编写一个程序,我不知道我是如何对PL / SQL新手的。我知道C#和VB以及其他编程,但我不确定我的方法是否错误。

非常感谢任何指导!

3 个答案:

答案 0 :(得分:4)

假设标题和其他任务总是按照您显示的顺序排序 - 因为否则我看不到您如何建立任何其他关系 - 并且您始终以标题开头,然后您可以将标题应用于标题你使用an analytic function like row_number()

来挑选出来
select task_id, task_name,
  row_number() over (order by task_id) as rnk
from tasks
where task_name like '***%';

   TASK_ID TASK_NAME                   RNK
---------- -------------------- ----------
         1 ******HEADER 1******          1 
         5 ******HEADER 2******          2 
         8 ******HEADER 3******          3 
        11 ******HEADER 4******          4 

你可以对非标题记录做同样的事情;但是这里的排名是他们的原始订单(包括标题)之间的差异,以及一旦删除它们之后:

select task_id, task_name, orig_rnk,
  row_number() over (order by task_id) as new_rnk,
  orig_rnk - row_number() over (order by task_id) as rnk
from (
  select task_id, task_name, row_number() over (order by task_id) as orig_rnk
  from tasks
)
where task_name not like '***%';

   TASK_ID TASK_NAME              ORIG_RNK    NEW_RNK        RNK
---------- -------------------- ---------- ---------- ----------
         2 TASK A                        2          1          1 
         3 TASK B                        3          2          1 
         4 TASK C                        4          3          1 
         6 TASK D                        6          4          2 
         7 TASK E                        7          5          2 
         9 TASK F                        9          6          3 
        10 TASK G                       10          7          3 
        12 TASK H                       12          8          4 
        13 TASK I                       13          9          4 
        14 TASK J                       14         10          4 

然后你可以使用该排名加入这两个列表:

with a as (
  select task_id, task_name, row_number() over (order by task_id) as rnk
  from tasks
  where task_name like '***%'
),
b as (
  select task_id, task_name,
    orig_rnk - row_number() over (order by task_id) as rnk
  from (
    select task_id, task_name, row_number() over (order by task_id) as orig_rnk
    from tasks
  )
  where task_name not like '***%'
)
select a.task_id, a.task_name, b.task_id, b.task_name
from a
join b on b.rnk = a.rnk
order by a.task_id, b.task_id;

   TASK_ID TASK_NAME               TASK_ID TASK_NAME           
---------- -------------------- ---------- --------------------
         1 ******HEADER 1******          2 TASK A               
         1 ******HEADER 1******          3 TASK B               
         1 ******HEADER 1******          4 TASK C               
         5 ******HEADER 2******          6 TASK D               
         5 ******HEADER 2******          7 TASK E               
         8 ******HEADER 3******          9 TASK F               
         8 ******HEADER 3******         10 TASK G               
        11 ******HEADER 4******         12 TASK H               
        11 ******HEADER 4******         13 TASK I               
        11 ******HEADER 4******         14 TASK J               

SQL Fiddle demo

答案 1 :(得分:3)

与Alex Poole(并做出相同的假设)一样,我倾向于使用分析函数来解决这个问题。但是,对我来说,这似乎是lead的一个很好的候选人:

select task_id, task_name,
  lead(task_id) over (order by task_id) as next_task_id
from tasks
where task_name like '***%';

   TASK_ID TASK_NAME            NEXT_TASK_ID
---------- -------------------- ------------
         1 ******HEADER 1******            5 
         5 ******HEADER 2******            8 
         8 ******HEADER 3******           11 
        11 ******HEADER 4******             

使用这些数据,下一步变得相当简单:

with a as (
  select task_id, task_name, lead(task_id) over (order by task_id) as next_task_id
  from tasks
  where task_name like '***%'
),
b as (
  select task_id, task_name
  from tasks
  where task_name not like '***%'
)
select a.task_id, a.task_name, b.task_id, b.task_name
from a
join b on b.task_id between a.task_id and coalesce(a.next_task_id,b.task_id)
order by a.task_id, b.task_id;

答案 2 :(得分:2)

以下是一些基于您最初发布的数据的解决方案 - 希望您可以根据您的特定数据集适当修改它们。 (我已经包含了两个,因为最终可能会比另一个表现得更好,但我无法说出哪些因为我没有完整的数据集!):

(这个第一个例子最初以更复杂的方式计算了header_id,但Alex Poole指出它可以像查找header_name一样完成。我已经更新它以使用Alex'的建议,因为它明显优越!)

with sample_data as (select 1 task_id, '******HEADER 1******' task_name from dual union all
                     select 2 task_id, 'TASK A' task_name from dual union all
                     select 3 task_id, 'TASK B' task_name from dual union all
                     select 4 task_id, 'TASK C' task_name from dual union all
                     select 5 task_id, '******HEADER 2******' task_name from dual union all
                     select 6 task_id, 'TASK D' task_name from dual union all
                     select 7 task_id, 'TASK E' task_name from dual union all
                     select 8 task_id, '******HEADER 3******' task_name from dual union all
                     select 9 task_id, 'TASK F' task_name from dual union all
                     select 10 task_id, 'TASK G' task_name from dual union all
                     select 11 task_id, '******HEADER 4******' task_name from dual union all
                     select 12 task_id, 'TASK H' task_name from dual union all
                     select 13 task_id, 'TASK I' task_name from dual union all
                     select 14 task_id, 'TASK J' task_name from dual),
             res as (select task_id,
                            last_value(case when task_name like '*%' then task_id end ignore nulls) over (order by task_id) header_id,
                            last_value(case when task_name like '*%' then task_name end ignore nulls) over (order by task_id) header_name,
                            case when task_name not like '*%' then task_name end task_name
                     from   sample_data)
select header_id,
       header_name,
       task_id,
       task_name
from   res
where  header_id != task_id
order by header_id, task_id;

 HEADER_ID HEADER_NAME             TASK_ID TASK_NAME           
---------- -------------------- ---------- --------------------
         1 ******HEADER 1******          2 TASK A              
         1 ******HEADER 1******          3 TASK B              
         1 ******HEADER 1******          4 TASK C              
         5 ******HEADER 2******          6 TASK D              
         5 ******HEADER 2******          7 TASK E              
         8 ******HEADER 3******          9 TASK F              
         8 ******HEADER 3******         10 TASK G              
        11 ******HEADER 4******         12 TASK H              
        11 ******HEADER 4******         13 TASK I              
        11 ******HEADER 4******         14 TASK J   

with sample_data as (select 1 task_id, '******HEADER 1******' task_name from dual union all
                     select 2 task_id, 'TASK A' task_name from dual union all
                     select 3 task_id, 'TASK B' task_name from dual union all
                     select 4 task_id, 'TASK C' task_name from dual union all
                     select 5 task_id, '******HEADER 2******' task_name from dual union all
                     select 6 task_id, 'TASK D' task_name from dual union all
                     select 7 task_id, 'TASK E' task_name from dual union all
                     select 8 task_id, '******HEADER 3******' task_name from dual union all
                     select 9 task_id, 'TASK F' task_name from dual union all
                     select 10 task_id, 'TASK G' task_name from dual union all
                     select 11 task_id, '******HEADER 4******' task_name from dual union all
                     select 12 task_id, 'TASK H' task_name from dual union all
                     select 13 task_id, 'TASK I' task_name from dual union all
                     select 14 task_id, 'TASK J' task_name from dual),
             hdr as (select task_id hdr_id,
                            task_name hdr_name
                     from   sample_data
                     where  task_name like '*%'),
             tsk as (select task_id,
                            last_value(case when task_name like '*%' then task_name end ignore nulls) over (order by task_id) header_name,
                            case when task_name not like '*%' then task_name end task_name
                     from   sample_data)
select hdr.hdr_id,
       hdr.hdr_name,
       tsk.task_id,
       tsk.task_name
from   hdr
       inner join tsk
         on (hdr.hdr_name = tsk.header_name
             and hdr.hdr_id != tsk.task_id)
order by hdr.hdr_id, tsk.task_id;

    HDR_ID HDR_NAME                TASK_ID TASK_NAME           
---------- -------------------- ---------- --------------------
         1 ******HEADER 1******          2 TASK A              
         1 ******HEADER 1******          3 TASK B              
         1 ******HEADER 1******          4 TASK C              
         5 ******HEADER 2******          6 TASK D              
         5 ******HEADER 2******          7 TASK E              
         8 ******HEADER 3******          9 TASK F              
         8 ******HEADER 3******         10 TASK G              
        11 ******HEADER 4******         12 TASK H              
        11 ******HEADER 4******         13 TASK I              
        11 ******HEADER 4******         14 TASK J