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(变量定义除外)。顺便说一句:您应该习惯于将语句终止符(;
)放在它所属的位置:在末尾: