Можно ли ограничить результат оконной функции с разделением без подзапроса? Этот код находится в 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 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;
Я хочу показать только первую строку каждого окна. Например: строки с cnt: [3M, 19], [Abbott Laborories, 20]
Вам фактически не нужна функция окна, так как первая строка max_cnt
всегда будет равна cnt
. Вместо этого используйте DISTINCT ON
в сочетании с GROUP BY
.
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;
Используйте подзапрос. Если вы хотите ровно одну строку (даже если есть связи), используйте 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()
если вам нужно несколько строк для учетной записи, в случае связей.