MYSQL从按用户分组的表中选择计数的前5位

MYSQL从按用户分组的表中选择计数的前5位,mysql,group-by,limit,group-concat,Mysql,Group By,Limit,Group Concat,我提前道歉,因为我可能没有正确描述我的问题。我正在尝试编写一个查询,该查询将每个用户选择的前5个最流行的\u user\u项,并通过group\u concat将每个用户的前几行连接到一个逗号分隔的字符串中,然后按用户的\u id进行分组 例如,如果用户_id为1的项_id为5行,项_id为2的两行,项_id为3的三行,以及4 5和6的一行,则结果将是1、3、2、4、5 下面是我的示例表结构 名称:所选用户项目 下面是我想要的输出的一个例子: user_id | group_concat_res

我提前道歉,因为我可能没有正确描述我的问题。我正在尝试编写一个查询,该查询将每个用户选择的前5个最流行的\u user\u项,并通过group\u concat将每个用户的前几行连接到一个逗号分隔的字符串中,然后按用户的\u id进行分组

例如,如果用户_id为1的项_id为5行,项_id为2的两行,项_id为3的三行,以及4 5和6的一行,则结果将是1、3、2、4、5

下面是我的示例表结构

名称:所选用户项目

下面是我想要的输出的一个例子:

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个不同的步骤

首先,您需要选择/选择/排序将显示的行。 这可以通过使用行数和分区来实现,但在本指南中,它们向您展示了MYSQL等价解决方案

其次,您需要筛选行数小于5的行,这将类似于每个查询的限制5

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

最后一步:您只需在5列中的每一列进行搜索,就可以获得每个用户所需的信息

我希望这能澄清问题


编辑:使用函数组将允许您用某种排序替换最后两个步骤,可以用一个查询来完成

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
此查询根据问题中的数据正确排序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,可以呈现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

这是最好的解决方案,因为您在嵌套列表列数据中保留了项id及其计数

谢谢你的链接。我来看看。你知道用这种方法是否可以实现组?这种方法可以让你为每个用户选择要连接的元素以及它们的显示顺序,但它不会连接它们,只是按照你的意愿对它们进行编号。也许我问错了,但这不是目标吗?对于concat,我认为你需要完成两个步骤。首先,使行的行为类似于列,这可以通过枢轴实现。第二,你需要对结果列进行搜索。没错,我也可以做一个临时的查找TableEdited答案,以使你所期望的一切都更清楚。谢谢你,我会看一看,然后再给你回复。很抱歉耽搁了,这工作做得很好!我知道我至少需要两个子查询。感谢您提供此解决方案!但我不知道如何将其转换为mysql。
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