2011-08-18 30 views
3

簡單介紹:PL/pgSQL:查找所有組所屬的人(也是間接的)

我有一個包含用戶和組的數據庫。 每個用戶可能是一個或多個組的成員。 每個組可能都有一個或多個父組。

模式:

CREATE TABLE users(
    username VARCHAR(64) NOT NULL PRIMARY KEY, 
    password VARCHAR(64) NOT NULL, 
    enabled BOOLEAN NOT NULL); 

CREATE TABLE groups (
    id bigserial NOT NULL PRIMARY KEY, 
    group_name VARCHAR(64) NOT NULL); 

CREATE TABLE groups_inheritance (
    group_id bigint NOT NULL, 
    parent_group_id bigint NOT NULL, 
    CONSTRAINT fk_group_inheritance_group FOREIGN KEY(group_id) REFERENCES groups(id), 
    CONSTRAINT fk_group_inheritance_group_2 FOREIGN KEY(parent_group_id) REFERENCES groups(id), 
    CONSTRAINT unique_uk_groups_inheritance UNIQUE(group_id, parent_group_id)); 

CREATE TABLE group_members (
    id bigint PRIMARY KEY, 
    username VARCHAR(64) NOT NULL, 
    group_id bigint NOT NULL, 
    CONSTRAINT fk_group_members_username FOREIGN KEY(username) REFERENCES users(username), 
    CONSTRAINT fk_group_members_group FOREIGN KEY(group_id) REFERENCES groups(id)); 

我正在尋找一個PL/pgSQL的功能,查找所有組(他們的名字)特定用戶所屬。

例子:

組名稱:人, 集團母公司:空

組名:學生, 集團母公司:人民

組名稱:Football_players, 集團母公司:人民

組名:Basketball_players, 組別家長:人

用戶名:馬切伊, 羣體:學生,Football_players

F( 「馬切伊」)= { 「學生」, 「人物」, 「Football_players」}

他屬於 「人」 只是因爲他屬於「學生」或「Football_players」。他不是「人」組的直接成員。

在此先感謝!

回答

3
WITH RECURSIVE group_ancestry AS (
SELECT group_id, username 
FROM group_members 
UNION 
SELECT groups_inheritance.parent_group_id, username 
FROM group_ancestry 
    JOIN groups_inheritance ON groups_inheritance.group_id = group_ancestry.group_id 
) 
SELECT username, group_id 
FROM group_ancestry 
+0

實際上,我決定接受它,因爲它更簡潔和適應性強。 –

1

如果您只有一個水平繼承(如例子中),那麼你可以使用這樣的查詢:

WITH group_ids AS 
(
    SELECT group_id 
    FROM group_members 
    WHERE username LIKE 'Maciej' 
) 
SELECT group_name 
FROM 
    (SELECT group_id FROM group_ids 
     UNION 
    SELECT DISTINCT parent_group_id 
    FROM groups_inheritance INNER JOIN group_ids USING(group_id)) g 
INNER JOIN groups ON id = group_id; 

結果:

group_name  
------------------ 
People 
Students 
Football_players 
(3 rows) 

PL/pgSQL函數:

DROP FUNCTION IF EXISTS f(varchar(64)); 
CREATE FUNCTION f(username varchar(64)) 
RETURNS text[] AS $$ 
DECLARE 
    gId bigint; 
    pgId bigint; 
    gName text; 
    result text[] = '{}'; 
BEGIN 
    FOR gId IN SELECT group_id FROM group_members WHERE username LIKE username 
    LOOP 
     SELECT INTO gName group_name FROM groupS WHERE id = gId; 
     result := result || gName; 
     FOR pgId IN SELECT parent_group_id FROM groups_inheritance WHERE group_id = gId 
     LOOP 
      SELECT INTO gName group_name FROM groups WHERE id = pgId; 
      IF NOT (result @> ARRAY[gName]) THEN 
       result := result || gName; 
      END IF; 
     END LOOP; 
    END LOOP; 
    RETURN result; 
END $$ 
LANGUAGE 'plpgsql'; 

結果:

SELECT f('Maciej'); 
       f     
------------------------------------ 
{Students,People,Football_players} 
(1 row) 

然而,對於嵌套家長團體,我認爲這應該遞歸是合適的。

編輯:

這裏是嵌套父組基於遞歸變體:

CREATE OR REPLACE FUNCTION f_recursive(gIdParam bigint, resultArrayParam bigint[]) 
RETURNS bigint[] AS $$ 
DECLARE 
    pgId bigint; 
    resultArray bigint[]; 
BEGIN 
    FOR pgId IN SELECT parent_group_id FROM groups_inheritance WHERE group_id = gIdParam 
    LOOP 
     IF NOT (resultArrayParam @> ARRAY[pgId]) THEN 
      resultArray := resultArray || pgId; 
      resultArray := resultArray || f_recursive(pgId, resultArray); 
     END IF; 
    END LOOP; 
    RETURN resultArray; 
END $$ 
LANGUAGE 'plpgsql'; 

CREATE OR REPLACE FUNCTION f(usernameParam varchar(64)) 
RETURNS text[] AS $$ 
DECLARE 
    gId bigint; 
    resultArray bigint[]; 
BEGIN 
    FOR gId IN SELECT group_id FROM group_members WHERE username LIKE usernameParam 
    LOOP 
     resultArray := resultArray || gId; 
     resultArray := resultArray || f_recursive(gId, resultArray); 
    END LOOP; 
    RETURN array_agg(group_name) 
      FROM groups INNER JOIN (SELECT unnest(resultArray)) u ON unnest = id; 
END $$ 
LANGUAGE 'plpgsql'; 

實施例插入件:

INSERT INTO groups (id, group_name) VALUES 
    (1, 'People'), (2, 'Workers'), (3, 'Programmers'), 
    (4, 'AI-Programmers'), (5, 'Administators'), (6, 'Managers'); 

INSERT INTO groups_inheritance (group_id, parent_group_id) VALUES 
    (2, 1), (3, 2), (4, 3), (5, 2), (6, 2); 

INSERT INTO users (username, password, enabled) VALUES 
    ('Maciej', '12345', true); 

INSERT INTO group_members (id, username, group_id) VALUES 
    (1, 'Maciej', 4), (2, 'Maciej', 5); 

結果:

SELECT f('Maciej'); 
          f        
----------------------------------------------------------- 
{AI-Programmers,Programmers,Workers,People,Administators} 
(1 row) 

另一種方法是使用WITH query以及RECURSIVE修飾符作爲顯示的@araqnid。

+0

感謝您的回答! –