簡體   English   中英

查找具有按子元素過濾的父元素的元素列表

[英]Find a list of elements with parent elements filtering by child elements

我有一些表,其中存儲了進入同一個表的父子關系,允許我使用單個表構建多級關系。

一個示例如下所示:

公司
ID 姓名 id_parent
1 戴姆勒
2 梅賽德斯 1
3 聰明的 1
4 梅賽德斯斯圖加特工廠 2
5 大眾汽車集團
6 奧迪 5
7 保時捷 5

該表允許我建立以下關系:

1. Daimler
     2. Mercedes
        4. Mercedes Plant Stuttgart
     3. Smart
5. Volkswagen Group
     6. Audi
     7. Porsche

我還有其他一些遵循相同架構的表。 現在雖然建立自上而下的關系更容易,但我也必須自下而上地建立這些關系,然后創建這些元素的數組。

在創建自下而上列表時,我有一個 id 數組,為此我必須首先過濾最底部的元素。

例如,我有一個 ids (4,7)數組。 該列表應過濾掉所有不必要的元素,並返回以下內容:

1. Daimler
     2. Mercedes
        4. Mercedes Plant Stuttgart
5. Volkswagen Group
     7. Porsche

既然有多個層次,我不能假設一段關系可以在某個層次結束,我必須假設所有的可能性。

使用自上而下的方法並加載所有可能的關系,然后過濾掉 id,這是我已經嘗試過的方法,由於需要加載然后過濾的數據量,結果非常緩慢且資源密集.

使用自下而上的方法似乎是最有效的方法。 到目前為止,我認為如果我可以從我的 SQL 中獲得以下結果,我可以使用 Java 完成其余的工作。

(假設我只為我的 SQL 參數提供 ids 4 和 7,它會在結果列表中返回其父級和父級等)

SQL 結果
ID 姓名 id_parent
1 戴姆勒
2 梅賽德斯 1
4 梅賽德斯斯圖加特工廠 2
5 大眾汽車集團
7 保時捷 5

我曾嘗試為此使用交叉表,但效果不佳。 因為我想在 JPA + Postgres 中使用它並將 SQL 也用於多個其他表,所以我正在嘗試構建一些通用的東西(函數?存儲過程?)以在所有具有這種結構的表中使用。

對於單個 ID,您只需編寫一個 SQL 遞歸查詢(如果您以前沒有編寫過遞歸查詢,那么互聯網上有無數關於如何執行此操作的示例)。 如果您想一次性獲取多個 ID 的結果,那么您需要將遞歸查詢放在 SP 中的循環中,並將每個 ID 傳遞到循環的下一次迭代中 - 將循環的每次迭代的結果相加到最終結果集

我現在已經解決了這個問題。 我不知道在這里實現的術語鄰接列表。

一旦使用它進行搜索,我就能找到問題的解決方案。

對於其他想知道如何做到這一點的人,我在 postgres 中使用以下遞歸查詢來做到這一點:

WITH RECURSIVE RESULT_ROW AS (SELECT ID, NAME, ID_PARENT
                             FROM COMPANY
                             WHERE ID = 758
                             UNION
                             SELECT C.ID, C.NAME, C.ID_PARENT
                             FROM COMPANIES C
                                      INNER JOIN RESULT_ROW CS ON CS.ID_PARENT = C.ID)
SELECT *
FROM RESULT_ROW;

為父級獲取一個給定子級的層次結構是一項相對容易的任務。 以下函數返回從給定子節點到頂級父節點的路徑(id 數組)(定義為id_parent為空)。 它還生成從父級到指定子級的路徑列表。 此外,如果指定了多個子節點,則每個子節點都返回相同的值。 多個子 ID不需要迭代。

create or replace function company_heir_path_from_child(child_array_in integer[])
   returns table ( child_id             integer   
                 , path_child_to_parent integer[]
                 , path_parent_to_child integer[]
                 ) 
  language sql
as $$
     with recursive com_heir(base_child, id_parent, child_to_parent, parent_to_child) as 
          ( select c.id, c.id_parent
                 , array[c.id]
                 , array[c.id]   
              from company c 
             where c.id = any(child_array_in)
            union all 
            select base_child, c.id_parent
                 , array_cat (child_to_parent, array[c.id])
                 , array_cat (array[c.id], parent_to_child) 
              from company  c 
              join com_heir h
                on (c.id = h.id_parent)
          )    
     select base_child, child_to_parent,  parent_to_child
       from com_heir
      where id_parent is null;
$$;

目前recursive query的主要缺點是保持層次結構有序。 下面使用從父級到子級的路徑(從上面)以及每個節點在層次結構中的位置來檢索name並在每個級別生成縮進。

create or replace function show_company_heir_from_child(child_array_in integer[])
    returns table ( id                    integer   
                   , name                 text
                   , path_parent_to_child integer[]
                   ) 
  language sql
as $$
    select c.id
         , substr(lpad( '', 4*(array_position(pc.path, c.id))-1,' ') || c.name,4) name    --substr (....) provides indentation for each level
         , path  
      from company  c 
      join (select child_id, path_parent_to_child  path, path_parent_to_child[1] root_parent
              from company_heir_path_from_child(child_array_in)
           ) pc on array_position(pc.path, c.id) is not null 
     order by root_parent, path; 
$$;

在此處查看演示
參考資料:數組數組函數
關於通用例程的注意事項:這些很難為 SQL 構建。 因為 SQL要求在提交評估之前修復所有結構元素(表、列、視圖、函數、過程名稱); 它們不能作為變量傳遞給評估。 這意味着您的通用例程必須實際將 SQL 編寫為字符串。 除非您非常小心,否則這可能非常困難,並且會將您的應用程序打開到SQL injection 也許更好的方法是構建/使用模板為每個表創建特定的例程,然后使用選擇器例程來調用適當的例程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM