Sql 如何在Oracle DB中按同一列中的多个值分组
我有一个用于用户访问软件应用程序的表。用户可以通过字母数字ID表示的应用程序的不同方面进行多次访问。类似地,多个用户可能具有相同或不同的访问集。 例如 我想根据同一组访问对客户进行分组。例如:Sql 如何在Oracle DB中按同一列中的多个值分组,sql,oracle,Sql,Oracle,我有一个用于用户访问软件应用程序的表。用户可以通过字母数字ID表示的应用程序的不同方面进行多次访问。类似地,多个用户可能具有相同或不同的访问集。 例如 我想根据同一组访问对客户进行分组。例如: USER | GROUP ----------------------------------------- Cust1 | Group1 Cust2 | Group
USER | GROUP
-----------------------------------------
Cust1 | Group1
Cust2 | Group1
Cust3 | Group2
目前,我已为此目的为一些重要用户创建了如下表:
Table USERGROUP
GROUP | ACCESS
-----------------------------------------
Group1 | BA1
Group1 | BB2
Group2 | CB1
Group2 | BA1
下面的查询将比较这两个表
select a.USER,u.GROUP from ACCESSTAB a, USERGROUP u where not exists
(select USERACCESS from ACCESSTAB where USER=a.USER order by 1
MINUS
select ACCESS from USERGROUP where GROUP=u.GROUP order by 1)
我希望在不使用USERGROUP表的情况下对用户进行分组,因为很难为所有用户进行维护,更不用说添加访问需要手动配置。表现并不重要
欢迎提供任何建议/帮助。提前谢谢 看来你的关系是:
当你说“难以为所有用户维护”时,你并不完全清楚你的意思,但你可能会说用户的权限集(#1)和组的权限集(#3)必须是相等的集,并且你担心保持它们之间的一致性将是困难的。如果我的假设是正确的,那么我建议您删除规则#1,删除ACCESSTAB表,并将USERGROUP表重命名为GROUP#u ACCESS。这将消除更新异常。同意@jeff6times7的观点,即您应该重新考虑您的数据模型,使访问组成为驱动型的,但同时。。。您可以使用
listag()
为每个用户生成访问权限列表:
并从不同的聚合生成组:
select 'Group' || rownum as groupname, groupaccess
from (
select distinct listagg(useraccess, ',') within group (order by useraccess) as groupaccess
from accesstab
group by username
order by groupaccess
);
GROUPNAME GROUPACCESS
--------- --------------------
Group1 BA1,BB2
Group2 BA1,CB1
并将其连接在一起,例如使用两个CTE:
with user_cte (username, groupaccess) as (
select username, listagg(useraccess, ',') within group (order by useraccess)
from accesstab
group by username
),
group_cte (groupname, groupaccess) as (
select 'Group' || rownum, groupaccess
from (
select distinct listagg(useraccess, ',') within group (order by useraccess) as groupaccess
from accesstab
group by username
order by groupaccess
)
)
select u.username, g.groupname
from user_cte u
join group_cte g on g.groupaccess = u.groupaccess
order by u.username;
USERNAME GROUPNAME
-------- ---------
Cust1 Group1
Cust2 Group1
Cust3 Group2
或者,您可以使用第一个CTE构建第二个CTE,以便只命中实际表一次:
with user_cte (username, groupaccess) as (
select username, listagg(useraccess, ',') within group (order by useraccess)
from accesstab
group by username
),
group_cte (groupname, groupaccess) as (
select distinct 'Group' || dense_rank () over (order by groupaccess), groupaccess
from user_cte
)
select u.username, g.groupname
from user_cte u
join group_cte g on g.groupaccess = u.groupaccess
order by u.username;
USERNAME GROUPNAME
-------- ---------
Cust1 Group1
Cust2 Group1
Cust3 Group2
或者即使没有第二个CTE:
with user_cte (username, groupaccess) as (
select username, listagg(useraccess, ',') within group (order by useraccess)
from accesstab
group by username
)
select distinct username,
'Group' || dense_rank() over (order by groupaccess) as groupname
from user_cte;
USERNAME GROUPNAME
-------- ---------
Cust1 Group1
Cust2 Group1
Cust3 Group2
(…这几乎是xQbert的方法)
如果有用,还可以在输出中包含聚合的groupaccess
,以列出每个组中的权限,但这似乎不是必需的
当然,一旦使用不同的访问组合添加(或更新)了用户,就会生成(或删除)一个组,并且现有组的编号可能会改变——这可能会让人困惑,但对您来说不是问题。如果这是一个问题,那么您必须维护中间表。只是一个简单的例子:
with AccessTab as
(SELECT '1' muser ,'BA1' useraccess from dual UNION ALL
SELECT '1','BB2' from dual UNION ALL
SELECT '2','BA1' from dual UNION ALL
SELECT '2','BB2' from dual UNION ALL
SELECT '3','CB1' from dual UNION ALL
SELECT '3','BA1' from dual),
cte as (SELECT muser, listagg(userAccess,',') within group (order by useraccess) GRP From AccessTab group by muser)
SELECT A.muser, dense_Rank() over ( order by a.GRP) as mGroup
FROM cte a
结果:
Muser mGroup
2 1
1 1
3 2
这可以通过listag()函数完成
SELECT USER, LISTAGG(USERACCESS, ', ') WITHIN GROUP (ORDER BY USER) AS USER_ACCESS_GROUP
FROM ACCESSTAB
GROUP BY USER
结果将是
USERNAME| USER_ACCESS_GROUP
Cust1 BA1, BB2
Cust2 BA1, BB2
Cust3 BA1, CB1
如果您对这些组中的每个组都有一个特殊的名称,您可以将listag()包装在DECODE()函数中。hakish的方法是按访问顺序列出不同的访问顺序,然后根据这些值加入用户,并添加一个窗口排名来定义您的组。一种不太老练的方法是对每个用户使用完全外部连接计数不同的访问,如果计数与其他用户匹配,则他们拥有相同的组,并再次使用密集秩分析获得一个组,这也是我的首选,但该表不能作为遗留系统的一部分删除,并且依赖于其他表(不知道为什么会这样设计).关于维护问题,我有25000多个用户,他们似乎拥有各种各样的访问权限,每个用户有多达25个访问权限,配置需要手动完成。我必须亲自检查这一点,以确保连接不会超过列的最大长度,幸运的是它没有。我想知道一个人如何我可以不使用Listag来做。但这是另一个问题,下次再问。谢谢。
SELECT USER, LISTAGG(USERACCESS, ', ') WITHIN GROUP (ORDER BY USER) AS USER_ACCESS_GROUP
FROM ACCESSTAB
GROUP BY USER
USERNAME| USER_ACCESS_GROUP
Cust1 BA1, BB2
Cust2 BA1, BB2
Cust3 BA1, CB1