假设我有三个表:user
,group
和xref
,这是一个为他们提供多对多RI的表。
我可能想看看每个用户所属的群组:
select
user.user_id,
user.user_name,
count(*) as group_count
from
user
inner join xref on user.user_id = xref.user_id
inner join group on group.group_id = xref.group_id
group by user.user_id, user.user_name
到目前为止一切都还可以。但是,如果我想要一些额外的信息怎么办?我正在报告,我想知道每个用户是开发人员还是内容管理员。现在,反模式出现了:
select
user.user_id,
user.user_name,
count(*) as group_count,
max( case group.group_name when 'Developers' then 'Y' else null end )
as is_dev
max( case group.group_name when 'Content Management' then 'Y' else null end )
as is_cm
from
user
inner join xref on user.user_id = xref.user_id
inner join group on group.group_id = xref.group_id
group by user.user_id, user.user_name
这有效,并产生预期的结果,但感觉非常错误。我想要询问Oracle的是:
“对于每个用户,请显示他们所在的群组数量。此外,对于每个用户的所有群组名称,请告诉我”开发者“是否为其中一个值。”
我实际要求的是:
“对于每个用户,请显示他们所在的群组数量。此外,对于每个用户的所有群组名称,请显示此
case
表达式生成的最高值。”
这是一种反模式的原因是,我基本上依赖Y
发生的事实,以便在评估null
时“冒泡”max()
以上select
user.user_id,
user.user_name,
count(*) as group_count,
any(group.group_name, 'Developers', 'Y', null) as is_dev,
any(group.group_name, 'Content Management', 'Y', null) as is_cm
from
user
inner join xref on user.user_id = xref.user_id
inner join group on group.group_id = xref.group_id
group by user.user_id, user.user_name
。如果有人想要复制或扩充这个查询,他们很容易忘记反模式并意外地将返回值更改为不使用相同的不直观巧合的东西。
基本上,我希望我能写的查询是:
first_value
我一直在寻找各种选择,似乎有一些潜力:
partition
可行,但我无法弄清楚如何将相应的over
窗口限制在右侧。any
子句的分析函数可能有效,但我做想要折叠我正在分组的列,所以它似乎不是一个完美的选择。 / LI>
这就是我得到的全部。有什么想法吗?
我认识到有两个非常简单的想法,“在代码中执行”或“在PL / SQL中执行此操作”,但这是作弊。 : - )
答案 0 :(得分:3)
我会从MAX切换到SUM(使用1而不是Y),因此您要说“计算此人所在群组的名称是开发人员”。
然后该模式类似于“计算购买价值超过30美元的销售数量”。
如果需要,您可以添加另一个表达式来说“如果计数大于零,那么'是'这个人就是开发人员”。非常明确,可能不必要。
答案 1 :(得分:2)
SELECT user.user_id,
user.user_name,
COUNT(*) group_count,
COUNT(DISTINCT DECODE(group_name, 'Developers', 'Y', NULL)) AS is_developer
COUNT(DISTINCT DECODE(group_name, 'Content Management', 'Y', NULL)) AS is_content_manager
FROM the_query
至于ANY
,它是一个类似于IN
的谓词,而不是一个函数:
SELECT *
FROM dual
WHERE 'baz' = ANY('foo', 'bar', 'baz')
答案 2 :(得分:0)
我更喜欢Gary's answer,但如果你想坚持使用布尔返回,你可以通过返回'N'而不是null来使排序更明确。
select
user.user_id,
user.user_name,
count(*) as group_count,
max( case group.group_name when 'Developers' then 'Y' else 'N' end )
as is_dev
max( case group.group_name when 'Content Management' then 'Y' else 'N' end )
as is_cm
from
user
inner join xref on user.user_id = xref.user_id
inner join group on group.group_id = xref.group_id
group by user.user_id, user.user_name
(对于写得很好的问题,+ 1)