具有redshift的MODE聚合功能

Ben*_*ier 7 sql postgresql aggregate-functions amazon-redshift

我有以下表格:

顾客

customer_id name
----------------
1           bob
2           alice
3           tim
Run Code Online (Sandbox Code Playgroud)

购买

id customer_id item_bought
--------------------------
1  1           hat
2  1           shoes
3  2           glasses
3  2           glasses
4  2           book
5  3           shoes
6  1           hat
Run Code Online (Sandbox Code Playgroud)

我想跟随结果:

customer_name item_bought_most_often
------------------------------------
bob           hat
alice         glasses
tim           shoes
Run Code Online (Sandbox Code Playgroud)

我会这样做(实际上没有尝试,只是想法):

SELECT customer.name as customer_name,
  MODE(item_bought) as item_bought_most_ofen
FROM customers
INNER JOIN purchases USING (customer_id)
GROUP_BY customer_id
Run Code Online (Sandbox Code Playgroud)

但是,Redshift中不存在MODE聚合功能.

似乎Redshift用户定义的函数只是常规的标量函数,而不是聚合函数.所以我认为我自己无法定义它.

任何解决方法?

Luk*_*zda 8

您可以先COUNT每个人购买,然后使用RANK()窗口函数:

SELECT name AS customer_name, item_bought AS item_bought_most_often
FROM(SELECT name,item_bought,RANK() OVER(PARTITION BY name ORDER BY cnt DESC) rnk
     FROM (SELECT c.name, p.item_bought, COUNT(*) AS cnt
           FROM customers c
           JOIN purchases p
             ON p.customer_id = c.customer_id
           GROUP BY c.name, p.item_bought) AS s1) AS s2
WHERE rnk = 1;
Run Code Online (Sandbox Code Playgroud)

LiveDemo

输出:

??????????????????????????????????????????
? customer_name ? item_bought_most_often ?
??????????????????????????????????????????
? alice         ? glasses                ?
? bob           ? hat                    ?
? tim           ? shoes                  ?
? zoe           ? pencil                 ?
? zoe           ? book                   ?
??????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

笔记:

RANK 将处理多个最常见的值。


编辑:

正如Lukas Eder提到的,它可以进一步简化:

SELECT name AS customer_name, item_bought AS  item_bought_most_often
FROM(SELECT name,item_bought,
            RANK() OVER(PARTITION BY name ORDER BY COUNT(*) DESC) rnk
     FROM customers c
     JOIN purchases p
       ON p.customer_id = c.customer_id
     GROUP BY c.name, p.item_bought)  AS s2
WHERE rnk = 1;
Run Code Online (Sandbox Code Playgroud)

db<>小提琴演示


Gor*_*off 5

您可以mode()使用row_number()以下方式模仿:

select name, item_bought
from (select c.name, p.item_bought, count(*) as cnt,
             row_number() over (order by count(*) desc) as seqnum
      from customers c join
           purchases p
           using (customer_id)
      group by c.name, p.item_bought
     ) cp
where seqnum = 1;
Run Code Online (Sandbox Code Playgroud)