2012-12-08 14 views
6

Chciałbym utworzyć listę pochodzenia liniowego dla projektu hodowli drzew. Rodzice są męskie/żeńskie pary, które nie muszą być związane (nie inbredu), stąd znaczenie do śledzenia i wizualizacji te rodowody ...Rekurencyjne PostgreSQL za pośrednictwem 2 tabel nadrzędnych/podrzędnych

Poniżej tabele testowe/danych PostgreSQL 9.1:

DROP TABLE if exists family CASCADE; 
DROP TABLE if exists plant CASCADE; 

CREATE TABLE family ( 
    id serial, 
    family_key VARCHAR(20) UNIQUE, 
    female_plant_id INTEGER NOT NULL DEFAULT 1, 
    male_plant_id INTEGER NOT NULL DEFAULT 1, 
    filial_n INTEGER NOT NULL DEFAULT -1, -- eg 0,1,2... Which would represent None, F1, F2... 
    CONSTRAINT family_pk PRIMARY KEY (id) 
); 

CREATE TABLE plant ( 
    id serial, 
    plant_key VARCHAR(20) UNIQUE, 
    id_family INTEGER NOT NULL, 
    CONSTRAINT plant_pk PRIMARY KEY (id), 
    CONSTRAINT plant_id_family_fk FOREIGN KEY(id_family) REFERENCES family(id) -- temp may need to remove constraint... 
); 

-- FAMILY Table DATA: 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (1,'NA',1,1,1); -- Default place holder record 
-- Root level Alba families 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (2,'family1AA',2,3,1); 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (3,'family2AA',4,5,1); 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (4,'family3AA',6,7,1); 
-- F2 Hybrid Families 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (5,'family4AE',8,11,0); 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (6,'family5AG',9,12,0); 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (7,'family6AT',10,13,0); 
-- F3 Double Hybrid family: 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (9,'family7AEAG',14,15,0); 
-- F3 Tri-hybrid backcross family: 
insert into family (id, family_key, female_plant_id, male_plant_id, filial_n) VALUES (10,'family8AEAGAT',17,16,0); 

-- PLANT Table DATA: 
-- Root level Alba Parents: 
insert into plant (id, plant_key, id_family) VALUES (1,'NA',1);  -- Default place holder record 
insert into plant (id, plant_key, id_family) VALUES (2,'female1A',1); 
insert into plant (id, plant_key, id_family) VALUES (3,'male1A',1); 
insert into plant (id, plant_key, id_family) VALUES (4,'female2A',1); 
insert into plant (id, plant_key, id_family) VALUES (5,'male2A',1); 
insert into plant (id, plant_key, id_family) VALUES (6,'female3A',1); 
insert into plant (id, plant_key, id_family) VALUES (7,'male3A',1); 
-- Female Alba progeny: 
insert into plant (id, plant_key, id_family) VALUES (8,'female4A',2); 
insert into plant (id, plant_key, id_family) VALUES (9,'female5A',3); 
insert into plant (id, plant_key, id_family) VALUES (10,'female6A',4); 
-- Male Aspen Root level parents: 
insert into plant (id, plant_key, id_family) VALUES (11,'male1E',1); 
insert into plant (id, plant_key, id_family) VALUES (12,'male1G',1); 
insert into plant (id, plant_key, id_family) VALUES (13,'female1T',1); 
-- F1 Hybrid progeny: 
insert into plant (id, plant_key, id_family) VALUES (14,'female1AE',5); 
insert into plant (id, plant_key, id_family) VALUES (15,'male1AG',6); 
insert into plant (id, plant_key, id_family) VALUES (16,'male1AT',7); 
-- Hybrid progeny 
insert into plant (id, plant_key, id_family) VALUES (17,'female1AEAG',9); 
-- Tri-hybrid backcross progeny: 
insert into plant (id, plant_key, id_family) VALUES (18,'female1AEAGAT',10); 
insert into plant (id, plant_key, id_family) VALUES (19,'female2AEAGAT',10); 

Poniżej zapytanie rekurencyjne, że pochodzą z dokumentacji Postgres WITH Queries:

WITH RECURSIVE search_tree(
     family_key 
    , female_plant 
    , male_plant 
    , depth 
    , path 
    , cycle 
) AS (
    SELECT 
      f.family_key 
     , pf.plant_key 
     , pm.plant_key 
     , 1 
     , ARRAY[ROW(pf.plant_key, pm.plant_key)] 
     , false 
    FROM 
      family f 
     , plant pf 
     , plant pm 
    WHERE 
     f.female_plant_id = pf.id 
     AND f.male_plant_id = pm.id 
     AND f.filial_n = 1 -- Include only F1 families (root level) 
     AND f.id <> 1  -- omit the default first family record 

    UNION ALL 

    SELECT 
      f.family_key 
     , pf.plant_key 
     , pm.plant_key 
     , st.depth + 1 
     , path || ROW(pf.plant_key, pm.plant_key) 
     , ROW(pf.plant_key, pm.plant_key) = ANY(path) 
    FROM 
      family f 
     , plant pf 
     , plant pm 
     , search_tree st 
    WHERE 
     f.female_plant_id = pf.id 
     AND f.male_plant_id = pm.id 
     AND f.family_key = st.family_key 
     AND pf.plant_key = st.female_plant 
     AND pm.plant_key = st.male_plant 
     AND f.filial_n <> 1 -- Include only non-F1 families (non-root levels) 
     AND NOT cycle 
) 
SELECT * FROM search_tree; 

