[英]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,它會在結果列表中返回其父級和父級等)
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.