【问题标题】:Hierarchical Query / base on table input分层查询/基于表输入
【发布时间】:2016-02-25 19:31:28
【问题描述】:

账户表:

      ID_ACC    PARENT_ACC
        76543    76543      <-edit
        18252    76543
        34456    76543
        456567   76543
        457564   65424
        45654242 6556756

信息表:

      ID     account_id  product_id
    875621     18252       98567
    875621     34456       98567

产品表:

    ACCOUNTID  PRODUCT_ID 
      456452     98567

帐户表包含帐户的 ID。但是一些账户(ID_ACC)有父账户(Parent_acc) - 这个例子展示了

ID_ACC:   18252,
          34456,
          456567 

具有相同的 parent_acc (76543)。

一些孩子有信息(信息表)

(本例表示1825234456与信息表连接)

接下来,一些 info.account_id 包含产品(产品表)(在此示例中 - 只有 18252 accountid 的产品具有帐号:456452。)

Account.id_acc=Info.account_id
Info.product_id=Product_id.product_id

我想选择所有帐户层次结构:

76543,
18252,
34456,
456567,
456452.

(对我来说)最难的事情是我需要基于表格(展示)。如果 to_show 表由例如填充。 34456 然后帐户输出需要与上面的列表相同。

如果 to_show 表由例如填充。 "76543,18252,34456" 那么情况同上,以此类推...

我所需要的只是呈现所有层次结构,无论哪个帐户(在产品表或信息或帐户中)在 to_show 表中......作为输入,我可以从产品表中获取子/父或帐户。 我试图这样做,但不幸的是这对我来说太难了(因为我在层次结构查询中完全是新手..)。 伙计们,你能帮帮我吗?

create table account (
id_acc number,
parent_acc number
)
;
create table info
(
ID number,
Account_id number,
Product_id number
);
create table product (
Account_id number,
product_id number
);
create table to_show (
ID number
);
insert into account (id_acc,parent_acc)) values (76543,76543);
insert into account (id_acc,parent_acc) values (18252,76543);
insert into account (id_acc,parent_acc) values (34456,76543);
insert into account (id_acc,parent_acc) values (456567,76543);
insert into account (id_acc,parent_acc) values (457564,65424);
insert into account (id_acc,parent_acc) values (45654242,6556756);
insert into info values (875621,18252,98567);
insert into info values (875621,34456,null);
insert into product values (456452,98567);
insert into to_show values (34456);

【问题讨论】:

  • 我很乐意提供帮助;但我不知道你真正追求的是什么。这对我来说似乎不是等级制度。但是与 76543 相关的所有帐户的列表。这就是你真正想要的吗?以某种方式与 76543 相关的所有帐户,即使是产品关系?
  • 我认为您说的是“层次结构”而不是其他东西。请想一个更好的词来形容它。否则,问题清晰而详细;我们只是看不到您想要实现的目标。
  • 为什么 45654242 没有出现在您的结果中?我认为没有理由将它们排除在外?是不是因为不是76543的孩子?
  • 这只是数据的一个例子。作为输入,我有表 to_show 将包含我想要显示的帐户 ID(当然包括所有层次结构)。也许等级制度是一个坏词——全是“树”——我认为它更好; )
  • xQbert: 45654242 不能出现,因为作为输入我有 account: 34456 。并且帐户 34456 与 45654242 无关

标签: sql oracle hierarchical-query


【解决方案1】:

一种方法可能是首先将缺少的层次结构从productinfo 考虑在内,然后生成结果...由于Productinfo 具有与层次结构相关的数据,我们需要将它们组合起来.我认为这可能是最令人不安的部分。并非所有层次结构数据都在一个地方。一旦我意识到这一点,就需要一个工会来允许在工作之前进行连接。

所以我们将product 连接到info 以获取所需的附加分层数据并将其合并到帐户表。这会产生一个包含所有需要的父子关系的数据集。

然后我们可以使用connect by prior 来遍历层次结构。

SELECT a.id_acc, level as lvl
FROM (SELECT id_acc, parent_acc FROM account 
      UNION 
      SELECT P.Account_ID, i.account_id  FROM info i
      INNER JOIN product p 
       on I.Product_ID=P.Product_ID) A
START WITH A.ID_ACC=76543
CONNECT BY prior  a.id_acc=a.parent_acc

这将返回:

Account Lvl
76543   1
18252   2
456452  3
34456   2
456567  2

注意:456452 存在于 18252 之下,因此层次关系保持不变;暗示 76543 有 3 个孩子(18252、34456、456567),而 18252 有一个孩子 456452。

鉴于您的问题的一些假设,我认为这是我能想到的最好的方法。

如果您需要知道完整路径 (sys_connect_by_path) 或者您需要消除 (nocycle) 或 isLeaf (connect_by_isLeaf) 的数据中的循环,您可以添加一些其他的花里胡哨在层次结构中显示无子记录。

SELECT a.id_acc
     , level as lvl
     , SYS_CONNECT_BY_PATH(ID_Acc, '/') "Path"
     , CONNECT_BY_ISLEAF "IsLeaf"
FROM (SELECT id_acc, parent_acc FROM account 
      UNION
      SELECT P.Account_ID, i.account_id  
      FROM info i
      INNER JOIN product p 
        on I.Product_ID=P.Product_ID) A
START WITH a.id_acc=76543
CONNECT BY nocycle prior a.id_acc=a.parent_acc

这将导致类似:

ID_acc  lvl Path                  isLeaf
76543   1   /76543                0
18252   2   /76543/18252          0
456452  3   /76543/18252/456452   1
34456   2   /76543/34456          1
456567  2   /76543/456567         1

有关功能的完整列表,请参阅Oracle Docs

我使用nocycle 淡化了数据中没有循环的重要性。如果数据中存在循环,您的结果可能不正确。

通过更多的工作,我们可以确定 to_Show 中涉及的顶层:

select distinct ID_ACC
from (SELECT id_acc, parent_acc FROM account 
      UNION
      SELECT P.Account_ID, i.account_id  
      FROM info i
      inner join product p 
        on i.product_id=p.product_id) a
where parent_acc is null
connect by  prior a.parent_acc = a.id_acc
start with id_acc in (select id from to_show)

并将其与上述内容结合起来,无需硬编码即可获得所需的结果。

SELECT a.id_acc
     , level as lvl
     , SYS_CONNECT_BY_PATH(ID_Acc, '/') "Path"
     , connect_by_isleaf "IsLeaf"
FROM (SELECT id_acc, parent_acc FROM account 
      UNION
      SELECT P.Account_ID, i.account_id  
      FROM info i
      INNER JOIN product p 
        on i.product_id=p.product_id) a
connect by nocycle prior a.id_acc=a.parent_acc
start with id_acc in 
(
select distinct ID_ACC
from (SELECT id_acc, parent_acc FROM account 
      UNION
      SELECT P.Account_ID, i.account_id  
      FROM info i
      inner join product p 
        on i.product_id=p.product_id) a
where parent_acc is null
connect by  prior a.parent_acc = a.id_acc
start with id_acc in (select id from to_show))

所以我们在这里所做的只是在子查询中使用 start with 来获取 to_Show 中每条记录的顶级域,并反转关系,因此我们向上爬而不是向下爬,然后使用不同的顶级域来返回整个层次结构;从而在 to_show 中显示节点;以及它所在的整个层次结构。

或作为 CTE(通用表表达式)以提高可读性和重构以消除重复。

with 
--Let's combine the hierarchies so they are all in one place.
hsource as (
   select id_acc, parent_acc from account 
   UNION
   SELECT P.Account_ID, i.account_id  
   FROM info i
   inner join product p 
   on i.product_id=p.product_id),
