组的最常见值取决于选择查询

Sta*_*ler 7 mysql

我对如何在SQL中执行此操作感到不满.我有一张桌子:

| User_id | Question_ID | Answer_ID |
|    1    |     1       |    1      |
|    1    |     2       |    10     |
|    2    |     1       |    2      |
|    2    |     2       |    11     |
|    3    |     1       |    1      |
|    3    |     2       |    10     |
|    4    |     1       |    1      |
|    4    |     2       |    10     |
Run Code Online (Sandbox Code Playgroud)

它保存用户对特定问题的答案.一个问题可能有多个答案.用户无法回答两次相同的问题.(因此,每{User_id,Question_ID}只有一个Answer_ID)

我正在尝试找到这个查询的答案:对于特定的问题和答案ID(与同一问题相关),我想找到给定答案的用户给出的其他问题最常见的答案.

例如,对于上表:

For question_id = 1 -> For Answer_ID = 1 - (Question 2 - Answer ID 10)
                       For Answer_ID = 2 - (Question 2 - Answer ID 11)
Run Code Online (Sandbox Code Playgroud)

是否可以在一个查询中执行?它应该在一个查询中完成吗?我应该只使用存储过程或Java吗?

new*_*ver 4

虽然 @rick-james 是对的,但我不确定当你不知道这样的查询通常是如何为 MySQL 编写的时,是否容易开始。

  1. 您需要查询来找出问题的最常见答案:

    SELECT 
      question_id, 
      answer_id, 
      COUNT(*) as cnt 
    FROM user_answers
    GROUP BY 1, 2
    ORDER BY 1, 3 DESC
    
    Run Code Online (Sandbox Code Playgroud)

    这将返回一个表,其中对于每个 Question_id 我们按降序输出计数。

    | 1 |  1 | 3 |
    | 1 |  2 | 1 |
    | 2 | 10 | 3 |
    | 2 | 11 | 1 |
    
    Run Code Online (Sandbox Code Playgroud)
  2. 现在我们应该解决所谓的每组最大任务。问题是,在 MySQL 中,出于性能考虑,此类任务通常不是用纯 SQL 来解决,而是使用 hacks,这些 hacks 依赖于内部如何处理查询的知识。

    在这种情况下,我们知道我们可以定义一个变量,然后迭代准备好的表,了解前一行,这使我们能够区分组中的第一行和其他行。

    SELECT 
      question_id, answer_id, cnt,
      IF(question_id=@q_id, NULL, @q_id:=question_id) as v
    FROM (
      SELECT 
         question_id, answer_id, COUNT(*) as cnt 
      FROM user_answers
      GROUP BY 1, 2
      ORDER BY 1, 3 DESC) cnts
    JOIN (
      SELECT @q_id:=-1
    ) as init;
    
    Run Code Online (Sandbox Code Playgroud)

    确保您已初始化该变量(并在初始化时尊重其数据类型,否则稍后可能会意外转换)。结果如下:

    | 1 |  1 | 3 |    1 |
    | 1 |  2 | 1 |(null)|
    | 2 | 10 | 3 |    2 |
    | 2 | 11 | 1 |(null)|
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在我们只需要过滤掉最后一列中带有 NULL 的行。由于实际上不需要该列,因此我们可以将相同的表达式移至 WHERE 子句中。cnt 列实际上也不需要,所以我们也可以跳过它:

    SELECT 
      question_id, answer_id
    FROM (
      SELECT 
        question_id, answer_id
      FROM user_answers
      GROUP BY 1, 2
      ORDER BY 1, COUNT(*) DESC) cnts
    JOIN (
      SELECT @q_id:=-1
    ) as init
    WHERE IF(question_id=@q_id, NULL, @q_id:=question_id) IS NOT NULL;
    
    Run Code Online (Sandbox Code Playgroud)
  4. 最后值得一提的是,为了使查询高效,您应该拥有正确的索引。此查询需要一个以 (question_id,answer_id) 列开头的索引。由于您无论如何都需要一个 UNIQUE 索引,因此按以下顺序定义它是有意义的:(question_id、answer_id、user_id)。

    CREATE TABLE user_answers (
      user_id INTEGER,
      question_id INTEGER,
      answer_id INTEGER,
      UNIQUE INDEX (question_id, answer_id, user_id) 
    ) engine=InnoDB;
    
    Run Code Online (Sandbox Code Playgroud)

这是一个可以使用的sqlfiddle:http://sqlfiddle.com/#!9/bd12ad / 20。