获取所有没有孩子的父母,或者如果有孩子,请获取最新的孩子

时间:2018-11-07 19:14:05

标签: sql

我有下表的订单。 orderId是主键,parentOrderId是表示此订单是否具有父订单的列。

例如:1,2,3,4没有母订单。 5,6,7,8有父订单。

+--------------------+----------------------+
|          order_id   | parent_order_id     |
+--------------------+----------------------+
|                  1 |                  null|
|                  2 |                  null|
|                  3 |                  null|
|                  4 |                  null|
|                  5 |                    1 |
|                  6 |                    2 |
|                  7 |                    3 |
|                  8 |                    3 |
+--------------------+----------------------+



I need to query all Parents with no children or if there are children only get the latest child. 
The result I need is : 4,5,6,8
   4 because it has no children and should be returned.
   5 because it is the only child of 1.
   6 because that is the only child of 2.
   8 because 3 has 2 children(7,8) and I need to return latest child. Pick max of orderId's.

我尝试过的事情:

SELECT
  MAX(order_id)
FROM
  orders
WHERE
  parent_order_id IS NOT NULL
GROUP BY
  parent_order_id
UNION ALL
SELECT
  order_id
FROM
  orders
WHERE
  parent_order_id IS NULL
MINUS
SELECT
  parent_order_id
FROM
  orders;

我正在寻找什么:

上面的查询返回4,5,6,8。问题是我正在将此查询输入IN子句,并且oracle对于IN子句有1000个限制。我试图看看是否有更好的方法使用联接来解决此问题。 更新: 联接将帮助我检索订单表中的所有列,而不仅仅是id,为简单起见,我仅包括两列,表中还有更多列。现在,我正在获取id的第一个并将其输入子句中的另一个查询中,以获取与这些id匹配的所有列。

我还在寻找非特定于供应商的sql。

谢谢您的帮助。

2 个答案:

答案 0 :(得分:1)

这里是使用connect by查询(实际上是一种联接形式-针对像您这样的分层数据进行了优化)的一种解决此问题的方法。 WITH子句不是解决方案的一部分,它仅用于模拟您的输入。使用实际的表名和列名。

请注意,我在结果中也得到带有order_id = 5的行(我在评论中问过这一点,您回答了其他问题,但不是这个问题)。

这显示了如何一次性获得所需的所有列。

with
  orders_table (order_id, parent_order_id) as (
    select 1, null from dual union all
    select 2, null from dual union all
    select 3, null from dual union all
    select 4, null from dual union all
    select 5, 1    from dual union all
    select 6, 2    from dual union all
    select 7, 3    from dual union all
    select 8, 3    from dual
  )
select order_id, parent_order_id
from   (
         select     o.*
              ,     max(order_id)
                        over (partition by connect_by_root order_id) as max_id
         from       orders_table o
         where      connect_by_isleaf = 1
         start with parent_order_id is null
         connect by parent_order_id = prior order_id
       )
where  order_id = max_id
;

ORDER_ID  PARENT_ORDER_ID
--------  ---------------
       5                1
       6                2
       8                3
       4    

答案 1 :(得分:1)

OP在对我的其他答案的评论中解释说,他需要一个尽可能使用标准SQL功能的查询。这样就排除了connect by查询,也排除了nvl()这样简单的功能。

下面的查询得到相同的结果。它的通用性较低,但是可以解决OP的问题(那里只有父母和孩子,而没有“第三代”节点)。

就像其他答案一样,它的编写使得可以选择原始表中的所有列(或一些相关子集)。最好使用解析函数来完成,就像我在其他《答案》中所做的一样。

with
  orders_table (order_id, parent_order_id) as (
    select 1, null from dual union all
    select 2, null from dual union all
    select 3, null from dual union all
    select 4, null from dual union all
    select 5, 1    from dual union all
    select 6, 2    from dual union all
    select 7, 3    from dual union all
    select 8, 3    from dual
  )
select order_id, parent_order_id
from   (
         select o.* 
              , max(order_id) over 
                     (partition by coalesce(parent_order_id, order_id)) as max_id
         from   orders_table o
       )
where order_id = max_id
;