Результат ограничения оконной функции mysql / postgres без подзапроса

0

Можно ли ограничить результат оконной функции с разделением без подзапроса? Этот код находится в 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;

Результат этого запроса дает:

Изображение 174551

Я хочу показать только первую строку каждого окна. Например: строки с cnt: [3M, 19], [Abbott Laborories, 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;
Теги:
window-functions
partition

2 ответа

1
Лучший ответ

Я хочу показать только первую строку каждого окна. Например: строки с cnt: [3M, 19], [Abbott Laborories, 20]

Вам фактически не нужна функция окна, так как первая строка max_cnt всегда будет равна cnt. Вместо этого используйте DISTINCT ON в сочетании с GROUP BY.

Из документации postgresql

SELECT DISTINCT ON (выражение [,...]) сохраняет только первую строку каждого набора строк, где эти выражения оцениваются равными. Выражения 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

1 путь, который я всегда испортил, когда я впервые начал использовать DISTINCT ON, должен убедиться, что порядок выражений в предложении ORDER BY начинается с выражений в DISTINCT ON. В приведенном выше примере ORDER BY начинается с acct.name

Если есть связь для первой позиции, первая строка, соответствующая критериям, будет возвращена. Это недетерминированно. В ORDER BY можно указать дополнительные выражения, чтобы повлиять на то, какие строки возвращаются в этом параметре.

пример:

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

будет возвращать строку, содержащую facebook, если для данной учетной записи, как facebook и direct дают тот же cnt.

Однако обратите внимание, что при таком подходе невозможно вернуть все строки, привязанные к первой позиции, то есть обе строки, содержащие facebook & direct (без использования подзапроса).

DISTINCT ON может быть объединено в одном и том же выражении с помощью GROUP BY (пример выше) и WINDOW FUNCTIONS (пример ниже). Предложение DISTINCT ON логически оценивается непосредственно перед LIMIT.

Например, следующий запрос (но бессмысленный) показывает комбинацию DISTINCT ON с WINDOW FUNCTION. Он вернет max_cnt строку на 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;
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() если вам нужно несколько строк для учетной записи, в случае связей.

  • 0
    Привет спасибо. Я пытался увидеть, есть ли способ сделать это без подзапроса.
  • 0
    @ Лена. , , Вы не можете обойтись без подзапроса, потому что вам нужно рассчитать количество для всех строк, а затем уменьшить количество строк. Я имею в виду, что фильтрация может использовать CTE или коррелированный подзапрос, но это не будет простой запрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню