没有子查询的mysql / postgres窗口函数限制结果

时间:2018-07-16 01:10:20

标签: mysql sql postgresql window-functions partition

是否可以通过分区没有子查询来限制窗口函数的结果?这段代码在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;

此查询的结果为:

output

我只想显示每个窗口分区的第一行。 例如:带有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;

2 个答案:

答案 0 :(得分:1)

  

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

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

postgresql documentation

  

SELECT DISTINCT ON(expression [,...])仅保留给定表达式求值相等的每组行的第一行。使用与ORDER BY相同的规则来解释DISTINCT ON表达式(请参见上文)。请注意,除非使用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(不使用子查询)。 / p>

DISTINCT ON可以在同一语句中与GROUP BY(上面的示例)和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;

答案 1 :(得分:0)

使用子查询。如果只需要一行(即使有平局),则使用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()