如何在Oracle中汇总其中包含重复值的行范围?

时间:2013-07-24 23:00:26

标签: oracle plsql

我有一个Oracle 11数据库表,可以跟踪每个部门的库存项目状态。

ITEM_NUMBER     DEPT_NO       STATUS
-----------     ---------     ---------
1               AAA           OK
2               AAA           OK
3               AAA           MISSING
4               BBB           OK
5               BBB           OK
6               AAA           OK
7               AAA           OK

我想创建一个视图,按部门和状态显示每个项目范围。如果范围内的任何项目都是MISSING,则状态应为MISSING,否则应该没问题。

在上面的示例中,视图输出应为:

START   END     DEPT_NO   STATUS
------  -----   -------   ------------
1       3       AAA       MISSING
4       5       BBB       OK
6       7       AAA       OK

如果部门只有一组记录,这将是一件简单的事情 - 但是,相同的部门可以显示在不同的范围内,因此使用MIN / MAX执行GROUP BY不起作用,因为它最终会汇总部门AAA的两个范围为一个:

select dept_no, min(item_number), max(item_number), min(status)
from inventory
group by dept_no

START   END     DEPT_NO   STATUS
------  -----   -------   ------------
1       7       AAA       MISSING
4       5       BBB       OK

这可以使用数据库视图完成还是太复杂?

4 个答案:

答案 0 :(得分:1)

好的,这应该有效

ps:结束和开始是保留字,我无法使用它们。我刚刚添加了一封信,但这仍然是一个明确的结果......

SELECT
    i.sstart,
    i.eend,
    i.dept_no,
    MIN(inventory.status) AS status
FROM (
    SELECT 
        DECODE(r, 1, 1, (LAG(max_range_item, 1) OVER(ORDER BY r) + 1)  ) AS sstart,
        max_range_item AS eend,
        dept_no
    FROM (
        SELECT
            rownum AS r,
            item_number AS max_range_item,
            dept_no
        FROM (
                SELECT 
                    item_number, 
                    dept_no, 
                    LEAD(dept_no, 1) OVER (ORDER BY item_number) AS next_dept_no
                FROM
                    inventory
                ORDER BY
                    item_number ASC
              ) i2
        WHERE
            dept_no != next_dept_no
        OR  next_dept_no IS NULL
        ORDER BY
            item_number ASC
        ) inv
    ) i
        JOIN inventory ON inventory.item_number BETWEEN i.sstart AND i.eend
GROUP BY
    i.sstart,
    i.eend,
    i.dept_no
ORDER BY
    i.sstart ASC

小提琴:http://sqlfiddle.com/#!4/944ff3/17

答案 1 :(得分:1)

另一种选择(适用于11g)

WITH qry( item_number, dept_no, status, range_id ) as (
    select item_number, dept_no, status, 1 range_id
    from inventory where item_number = 1
    UNION ALL
    select src.item_number, src.dept_no, src.status,  
         case when src.dept_no <> qry.dept_no
              then qry.range_id + 1 else qry.range_id
         end
    from inventory src
    join qry on src.item_number = qry.item_number + 1
)
SELECT range_id, dept_no, min(item_number), max(item_number), min(status)
from qry
group by range_id, dept_no
order by 1
;

  RANGE_ID DEPT_NO MIN(ITEM_NUMBER) MAX(ITEM_NUMBER) MIN(STATUS)
---------- ------- ---------------- ---------------- -----------
         1 AAA                    1                3 MISSING     
         2 BBB                    4                5 OK          
         3 AAA                    6                7 OK    

答案 2 :(得分:1)

你可以使用窗口函数解决这个问题,但是可以解决这个问题。第一种方法是使用lag()来确定新“部门”部门的起点。第二个是该值的累积和。累积和作为分组ID,然后可用于聚合。

select dept_no, min(item_number), max(item_number),
       (case when sum(case when status = 'MISSING' then 1 else 0 end) > 0
             then 'Missing'
             else 'Ok'
        end)
from (select i.*, sum(GroupStart) over (order by item_number) as Grouping
      from (select i.*,
                   (case when dept_no = lag(dept_no) over (order by item_number)
                         then 0 else 1
                    end) as GroupStart
            from inventory i
           ) i
     ) i
group by dept_no, grouping;

答案 3 :(得分:0)

如果可以使用数据库视图完成此操作,我认为这将非常复杂。您可以研究的一个选项是尝试创建一个内部查询,该查询是在item_number = item_number + 1上连接到自身的同一个表,并添加一个列,用于检测a.item_number!= b.item编号的位置以确定组边界和然后在外部查询中使用此组边界列作为分组。

然而,问题显然更适合于一次循环一行的程序方法。因此,如果可以的话,我建议将其作为pl / sql程序。