--Now lets get the top domain or root nodes of each item in To_Show
top_domain as (
   select distinct id_acc  
   from (hSource) a
   where parent_acc is null  --this null compare is actually what tells us it's a root/Top domain!  Assumption being all roots have null parent_Acc!
   connect by  prior a.parent_acc = a.id_acc
   start with id_acc in (select id from to_show))
--Now lets use those top domains or root notes to render the hierarchy of each.
SELECT a.id_acc
     , level as lvl
     , SYS_CONNECT_BY_PATH(ID_Acc, '/') "Path"
     , connect_by_isleaf "IsLeaf"
FROM (Select id_acc, parent_acc from hSource) a
connect by nocycle prior a.id_acc=a.parent_acc
start with id_acc in  (Select ID_ACC from Top_Domain)

最后...如果我认为parent_Acc 为空的假设不正确,那么您需要将 top_domain 中的查询更改为以下内容:Connect by prior 假设顶层将为空,否则您将获得循环数据这就是为什么您不能简单地设置a.ID_ACC=a.parent_Acc,所以我们只需使用case 语句更改两个语句数据以使Parent_ACC is null 成为一个真实的语句。从技术上讲,where 子句可能只是ID_ACC=parent_Acc,但我想保持主题。但是,connect by prior必须 更改。当我说数据中没有循环时,请记住上面的内容......好吧,当ID_ACC=parent_ACC 导致循环时。我的意思是来吧!我怎么能做自己的父母?但是我们使用 case 语句来处理它。

with 
--Let's combine the hierarchies so they are all in one place.
hsource as (
   select id_acc, parent_acc from account 
   UNION
   SELECT P.Account_ID, i.account_id  
   FROM info i
   inner join product p 
   on i.product_id=p.product_id),
--Now lets get the top domain or root nodes of each item in To_Show
top_domain as (
   select distinct a.id_acc
   from (hsource) a
--Modification starts here...
   where case when  a.parent_acc =a.id_Acc then null else a.parent_Acc end is null
   connect by prior (case when a.parent_acc=a.id_acc then null else a.parent_Acc end) = a.id_acc
--ends here
   start with a.id_acc in (select id from to_show))
--Now lets use those top domains or root notes to render the hierarchy of each.
    SELECT a.id_acc
         , level as lvl
         , SYS_CONNECT_BY_PATH(ID_Acc, '/') "Path"
         , connect_by_isleaf "IsLeaf"
    FROM (Select id_acc, parent_acc from hSource) a
    connect by nocycle prior a.id_acc=a.parent_acc
    start with id_acc in  (Select ID_ACC from Top_Domain)

【讨论】:

  • xQbert - 感谢您的回复 :) 但我必须避免硬编码。您使用了 a.id_acc = 76543。正如我提到的输入,我将获得表 to_show。表 account,product,info 中的数据只是展示我想要实现的目标的一个示例。
  • 假设表 to_show 由 "34456" 填充。基于该帐户,我需要显示所有层次结构/帐户树。输出将显示与您的代码相同的值。但就像我提到的 - 不幸的是我不能使用硬编码......我不知道哪些帐户将在 to_show 表中。
  • @user5506560 午餐... 增强包括 to_show。我假设 to_Show 中的值可以位于层次结构中的任何级别,并且您希望我们转到每个层次结构的顶部并返回整个树。
  • 我还将代码重构为公用表表达式,以减少冗余并提高可读性。然而结果和截图一样!
  • xQbert - U 是最好的 :) 这真的很有帮助... :) 我还有一个问题 - 假设 Account 表中的 null (对于 76543 ID)我将有相同的数字76543。所以唯一的区别是 76543 而不是 null。该帐户仍然是父帐户。我应该如何更改代码?我试图在 where parent_acc = a.id_acc 上更改“哪里 parent_acc 为空”,但它不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-16
  • 2016-11-21
  • 1970-01-01
  • 2013-03-21
  • 2018-12-25
  • 2018-10-20
  • 1970-01-01
相关资源
最近更新 更多