【问题标题】:not a single-group group function using select case statement不是使用 select case 语句的单组组函数
【发布时间】:2015-09-07 13:48:40
【问题描述】:

我正在写下面的查询,它将两个选择查询相除并计算百分比。但我收到not a single-group group function 的错误

select CASE WHEN COUNT(*) = 0 THEN 0 ELSE round((r.cnt / o.cnt)*100,3) END from 
    (Select count(*) as cnt from O2_CDR_HEADER WHERE STATUS NOT IN(0,1) and DATE_CREATED > (SYSDATE - 1)) r cross join
    (Select count(*) as cnt from O2_CDR_HEADER WHERE DATE_CREATED > (SYSDATE - 1)) o;

【问题讨论】:

  • @GordonLinoff rcross join 之前定义。
  • 如果您以可读的方式格式化代码会更容易判断。

标签: sql oracle group-by case-when


【解决方案1】:

您不需要使用连接。如果我是你,我会这样做:

select case when count(*) = 0 then 0
            else round(100 * count(case when status not in (0, 1) then 1 end) / count(*), 3)
       end non_0_or_1_status_percentage
from   o2_cdr_header
where  date_created > sysdate - 1;

这是一个简单的演示:

with t as (select 1 status from dual union all
           select 2 status from dual union all
           select 3 status from dual union all
           select 2 status from dual union all
           select 4 status from dual union all
           select 5 status from dual union all
           select 6 status from dual union all
           select 7 status from dual union all
           select 1 status from dual union all
           select 0 status from dual union all
           select 1 status from dual)
select case when count(*) = 0 then 0
            else round(100 * count(case when status not in (0, 1) then 1 end) / count(*), 3)
       end col1
from   t
where 1=0;

      COL1
----------
         0

如果您不确定在 case 语句中过滤计数是否与在 where 子句中过滤时返回的结果相同,这里有一个演示可以证明这一点:

with t as (select 1 status from dual union all
           select 2 status from dual union all
           select 3 status from dual union all
           select 2 status from dual union all
           select 4 status from dual union all
           select 5 status from dual union all
           select 6 status from dual union all
           select 7 status from dual union all
           select 1 status from dual union all
           select 0 status from dual union all
           select 1 status from dual)
select 'using case statement' how_count_filtered,
       count(case when status not in (0, 1) then 1 end) cnt
from   t
union all
select 'using where clause' how_count_filtered,
       count(*) cnt
from   t
where  status not in (0, 1);

HOW_COUNT_FILTERED          CNT
-------------------- ----------
using case statement          7
using where clause            7

【讨论】:

  • @Boniest 即使我使用连接,它肯定会返回相同的结果,对吗?有很多方法可以改变查询逻辑?
  • 是的,你会得到相同的结果,只是 - 希望 - 更快。
  • 我刚刚更新了我的答案以包含一个演示,即过滤要在 case 语句中计算的行返回的结果与过滤 where 子句中的行然后做了一个计数(*)。
【解决方案2】:

您在同一个 SELECT 查询中引用了一个聚合函数 (COUNT(*)) 和一个单独的列表达式(r.cnto.cnt)。除非为相关的各个列添加 GROUP BY 子句,否则这不是有效的 SQL。

提供一个有效的替代方案会更容易,您可以阐明您希望此查询返回什么(给定示例架构和一组数据)。作为一种猜测,我想说您可以简单地将COUNT(*) 替换为o.cnt 以避免除以0 的问题。如果这里预计会出现其他一些逻辑,则需要澄清这是什么。

【讨论】:

  • 到目前为止,我的两个内部选择查询都返回 0。我不明白我必须在哪里使用 group by 子句,因为如果语句本身我得到整数作为返回值,对吗?它没有返回行数或其他东西
  • @Rahul - 引擎无法保证您的子查询返回标量值,它会期望您优化查询以确保不会出现不明确的分组问题。如上所述,没有明显的理由需要在初始 SELECT 语句中使用函数 COUNT(*)。只需使用o.cnt 并快乐:)
【解决方案3】:

您似乎想要获得不在 0,1 或 0 中的状态百分比(如果没有结果)。

也许这就是你想要的第一行?

SELECT CASE WHEN (R.CNT = 0 AND O.CNT = 0) THEN 0 ELSE ROUND((R.CNT *100.0 / O.CNT),3) END 

【讨论】:

  • 如果R.CNT 不是0,而O.CNT 是怎么办? ;)
  • @PinnyM 这不可能发生,不是吗? r 集是 o 集的子集。反过来也是可能的,但无论如何我认为 OP 对 count(*) = 0 的含义是根本没有结果,这会做那个。
  • 我认为在这种情况下,我只需要将 o.cnt 设置为 0,因为仅对于别名 o 选择条件,如果它返回 0,则 nit 将给出可被 0 整除的错误错误
  • @jpw 除非在子查询之间的日期边缘上运行,否则它不会:) 当然这实际上不会发生,但是检查 R.CNT 并没有真正在这里添加任何东西。并且这些解决方案都没有像COUNT(*) 那样检查源表中的“任何”行 - 但这样的检查实际上不会阻止除以 0。
【解决方案4】:

您不需要交叉连接。选择计数并稍后进行除法。

select case when ocnt > 0 then round((rcnt / ocnt)*100,3)
       else 0 end
from
(
select 
CASE WHEN STATUS NOT IN(0,1) and DATE_CREATED > (SYSDATE - 1)
 THEN COUNT(*) END as rcnt,
CASE WHEN DATE_CREATED > (SYSDATE - 1)
 THEN COUNT(*) END as ocnt 
from O2_CDR_HEADER
group by status, date_created
) t

【讨论】:

  • 也许不是,但这并不能真正解决错误发生原因的问题。您还为 status 和 date_created 添加了 GROUP BY,这将导致返回多行。我认为这不是 OP 的意图。
【解决方案5】:

Boneist 的回答很好,但我会写成:

select coalesce(round(100 * avg(case when status not in (0, 1) then 1.0 else 0
                                end), 3), 0) as non_0_or_1_status_percentage
from   o2_cdr_header
where  date_created > sysdate - 1;

【讨论】:

    【解决方案6】:

    这是对我来说完美的答案

    select CASE WHEN (o.cnt = 0) THEN 0 ELSE round((r.cnt / o.cnt)*100,3) END from 
    (Select count(*) as cnt from O2_CDR_HEADER WHERE STATUS NOT IN(0,1) and DATE_CREATED > (SYSDATE - 1)) r cross join
    (Select count(*) as cnt from O2_CDR_HEADER WHERE DATE_CREATED > (SYSDATE - 1)) o
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-03
      • 1970-01-01
      • 2011-08-15
      • 2020-03-02
      • 2023-04-07
      • 2014-04-11
      • 2020-08-07
      相关资源
      最近更新 更多