【问题标题】:mysql/postgres window function limit result without subquery没有子查询的mysql/postgres窗口函数限制结果
【发布时间】:2018-12-23 12:09:27
【问题描述】:

是否可以限制窗口函数的结果,有分区,没有子查询?此代码位于 postgres/mysql 中。我正在寻找 mysql 和 postgres 中的解决方案。

例如:假设连接与问题的重点无关。

select acct.name, we.channel, count(*) as cnt,
    max(count(*)) over (partition by name order by count(*) desc) as max_cnt
from web_events we join accounts acct
    on we.account_id=acct.id
group by acct.name, we.channel
order by name, max_cnt desc;

这个查询的结果是:

我只想显示每个窗口分区的第一行。 例如:带有cnt的行:[3M,19],[Abbott Labortories,20]

我尝试了以下不起作用(在窗口函数中添加了限制 1):

select acct.name, we.channel, count(*) as cnt,
        max(count(*)) over (partition by name order by count(*) desc limit 1) as max_cnt
    from web_events we join accounts acct
        on we.account_id=acct.id
    group by acct.name, we.channel
    order by name, max_cnt desc;

【问题讨论】:

    标签: mysql sql postgresql window-functions partition


    【解决方案1】:

    我只想显示每个窗口分区的第一行。例如:带有cnt的行:[3M,19],[Abbott Labortories,20]

    这里实际上不需要窗口函数,因为第一行的max_cnt 总是等于cnt。而是将DISTINCT ONGROUP BY 结合使用。

    来自postgresql documentation

    SELECT DISTINCT ON ( expression [, ...] ) 只保留给定表达式计算结果为相等的每组行的第一行。 DISTINCT ON 表达式使用与 ORDER BY 相同的规则进行解释(见上文)。请注意,每个集合的“第一行”是不可预测的,除非使用 ORDER BY 来确保所需的行首先出现

    SELECT DISTINCT ON(acct.name) 
      acct.name
    , we.channel
    , COUNT(*) cnt
    FROM web_events we 
    JOIN accounts acct
      ON we.account_id=acct.id
    GROUP BY 1, 2
    ORDER BY name, cnt DESC;
    

    这是 sqlfiddle 中的一个快速演示。 http://sqlfiddle.com/#!17/57694/8

    我第一次开始使用DISTINCT ON 时总是搞砸的一种方法是确保ORDER BY 子句中的表达式顺序以DISTINCT ON 中的表达式开头。在上面的示例中,ORDER BYacct.name 开头

    如果第一名出现平局,则返回符合条件的第一行。这是不确定的。可以在 ORDER BY 中指定其他表达式以影响在此设置中返回的行。

    示例:

    ORDER BY name, cnt DESC, channel = 'direct'
    

    将返回包含facebook 的行,如果对于给定帐户,facebookdirect 产生相同的cnt

    但是,请注意,使用这种方法,不可能返回与第一个位置相关的所有行,即包含facebookdirect 的两行(不使用子查询)。

    DISTINCT ON 可以与GROUP BYs(上例)和WINDOW FUNCTIONS(下例)组合在同一语句中。 DISTINCT ON 子句在 LIMIT 之前进行逻辑评估。

    例如,以下查询(尽管毫无意义)展示了DISTINCT ONWINDOW FUNCTION 的组合。它将根据max_cnt返回一个不同的行

    SELECT DISTINCT ON(mxcnt) 
      acct.name
    , we.channel
    , COUNT(*) cnt
    , MAX(COUNT(*)) OVER (PARTITION BY acct.name) mxcnt
    FROM web_events we 
    JOIN accounts acct
      ON we.account_id=acct.id
    GROUP BY 1, 2
    ORDER BY mxcnt, cnt DESC;
    

    【讨论】:

      【解决方案2】:

      使用子查询。如果您只想要一行(即使有平局),请使用row_number()

      select name, channel, cnt
      from (select acct.name, we.channel, count(*) as cnt,
                   row_number() over (partition by acct.name order by count(*) desc) as seqnum
            from web_events we join
                 accounts acct
                 on we.account_id = acct.id
            group by acct.name, we.channel
           ) wea
      order by name;
      

      如果您想要一个帐户的多行,您可以使用rank(),以防出现平局。

      【讨论】:

      • 嗨,谢谢。我试图看看是否有办法在没有子查询的情况下做到这一点。
      • @Lena 。 . .你真的不能没有子查询,因为你需要计算所有行的计数然后减少行数。我的意思是,过滤可以使用 CTE 或相关子查询,但它不会是一个简单的查询。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-10
      • 1970-01-01
      • 2010-10-01
      • 2011-03-24
      • 2018-12-12
      相关资源
      最近更新 更多