【问题标题】:Using column generated by listagg in a where clause在 where 子句中使用 listagg 生成的列
【发布时间】:2017-04-11 14:10:41
【问题描述】:

我在一个 oracle 数据库中有 3 个表。它们看起来像这样:

Table User:

username|forename|surname
a          a       a
b          b       b
c          c       c


Table Right:

username|organisationname|right
a           x              user
a           x              admin
a           x              owner
a           y              user
a           y              admin
a           z              owner
b           x              user
c           y              user
c           y              admin
c           z              user

Table Organisation:

organisationname|number
x                 12
y                 14
z                 42

Right有两个foreign_key:username指向表user中的同名列,organisationname指向表Organisation

对于在organisation x 上拥有正确用户的所有user,我想获取用户拥有1 个或多个权限的所有organisations(组织x 除外)的列表。

在我的例子中: a 在 x 上有正确的用户,b 在 x 上有正确的用户,c 在 x 上没有正确的用户。 所以我只想为用户 a 和 b(而不是 c !!!)获取所有组织(不同的!,没有组织名称两次)。 如果一个用户对 x 之外的另一个组织没有权限(他必须拥有正确的用户),我希望该用户出现在输出中,但没有组织。

预期的输出是:

user|organisations
a    y; z
b  

我希望你明白我的意思。 到目前为止,我的查询如下:

select username, 
/* listagg put all the organisationnames of one user in one column separated by ;  
   then the organsation x gets removed from the result*/
regexp_replace (listagg (organisationname, '; ') 
       within group (order by organisationname), '(^x;?)|( ?x;)|(; x$) ', '') as "organisations"
from(
    select u2.username as username,
           o.organisationname as organisationname,
           /*I do this to remove duplicate entries in the list of organisations of one user */
           row_number() over (partition by u2.username, o.organisationname order by u2.username) as rn
       from RIGHT r2, ORGANISATION o, USER u2 
       where r2.organisationname = o.organisationname
       and r2.username = u2.username
       and r2.username IN
           (
            select u.username
            from USER u, right r
            where r.username = u.username
            and r.right = 'user'
            )  
     order by u2.username, o.organisationname         
)
where rn = 1
group by username

该查询有效,但现在我想过滤列组织。但我无法在 where 子句中使用它。当我尝试使用它时,oracle 会抛出错误: ORA-00904: 标识符无效

有谁知道我如何做到这一点?我认为oracle有问题是因为listagg的解析函数。 我也会很高兴关于如何简化我的查询的建议。谢谢!

这是我尝试在 where 子句中使用的列组织:

select * from ( ***long query shown above***) 
where organisations like '%termToSearch%';  

【问题讨论】:

  • 你也可以发帖吗,你目前正在尝试什么,这给了你错误
  • 我在问题的末尾使用 where 子句进行查询

标签: sql oracle listagg


【解决方案1】:

逐步:user|x 的用户名,然后是除 x 之外的不同用户|组织,然后按用户名聚合:

select
  username,
  listagg(organisationname, '; ') within group (order by organisationname) as organisations
from
(
  select distinct username, organisationname
  from right
  where username in 
  (
    select username 
    from right 
    where organisationname = 'x' and right = 'user'
  )
  and organisationname <> 'x'
)
group by username;

(很遗憾,LISTAGG 不接受 DISTINCT 关键字,因此我们需要两个步骤而不是一个步骤来构建不同组织的列表。)

更新:为了让用户也没有任何其他组织,我们将删除条件 and organisationname &lt;&gt; 'x' 并将 case 构造添加到 LISTAGG

select
  username,
  listagg(case when organisationname <> 'x' then organisationname end, '; ')
    within group (order by organisationname) as organisations
from
(
  select distinct username, organisationname
  from right
  where username in 
  (
    select username 
    from right 
    where organisationname = 'x' and right = 'user'
  )
)
group by username;

【讨论】:

  • 谢谢托尔斯滕。您的查询看起来比我的查询简单得多(我可以在 where 子句中使用组织)。但我认为您的查询没有给我正确的结果。在我的示例中,我有用户 b,他在组织 x 上拥有正确的用户,但在其他组织上没有权限。我想在我的输出中有这个用户(没有任何组织)。对于您的查询,该用户不在输出中,因为您只是查找不在组织 x 上的权限。
【解决方案2】:

更改代码部分,添加别名,

旧代码 -

within group (order by organisationname), '(^x;?)|( ?x;)|(; x$) ', '') as "organisations"

新代码 -

 within group (order by organisationname), '(^x;?)|( ?x;)|(; x$) ', '') organisations

新代码 -

within group (order by organizationname), '(^x;?)|( ?x;)|(; x$) ', '') as "ORGANISATIONS"

如果还想用旧代码,可以把最后一个where条件改成——

where "organisations" like '%termToSearch%'; 

参考:Schema Object Names and Qualifiers

【讨论】:

  • 谢谢!您的两个解决方案都有效!你能解释一下为什么当我用大写字母而不是小写字母写“ORGANISATIONS”时它会起作用吗?
  • @Markus - 更新答案,参考链接解释了如何命名和存储数据库对象。
  • @Markus:用引号告诉 DBMS 您希望区分大小写。没有引号,Oracle 在内部以大写形式存储名称。这就是organisations = ORGANISATIONS = OrGaNiSaTiOnS = "ORGANISATIONS" &lt;&gt; "organisations" 的原因。 (顺便说一句,as 没有任何区别。)
  • AS 只是有时更容易阅读,这纯粹是个人喜好。 @ThorstenKettner 是对的,它根本没有任何区别。
  • 感谢你们!今天学到了很多关于 SQL 的新东西(例如第一次使用 listagg)!我赞成你的两个答案!
猜你喜欢
  • 2021-10-09
  • 1970-01-01
  • 2011-03-27
  • 2010-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多