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

Mysql 复杂SQL查询:类似于树遍历的选择,mysql,sql,sql-server,oracle,db2,Mysql,Sql,Sql Server,Oracle,Db2,考虑以下(1:N)关系: [entity:user][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

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

[entity:user][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
我需要设计一个查询(不是存储过程),它将输入一个
用户id
,并考虑以下事实:

如果用户被授予访问
rid
的权限,那么它也可以访问给定
rid
父rid
——该
rid
本身已启用
(enabled=true)。

这应该一直持续到到达
根rid
,即
父rid
属性为
null

在上面的示例中,用户
'a.b.b.a-basa'
的可访问rid列表将是

a.b.b.a
a.b.b
a.b
对于
a.a.a-basa

a.a.a
a.a
a

我们能用一个查询得到这个列表吗?任何sql供应商都可以。

在Oracle中,您可以使用分层查询来实现这一点。在Oracle中,您可以使用分层查询来实现这一点。搜索“连接方式”或查看此链接。

这将为您带来成功。 答案适用于SQLServer2005及以后的版本

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    

这对你来说应该是个好消息。 答案适用于SQLServer2005及以后的版本

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    
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

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

分层数据有几种模型。大多数模型(如邻接列表)对于某些查询需要某种递归。使用物化路径模型的设计,无需递归查询即可实现所需的功能

在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\u id
正在存储您在
rid\u key
列中已有的数据

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

还要注意的是,上面的查询根本不使用
parent\u rid
列。我确信它可以进一步改进。

有几种分层数据模型。大多数模型(如邻接列表)对于某些查询需要某种递归。使用物化路径模型的设计,无需递归查询即可实现所需的功能

在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\u id
正在存储您在
rid\u key
列中已有的数据

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

还要注意的是,上面的查询根本不使用
parent\u rid
列。我相信它还可以进一步改进。

+1对于这个问题,使用循环,这会更容易…:)但无论如何,你需要一个查询…级别的数量已知吗?如果没有,我认为这是不可能的,因为SQL不支持递归。你检查过了吗?@cha:SQL(语言)支持递归。SQL的MySQL实现不需要(仅通过过程和函数),而且通过这种特殊的设计,甚至不需要递归查询。这可以用一个简单的,可以在所有DBMS中使用的方法来完成。对于这个问题+1,有一个循环,这会更容易…:)但是无论如何,你需要一个查询…级别的数量已知吗?如果没有,我认为这是不可能的,因为SQL不支持递归。你检查过了吗?@cha:SQL(语言)支持递归。SQL的MySQL实现不需要(仅通过过程和函数),而且通过这种特殊的设计,甚至不需要递归查询。它可以用一个简单的、适用于所有DBMS的数据库来完成,应该也适用于PostgreSQL和DB2(除了变量定义)。顺便说一句:您应该习惯于将语句终止符(
)放在它所属的位置:在末尾:应该也适用于PostgreSQL和DB2(变量定义除外)。顺便说一句:您应该习惯于将语句终止符(
)放在它所属的位置:在末尾: