【问题标题】:mysql group by with having removes results from sum overmysql group by 从总和中删除结果
【发布时间】:2021-12-15 02:36:43
【问题描述】:

我有一个查询,它正在对项目进行分组,选择一个组中的项目数,以及该组在 where 子句中未过滤掉的所有项目中的百分比

我想删除所有包含两个或更少项目的组,以减少占整个数据集一小部分的结果。

这是我的(简化的)查询:

select count(i.itemId) as itemCount,
       concat(format(100 * count(i.itemId) / sum(count(i.itemId)) over (), 2), '%') as totalPercentage
from thing t
join item i on t.thingId = i.thingId
where t.createdDate > startdate and t.createdDate < enddate
group by t.thingId
order by count(i.itemId) desc, t.thingId desc;

我认为我应该像这样添加一个有子句:

...
group by t.thingId
having count(i.itemId) > 2
order by count(i.itemId) desc, t.thingId desc;

会解决问题,但是当我这样做时,结果显示的总百分比现在不准确,因为 sum(count(i.itemId)) over () 现在忽略了其中包含 2 个或更少项目的组。

我知道可以通过将此查询设为内部查询,然后在此查询之外的选择中进行过滤来做到这一点,但我不希望这样做,因为在我的团队中,我们尽量避免使用内部查询除非必要,否则查询。

我也知道可以制作一个临时表,并通过从该临时表中进行选择来过滤它,但我什至不想进入那个,因为它看起来很丑。

TLDR:是否可以从选择结果中过滤掉组,但仍将它们包含在 sum() over () 子句中而没有任何恶作剧?

【问题讨论】:

  • 任何过滤都会从结果集中删除行,因此计算无法访问它们。分析函数在查询执行的最后一步计算(在select 列表表达式评估和order by 之前),因此要过滤掉一些行,您必须将计算放入子查询并在外部查询中应用过滤器
  • 避免“内部查询”(子查询/内联视图)表明了对 SQL 的根本误解。您错误地将其视为命令式语言,但它是声明性的;语法允许你表达一个问题,然后 DBMS 生成一个计划来解决它。仅仅因为某些东西在子查询中并不意味着 DBMS 会盲目地执行它。谓词下推、类宏展开等方法,意味着生成的计划不是那么幼稚。例如SELECT * FROM (SELECT * FROM X) AS Y WHERE foo='bar' 产生与SELECT * FROM X WHERE foo='bar' 完全相同的计划

标签: mysql sql filter


【解决方案1】:

首先计算百分比并将其用作子查询,然后过滤,因为任何过滤都会删除您需要的行

你喜欢

SELECT
    itemCount,totalPercentage
FROM
    (select 
        t.thingId,
        count(i.itemId) as itemCount,
        concat(format(100 * count(i.itemId) / sum(count(i.itemId)) over (), 2), '%') as totalPercentage
    from thing t
    join item i on t.thingId = i.thingId
    where t.createdDate > startdate and t.createdDate < enddate
    group by t.thingId) t1
WHERE intemCount = 2
order by itemCount desc, thingId desc;

【讨论】:

    【解决方案2】:

    一般来说,如果没有子查询的帮助,就不可能在 SQL 中计算百分比。要生成百分比,您需要一个总数除以,这需要一个单独的聚合查询。

    我希望您的团队关于子查询的政策实际上是“尽可能避免使用dependent a/k/a correlated subqueries”。那些是可以影响性能的。避免所有子查询作为政策问题?这有点像在编程中避免使用子程序——它禁止使用主要的语言特性。

    【讨论】:

    • 这实际上是不准确的。窗口化/分析函数完全允许这样做而无需子查询。 op面临的问题是他们试图在同一范围内过滤和应用解析函数,并发现操作的顺序导致首先应用过滤器。至于避免相关子查询作为一般原则,那也是错误的。通过正确的表述和索引,它们没有任何问题。这个答案充满了谬误。
    【解决方案3】:

    您可以尝试 case 语句,以便它给出空百分比,然后在下一阶段过滤掉空值,无论是 sql 还是一些前端代码。

    ...
    case when count(i.itemId) > 2 
    then concat(format(100 * count(i.itemId) / sum(count(i.itemId)) over (), 2), '%') end as 
    totalPercentage
    ...
    

    这可能只会增加计算时间,或者可以使用其他答案中提到的过滤掉它的外部查询。

    【讨论】:

    • 操作想要排除行,这不排除行。至于以后排除它们,“用 1 或 2 排除行”也可以,用 NULL 替换它们完全是多余的。
    • @MatBailie 他们可以用任何值替换,是的,还有比 null 更好的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多