复杂SQL查询:选择类似树遍历

时间:2013-01-13 09:17:33

标签: mysql sql sql-server oracle db2

考虑以下(1:N)关系:

[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,那么它还可以访问parent rid给定的rid - rid本身已启用{{1} }

这应该持续到我们到达(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

我们可以使用单个查询获取此列表吗?任何sql供应商都没问题。

4 个答案:

答案 0 :(得分:1)

在Oracle中,您可以使用分层查询来实现此目的。搜索CONNECT BY或查看此article

答案 1 :(得分: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    

答案 2 :(得分: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

答案 3 :(得分:1)

分层数据有几种模型。大多数模型(如邻接列表)需要对某些查询进行某种递归。使用物化路径模型的设计,您可以在没有递归查询的情况下实现所需。

SQL-fiddle test-mysql 的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列。而且我相信它可以进一步改进。