MYSQL select table 中按用户分组的计数的前 5 名

MYSQL select top 5 of a count from table that is grouped by user

我提前道歉,因为我可能没有正确描述我的问题。我正在尝试编写一个查询,该查询采用每个用户前 5 个最受欢迎的 chosen_user_items,并通过 group_concat 将每个用户的前几行连接成逗号分隔的字符串,然后按 user_id 分组.

例如,如果 user_id of 1 有五行表示 item_id of 1,两行表示 item_id of 2,三行表示 item_id of 3,和一行 4 5 和 6,那么结果将是 1, 3, 2, 4, 5.

这是我的示例 table 结构。

姓名:chosen_user_items

id | user_id | item_id
------------------------
1  | 1       | 1
2  | 1       | 4
3  | 1       | 19
4  | 1       | 10
5  | 1       | 13
.  | 1       | 1
.  | 1       | 11
.  | 1       | 18
.  | 1       | 212
.  | 1       | 654
.  | 2       | 1
.  | 2       | 28
.  | 2       | 568
.  | 2       | 112
.  | 2       | 354
.  | 3       | 4
.  | 3       | 4
.  | 3       | 19
.  | 3       | 212
.  | 3       | 654
.  | 3       | 4
.  | 3       | 4
.  | 3       | 253
.  | 3       | 187
.  | 3       | 212

这是我想要的输出示例:

user_id | group_concat_results
------------------------------
1       | 1, 4, 19, 13, 212
2       | 1, 28, 568, 212, 354
3       | 4, 212, 19, 654, 253

这是我目前的查询

SELECT `chosen_user_items`.`item_id`, COUNT(`chosen_user_items`.`item_id`) AS 'item_count' 
FROM `chosen_user_items` 
WHERE `chosen_user_items`.`user_id` = 1
GROUP BY `chosen_user_items`.`item_id` 
ORDER BY `item_count` DESC 
LIMIT 5

虽然这对单个用户非常有效,但我希望能够 运行 所有用户只查询一次(以避免进行成百上千次数据库查询),并且必须手动用 PHP.

等语言连接结果

提前致谢。

要解决这个问题,我认为您需要执行 4 个不同的步骤。

首先,您需要 choose/select/order 将显示的行。 这可以使用 row_number 和分区来完成(这不能在 MYSQL 中使用,但在本指南中,他们向您展示了 MYSQL 等效解决方案 https://blog.sqlauthority.com/2014/03/09/mysql-reset-row-number-for-each-group-partition-by-row-number/

其次,您需要过滤 row_number 小于 5 的行,这将表现得像每个查询的 "limit 5"。

第三步,您需要将每个用户的这5条记录转换为5列。 这可以使用 pivot table 来完成。在这里您可以找到您必须执行的操作的示例:MySQL pivot table

最后一步:您需要做的就是连接 5 列中的每一列,您将获得每个用户所需的信息。

我希望这能说明问题

编辑:使用函数 GROUP_CONCAT 将允许您替换最后 2 个步骤

通过某种排名,只需 1 个查询即可完成。

select user_id, group_concat(item_id) from 
(
select 
  user_id
  ,item_id
  ,@item_rank := if(@current_item = user_id, @item_rank+1,1) as item_rank 
  ,@current_item:=user_id
  from
      (      
      select 
        user_id
        ,item_id
        ,count(*) aantal
      from chosen_user_items
      group by user_id,item_id
      order by user_id,count(*) desc
      ) a )b
      where item_rank <6
      group by user_id

这里有一个 sqlfiddle 来测试它。

我只有 group_concat 的顺序有问题,没有相应的顺序。

尝试以下查询以查看 group_concat 之前的结果,也许您可​​以更好地连接它。

select 
  user_id
  ,item_id
  ,@item_rank := if(@current_item = user_id, @item_rank+1,1) as item_rank 
  ,@current_item:=user_id
  from
      (      
      select 
        user_id
        ,item_id
        ,count(*) aantal
      from chosen_user_items
      group by user_id,item_id
      order by user_id,count(*) desc
      ) a 

此查询根据您问题中的数据对 concat 进行了正确排序:

select user_id, group_concat(item_id) from 
(
select 
  user_id
  ,item_id
  ,@item_rank := if(@current_item = user_id, @item_rank+1,1) as item_rank 
  ,@current_item:=user_id
  from
      (      
      select 
        user_id
        ,item_id
        ,count(*) aantal
      from chosen_user_items
      group by user_id,item_id
      order by user_id,count(*) desc
      ) a )b
      where item_rank <6
      group by user_id
      order by user_id,item_rank asc

使用 R 的解决方案。

dbplyr 程序包将允许您 运行 此脚本直接针对数据库,而无需将数据提取到内存中。如果您不想使用 R,您可以 render dbplyr 从您的 R 语句生成的 SQL 查询。

library(tidyverse)
library(stringr)

# --- Set Up ---
dat <- tribble(
  ~user_id, ~item_id,
  1, 1,
  1, 4,
  1, 19,
  1, 10,
  1, 13,
  1, 1,
  1, 11,
  1, 18,
  1, 212,
  1, 654,
  2, 1,
  2, 28,
  2, 568,
  2, 112,
  2, 354,
  3, 4,
  3, 4,
  3, 19,
  3, 212,
  3, 654,
  3, 4,
  3, 4,
  3, 253,
  3, 187,
  3, 212
)

# --- Prep --- 
pre <- dat %>% 
  group_by(user_id) %>% 
  arrange(user_id, item_id) %>% 
  add_count(item_id) %>% 
  rename(
    n_items = n
  ) %>% 
  distinct(user_id, item_id, .keep_all = TRUE) %>% 
  top_n(5, n_items) %>% 
  slice(1:5) %>% 
  arrange(user_id, desc(n_items)) 

# --- Solve ---
# Hacky
solution_one <- pre %>% 
  mutate(collapsed = str_c(item_id, collapse = ", ")) %>% 
  slice(1) %>% 
  select(user_id, collapsed)

# Ideal
solution_two <- pre %>%
  nest() %>% 
  mutate(
    collapsed = data %>% 
      map("item_id") %>% 
      map_chr(str_c, collapse = ", "))

输出:

solution_two
#> # A tibble: 3 x 3
#>   user_id             data            collapsed
#>     <dbl>           <list>                <chr>
#> 1       1 <tibble [5 x 2]>     1, 4, 10, 11, 13
#> 2       2 <tibble [5 x 2]> 1, 28, 112, 354, 568
#> 3       3 <tibble [5 x 2]> 4, 212, 19, 187, 253

这是最佳解决方案,因为您在嵌套列表列 data 中保留了 item_id 及其计数。