【问题标题】:How can I check for a certain value in all aggregated rows?如何检查所有聚合行中的某个值?
【发布时间】:2010-09-16 21:51:07
【问题描述】:

假设我有三个表:usergroupxref,一个为它们提供多对多 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 的是:

“对于每个用户,告诉我他们在多少个组中。另外,对于每个用户的所有组名,告诉我'Developers' 是否是值之一。”

实际上要问的是:

“对于每个用户,告诉我他们在多少个组中。另外,对于每个用户的所有组名,告诉我这个case 表达式产生的最大值。”

这是一个反模式的原因是我基本上依赖于这样一个事实,即当使用max() 评估时,Y 发生null 上方“冒泡”。如果有人想复制或扩充此查询,他们很容易忘记反模式并意外地将返回值更改为不使用相同不直观巧合的内容。

基本上,我希望我能写的查询是这样的:

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 函数记录在here 中,但它只存在于一种称为Oracle OLAP DML 的神秘方言中,我认为我无法在10g 上仅使用SQL 访问它。但是,它似乎完全按照我的意愿行事。

这就是我的全部。有什么想法吗?

我认识到有两个非常简单的想法,“在代码中执行”或“在 PL/SQL 中执行”,但这是作弊。 :-)

【问题讨论】:

  • 用户可以同时属于开发者和内容管理者吗?这就是你需要两个单独的列的原因吗?
  • 我没有将 [SQL] 放在标题中,而是仅依靠“sql”标签。
  • @be here now- 是的,这就是原因。
  • @Nick Pierpoint - 当你写关于 SO 的问题时,它会根据标题为你提供一个潜在类似问题的列表。其他一些问题都以 [SQL] 开头,我发现它是一种有用的方式来查找实际上相关的“相似”建议。所以,我复制了约定! :-)
  • 我认为在标题中放置标签不是惯例:stackoverflow.com/questions/tagged/sql

标签: sql oracle reporting analytics aggregate-functions


【解决方案1】:

我会从 MAX 切换到 SUM(使用 1 而不是 Y),所以您说的是“计算此人所在的组数,其中组名为 Developers”。

那么该模式类似于“计算购买价值超过 30 美元的销售数量”。

如果需要,您可以添加另一个表达式来表示“如果计数大于零,则‘是’此人是开发人员”。非常明确,但可能没有必要。

【讨论】:

  • 赞成,因为SUM() 似乎不太适合解决方法。但是,我现在坚持接受任何答案。谢谢!
【解决方案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')

【讨论】:

    【解决方案3】:

    我更喜欢 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)

    【讨论】:

    • 感谢您的 +1 和回答。不幸的是,“N”成为我在问题中提到的同一问题的受害者,即您依赖于“N”恰好比“Y”排序更高的事实。例如,如果您使用的是“始终”和“从不”而不是“Y”和“N”,那么每个单元格都将是“从不”,因为max 将始终选择它。这是一个不直观的“陷阱”,我相信,它是使其成为真正反模式的部分原因。
    • 正如我所说,我更喜欢计数,但我认为使用 Y 和 N 作为标志非常安全。
    猜你喜欢
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-14
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多