Poniżej jest sygnał wyjściowy:

F1 family1AA=(female1A x male1A) > F2 family4AE=(female4A x male1E) > F3 family7AEAG=(female1AE x male1AG) > F4 family8AEAGAT=(female1AEAG x male1AT) 
F1 family2AA=(female2A x male2A) > F2 family5AG=(female5A x male1G) > F3 family7AEAG=(female1AE x male1AG) > F4 family8AEAGAT=(female1AEAG x male1AT) 
F1 family3AA=(female3A x male3A) > F2 family6AT=(female6A x female1T) > F3 family8AEAGAT=(female1AEAG x male1AT) 

Powyższe zapytanie rekursywne wyświetla 3 wiersze z odpowiednimi rodzicami F1, ale ścieżka nie wyświetla dalszych rodzin/rodziców. Byłbym wdzięczny za pomoc, aby wynik rekursywny był podobny do pożądanego wyjścia wymienionego powyżej.

+0

Nicea pytanie; bardzo dobrze postawione. bardzo kompletny. Pracuję nad tym ... – wildplasser

+0

Nie jestem pewien, czy rozumiem, jak zdefiniowana jest hierarchia. Nie mogę znaleźć relacji rodzic/dziecko w przykładowych tabelach. Czy możesz wyjaśnić nieco, w jaki sposób znaleziono rodzica (lub dziecko)? –

+0

Czy jest możliwe, że wiersz z 'plant.id = 11' powinien mieć' 2' jako "family_id"? –

Odpowiedz

4

Mam dostosowane zapytania, co mam rozumieć, niekoniecznie do tego, co jest wymagane :-)

kwerendy zaczyna się na trzech podanych rodzin określonych f.id != 1 AND f.filial_n = 1 i rekurencyjnie rozszerza dostępne dzieci.

Pod jakim warunkiem należy wybrać tylko trzy ostatnie mecze, jest poza moim zrozumieniem. Być może dla każdej początkowej rodziny najdłuższy łańcuch anchestorów?

WITH RECURSIVE expanded_family AS (
    SELECT 
     f.id, 
     f.family_key, 
     pf.id   pd_id, 
     pf.plant_key pf_key, 
     pf.id_family pf_family, 
     pm.id   pm_id, 
     pm.plant_key pm_key, 
     pm.id_family pm_family, 
     f.filial_n 
    FROM family f 
     JOIN plant pf ON f.female_plant_id = pf.id 
     JOIN plant pm ON f.male_plant_id = pm.id 
), 
search_tree AS (
    SELECT 
     f.*, 
     1 depth, 
     ARRAY[f.family_key::text] path 
    FROM expanded_family f 
    WHERE 
     f.id != 1 
     AND f.filial_n = 1 
    UNION ALL 
    SELECT 
     f.*, 
     depth + 1, 
     path || f.family_key::text 
    FROM search_tree st 
     JOIN expanded_family f 
      ON f.pf_family = st.id 
      OR f.pm_family = st.id 
    WHERE 
     f.id <> 1 
) 
SELECT 
    family_key, 
    depth, 
    path 
FROM search_tree; 

Wynikiem jest:

family_key | depth |      path      
---------------+-------+------------------------------------------------- 
family1AA  |  1 | {family1AA} 
family2AA  |  1 | {family2AA} 
family3AA  |  1 | {family3AA} 
family4AE  |  2 | {family1AA,family4AE} 
family5AG  |  2 | {family2AA,family5AG} 
family6AT  |  2 | {family3AA,family6AT} 
family7AEAG |  3 | {family1AA,family4AE,family7AEAG} 
family7AEAG |  3 | {family2AA,family5AG,family7AEAG} 
family8AEAGAT |  3 | {family3AA,family6AT,family8AEAGAT} 
family8AEAGAT |  4 | {family1AA,family4AE,family7AEAG,family8AEAGAT} 
family8AEAGAT |  4 | {family2AA,family5AG,family7AEAG,family8AEAGAT} 

rzeczy techniczne:

  • mam usunięte cycle rzeczy ze względu na czystą danych nie powinno być konieczne (IMHO).

  • expanded_family może być wstawiony, jeśli wystąpi jakiś nieparzysty problem z wydajnością, ale na razie sprawia, że ​​zapytanie cykliczne jest bardziej czytelne.

EDIT

