【问题标题】:GROUP BY and 2 LIMITS分组依据和 2 个限制
【发布时间】:2019-10-21 07:32:32
【问题描述】:

想象一下这张桌子

Name | Date | Value
--------------------
Olli | 2019 | 2
Olli | 2018 | 2
Olli | 2017 | 2
Olli | 2016 | 1
Olli | 2015 | 1
Olli | 2014 | 1
Pete | 2019 | 3
Pete | 2018 | 3
Pete | 2017 | 3
Pete | 2016 | 2
Pete | 2015 | 9

我想选择一个短期平均值(最近 3 行)和每个名字的历史平均值的商:

Olli: 2 / 1.5 = 1.3333
Pete: 3 / 4 = 0.75

不按名称分组,我可以轻松选择所有组件:

SELECT MEAN(value) FROM tbl WHERE name = 'Olli' -- all time mean, used directly in final query 

-- for the short term mean I use a CTE:
WITH short_term_tbl AS (SELECT * FROM tbl WHERE name = 'Olli' ORDER BY Date DESC LIMIT 3)
    SELECT MEAN(short_term_tbl.value) / MEAN(table.value)
    FROM short_term_tbl, tbl

但是我怎样才能把它推广到所有的名字呢?

我很高兴找到适用于 mysql 和 postgresql 的解决方案。

【问题讨论】:

    标签: mysql sql common-table-expression


    【解决方案1】:

    在 MySQL 8.0 中(您似乎正在使用它,因为您使用的是 CTE,仅从该版本的 MySQL 开始可用),这可以使用row_number() 如下完成:

    select name, avg(case when rn <= 3 then value end) / avg(value)
    from (
        select t.*, row_number() over(partition by name order by date desc) rn from mytable t
    ) t
    group by name
    

    子查询为共享相同name 的组中的每个记录分配一个等级,按date 降序排列。然后外部查询进行条件聚合。

    【讨论】:

    • 哦,完美!谢谢!
    • 现在我的用例在实践中有点复杂。我不需要计算avg(value),而是需要像value*value 这样的表达式,即平方和。这如何与 case when rn &lt;=3... 表达式一起使用?
    • @OlliD-Metz:只是:avg(case when rn &lt;= 3 then value * value end) / avg(value * value)
    【解决方案2】:

    这是获得自定义平均值的一种方法:

    SELECT Name, 
      AVG(CASE WHEN RowNum <=3 THEN Value END) / AVG(Value) AS avg_custom
    FROM (
      SELECT Name, Date, Value,
        ROW_NUMBER() OVER(
          PARTITION BY Name ORDER BY Date DESC -- Rank rows by date, within "name" groups
        ) AS RowNum
      FROM tbl
    ) src
    GROUP BY Name
    ;
    

    它也应该在 Postgres 中工作。

    DB Fiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多