3

我有一个像这样的简单表:

user    letter
--------------
1       A
1       A
1       B
1       B
1       B
1       C

2       A
2       B
2       B
2       C
2       C
2       C

我想获得每个用户出现的前 2 个“字母”,就像这样

user    letter  rank(within user group)
--------------------
1       B       1
1       A       2

2       C       1
2       B       2

甚至更好:折叠成列

user    1st-most-occurrence  2nd-most-occurrence
1       B                   A
2       C                   B

我怎样才能在 postgres 中做到这一点?

4

3 回答 3

2

像这样的东西:

select *
from (
    select userid, 
           letter, 
           dense_rank() over (partition by userid order by count(*) desc) as rnk
    from letters
    group by userid, letter
) t
where rnk <= 2
order by userid, rnk;

请注意,我替换useruserid因为使用列的保留字是一个坏习惯。

这是一个 SQLFiddle:http ://sqlfiddle.com/#!12/ec3ec/1

于 2013-09-05T09:48:16.593 回答
1
with cte as (
    select 
        t.user_id, t.letter,
        row_number() over(partition by t.user_id order by count(*) desc) as row_num
    from Table1 as t
    group by t.user_id, t.letter
)
select
    c.user_id,
    max(case when c.row_num = 1 then c.letter end) as "1st-most-occurance",
    max(case when c.row_num = 2 then c.letter end) as "2st-most-occurance"
from cte as c
where c.row_num <= 2
group by c.user_id

=> sql 小提琴演示

于 2013-09-05T11:53:36.960 回答
0

所需功能:

CREATE OR REPLACE FUNCTION sortCountLimitOffset(anyarray, int, int)
  RETURNS anyarray AS 'select array_agg(x) from (select x from (select unnest($1) as x) as t group by x order by count(*) desc offset $2 limit $3) t;'
  LANGUAGE sql VOLATILE
  COST 100;

解决方案1:(返回所有连接为字符串的字母)

select
    usr,
    array_to_string(sortCountLimitOffset(array_agg(letter), 0, 5), ',')
from ttt
group by usr;

输出:

 usr | array_to_string
-----+-----------------
   1 | B,A,C
   2 | C,B,A
(2 Zeilen)

解决方案 2:(在单独的列中返回每个第 n 个字母)

select
    usr,
    array_to_string(sortCountLimitOffset(array_agg(letter), 0, 1), ',') letter1,
    array_to_string(sortCountLimitOffset(array_agg(letter), 1, 1), ',') letter2,
    array_to_string(sortCountLimitOffset(array_agg(letter), 2, 1), ',') letter3,
    array_to_string(sortCountLimitOffset(array_agg(letter), 3, 1), ',') letter4,
    array_to_string(sortCountLimitOffset(array_agg(letter), 4, 1), ',') letter5
from ttt
group by usr;

输出:

 usr | letter1 | letter2 | letter3 | letter4 | letter5
-----+---------+---------+---------+---------+---------
   1 | B       | A       | C       |         |
   2 | C       | B       | A       |         |
(2 Zeilen)

也可以从调用函数的函数中内联 SELECT。但是现在的方式,更容易重用和维护代码。

于 2014-02-13T16:14:17.763 回答