niewielką modyfikację zapytania filtruje tych rzędów, gdzie dla każdego „głównym” rodziny (czyli te, w którym rozpoczęło się zapytanie), istnieje ścieżka najdłuższy.

pokażę tylko zmieniony udział w search_tree, więc trzeba skopiować głowicę z poprzedniej części:

-- ... 
search_tree AS 
(
    SELECT 
     f.*, 
     f.id   family_root, -- remember where the row came from. 
     1 depth, 
     ARRAY[f.family_key::text] path 
    FROM expanded_family f 
    WHERE 
     f.id != 1 
     AND f.filial_n = 1 
    UNION ALL 
    SELECT 
     f.*, 
     st.family_root, -- propagate the anchestor 
     depth + 1, 
     path || f.family_key::text 
    FROM search_tree st 
     JOIN expanded_family f 
      ON f.pf_family = st.id 
      OR f.pm_family = st.id 
    WHERE 
     f.id <> 1 
) 
SELECT 
    family_key, 
    path 
FROM 
(
    SELECT 
     rank() over (partition by family_root order by depth desc), 
     family_root, 
     family_key, 
     depth, 
     path 
    FROM search_tree 
) AS ranked 
WHERE rank = 1; 

wynik jest:

family_key |      path      
---------------+------------------------------------------------- 
family8AEAGAT | {family1AA,family4AE,family7AEAG,family8AEAGAT} 
family8AEAGAT | {family2AA,family5AG,family7AEAG,family8AEAGAT} 
family8AEAGAT | {family3AA,family6AT,family8AEAGAT} 
(3 rows) 

EDIT2

Na podstawie komentarzy dodałem wersję ścieżki pretty_print:

WITH RECURSIVE expanded_family AS (
    SELECT 
     f.id, 
     pf.id_family pf_family, 
     pm.id_family pm_family, 
     f.filial_n, 
     f.family_key || '=(' || pf.plant_key || ' x ' || pm.plant_key || ')' pretty_print 
    FROM family f 
     JOIN plant pf ON f.female_plant_id = pf.id 
     JOIN plant pm ON f.male_plant_id = pm.id 
), 
search_tree AS 
(
    SELECT 
     f.id, 
     f.id   family_root, 
     1 depth, 
     'F1 ' || f.pretty_print path 
    FROM expanded_family f 
    WHERE 
     f.id != 1 
     AND f.filial_n = 1 
    UNION ALL 
    SELECT 
     f.id, 
     st.family_root, 
     st.depth + 1, 
     st.path || ' -> F' || st.depth+1 || ' ' || f.pretty_print 
    FROM search_tree st 
     JOIN expanded_family f 
      ON f.pf_family = st.id 
      OR f.pm_family = st.id 
    WHERE 
     f.id <> 1 
) 
SELECT 
    path 
FROM 
(
    SELECT 
     rank() over (partition by family_root order by depth desc), 
     path 
    FROM search_tree 
) AS ranked 
WHERE rank = 1; 

Rezultatem jest

path                   
---------------------------------------------------------------------------------------------------------------------------------------------------------- 
F1 family1AA=(female1A x male1A) -> F2 family4AE=(female4A x male1E) -> F3 family7AEAG=(female1AE x male1AG) -> F4 family8AEAGAT=(female1AEAG x male1AT) 
F1 family2AA=(female2A x male2A) -> F2 family5AG=(female5A x male1G) -> F3 family7AEAG=(female1AE x male1AG) -> F4 family8AEAGAT=(female1AEAG x male1AT) 
F1 family3AA=(female3A x male3A) -> F2 family6AT=(female6A x female1T) -> F3 family8AEAGAT=(female1AEAG x male1AT) 
(3 rows) 
+0

Niesamowite - powinienem móc go pobrać stąd! Mogę użyć PL/pgsql do usunięcia duplikatów przodków i dodać formatowanie rodzica/dziecka. Dzięki za pomoc! Pomogłeś wyhodować lepsze drzewa !!! – user1888167

+0

@ user1888167: pl/pgsql nie jest wymagany. Możesz dodać odpowiednie filtry w trzech miejscach: 'GDZIE' części nierekurencyjnej (gdzie' f.id' i 'f.filial_id' są już zaznaczone), rekursywny' GDZIE' i możesz również dodać filtr do "zewnętrzny" wybierz. "Zewnętrzne" "SELECT" jest typowym miejscem dla takich rzeczy. Aby wykonać filtrowanie, możesz użyć więcej informacji niż pokazuje bieżące wyjście.
Po prostu nie wiedziałem, jakie kryteria chcesz zastosować. –

+0

Najbardziej pożądanym kryterium byłoby wyświetlenie tylko "kompletnych paczek rodzinnych", które byłyby ostatnimi trzema rzędami danych wyjściowych. Więc tak, dla każdej początkowej rodziny byłby to najdłuższy unikalny łańcuch anchestorów? czy to możliwe? – user1888167

Powiązane problemy