2013-01-13 69 views
2

考慮以下(1:N)關係:複雜的SQL查詢:選擇像樹遍歷

[entity: user] <------ rid key ------> [entity: rid]

考慮兩個表中的數據爲:

select * from user; 
user-id  rid-key 
a-basa   a 
b-basa   b 
a.a-basa  a.a 
a.b-basa  a.b 
a.a.a-basa  a.a.a 
a.a.b-basa  a.a.b 
a.b.a-basa  a.b.a 
a.b.b-basa  a.b.b 
a.b.b.a-basa a.b.b.a 
a.b.b.b-basa a.b.b.b 



select * from rid; 

rid-key parent-rid enabled 
a   null  true 
b   null  true 
a.a   a   true 
a.b   a   false 
a.a.a  a.a   true 
a.b.a  a.b   true 
a.b.b  a.b   true 
a.b.b.a  a.b.b  true 
...... 
n rows 

我需要設計一個單一查詢(而不是存儲過程),這將輸入user-id,下面的事實被認爲是:

如果用戶被授予訪問rid,那麼它也可以訪問給定ridparent rid - 的rid本身也啓用(enabled = true).

這應該繼續,直到我們到達root rid,即。 parent rid財產是null

在上面的例子中,訪問擺脫用戶'a.b.b.a-basa'的名單將是:

a.b.b.a 
a.b.b 
a.b 

a.a.a-basa

a.a.a 
a.a 
a 

我們可以得到使用單個查詢這個名單?任何SQL供應商都很好。

+0

對於這個問題+1,用一個循環,這將是... :)更容易,但無論如何,你需要一個查詢... – bonCodigo

+0

是已知的級別數?如果不是,我不相信這是可能的,因爲SQL不支持遞歸。 – cha

+0

您是否檢查過http://stackoverflow.com/questions/2319284/sql-recursive-query-on-self-refrencing-table-oracle? – obourgain

回答

1

有幾種模型分層數據。大多數模型(如鄰接列表)需要對某些查詢進行某種遞歸。通過使用物化路徑模型的設計,您可能需要的是沒有遞歸查詢的情況。

在MySQL中測試(沒有遞歸查詢),在SQL-fiddle test-mysql。它可以很容易地轉換爲其他DBMS,如果修改字符串連接部分:

SELECT 
    COUNT(*)-1 AS steps_up, 
    rid2.rid_key AS ancestor_rid_key 
FROM 
    u2 
    JOIN 
    rid 
     ON u2.rid_key = rid.rid_key 
     OR u2.rid_key LIKE CONCAT(rid.rid_key, '.%') 
    JOIN 
    rid AS rid2 
     ON rid.rid_key = rid2.rid_key 
     OR rid.rid_key LIKE CONCAT(rid2.rid_key, '.%') 
WHERE 
    u2.userid = 'basa' 
    AND 
    u2.rid_key = 'a.b.b.a' 
GROUP BY 
    rid2.rid_key, rid2.enabled 
HAVING 
    COUNT(*) + (rid2.enabled = 'true') 
    = SUM(rid.enabled = 'true') + 1 ; 

它採用了這一觀點,這並不是必需的,但它表明user.user_id被存儲,你已經在rid_key數據柱。

CREATE VIEW u2 AS 
SELECT 
    SUBSTRING_INDEX(user_id, '-', -1) AS userid 
    , rid_key 
FROM user ; 

還有一點需要注意的是,上面的查詢根本不使用parent_rid列。我相信它可以進一步改善。

1

在Oracle中,您可以使用分層查詢來實現此目的。搜索CONNECT BY或查看此article

1

這應該讓你的球滾動。 答案工作的SQL Server 2005年起

DECLARE @UsersRIDkey VARCHAR(10) = 'a.a.a' 
;WITH UserCTE (userid, ridkey) AS 
(
    SELECT 'a-basa',   'a'  UNION ALL 
    SELECT 'b-basa',   'b'  UNION ALL 
    SELECT 'a.a-basa',  'a.a'  UNION ALL 
    SELECT 'a.b-basa',  'a.b'  UNION ALL 
    SELECT 'a.a.a-basa',  'a.a.a' UNION ALL 
    SELECT 'a.a.b-basa',  'a.a.b' UNION ALL 
    SELECT 'a.b.a-basa',  'a.b.a' UNION ALL 
    SELECT 'a.b.b-basa',  'a.b.b' UNION ALL 
    SELECT 'a.b.b.a-basa', 'a.b.b.a' UNION ALL 
    SELECT 'a.b.b.b-basa', 'a.b.b.b' 
) 
,RidCTE (ridkey, parentrid, isenabled) AS 
(
    SELECT 'a',   null,  1 UNION ALL 
    SELECT 'b',   null,  1 UNION ALL 
    SELECT 'a.a',   'a',   1 UNION ALL 
    SELECT 'a.b',   'a',   0 UNION ALL 
    SELECT 'a.a.a',  'a.a',  1 UNION ALL 
    SELECT 'a.b.a',  'a.b',  1 UNION ALL 
    SELECT 'a.b.b',  'a.b',  1 UNION ALL 
    SELECT 'a.b.b.a',  'a.b.b',  1 
) 
,RidHierarchyCTE AS 
(
    SELECT * 
    FROM RidCTE 
    WHERE ridkey = @UsersRIDkey 
    UNION ALL 
    SELECT R.ridkey, R.parentrid, R.isenabled 
    FROM RidHierarchyCTE H 
    JOIN RidCTE    R ON R.ridkey = H.parentrid 
) 
SELECT ridkey 
FROM RidHierarchyCTE  
+0

同樣適用於PostgreSQL和DB2(除了變量定義)。順便說一句:你應該習慣把語句終結符(';')放在它所在的位置:http://sqlblog.com/blogs/aaron_bertrand/archive/2009/09/03/ladies-and-gentlemen- start-your-semi-colons.aspx –

+0

你是對的......我還在爲形成習慣而奮鬥^^ – MarkD

1

Oracle解決方案上:

SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled 
    2 from users u 
    3   inner join rid r 
    4     on r.rid_key = u.rid_key 
    5 start with u.user_id = 'a.a.a-basa' 
    6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true' 
    7/

USER_ID  RID_KEY PAREN ENABL 
------------ ------- ----- ----- 
a.a.a-basa a.a.a a.a true 
a.a-basa  a.a  a  true 
a-basa  a  null true 

SQL> select u.user_id, r.rid_key, r.parent_rid, r.enabled 
    2 from users u 
    3   inner join rid r 
    4     on r.rid_key = u.rid_key 
    5 start with u.user_id = 'a.b.b.a-basa' 
    6 connect by prior r.parent_rid = r.rid_key and prior enabled = 'true' 
    7/

USER_ID  RID_KEY PAREN ENABL 
------------ ------- ----- ----- 
a.b.b.a-basa a.b.b.a a.b.b true 
a.b.b-basa a.b.b a.b true 
a.b-basa  a.b  a  false 

http://sqlfiddle.com/#!4/d529f/1