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

时间:2017-07-03 17:24:40

标签: 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     |

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

我正在尝试找到此查询的答案:对于特定问题和答案ID(与同一问题相关),我想找到具有给定答案的用户对OTHER问题给出的最常见答案。< / p>

例如,对于上表:

For question_id = 1 -> For Answer_ID = 1 - (Question 2 - Answer ID 10)
                       For Answer_ID = 2 - (Question 2 - Answer ID 11)

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

3 个答案:

答案 0 :(得分:4)

你想要一条鱼吗?或者你想学习如何捕鱼?

您的问题似乎有多个步骤。

  1. 获取有关“具有给定答案的用户的问题”的信息。设计此SELECT并假设结果形成一个新表。

  2. 应用“OTHER”限制。这可能是AND ... != ...添加的次要SELECT #1

  3. 现在找到“最常见的答案”。这可能涉及ORDER BY COUNT(*) DESC LIMIT 1。很可能

  4. 使用派生表:

    SELECT ...
        FROM ( select#2 )
    

答案 1 :(得分: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
    

    这将返回一个表格,其中我们输出的每个question_id按降序排列。

    | 1 |  1 | 3 |
    | 1 |  2 | 1 |
    | 2 | 10 | 3 |
    | 2 | 11 | 1 |
    
  2. 现在我们应该解决所谓的greatest-n-per-group任务。问题在于,在MySQL中为了性能,这样的任务通常不是在纯SQL中解决,而是使用黑客知识如何在内部处理查询。

    在这种情况下,我们知道我们可以定义一个变量,然后遍历ready表,了解前一行,这样我们就可以区分组中的第一行和其他行。

    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;
    

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

    | 1 |  1 | 3 |    1 |
    | 1 |  2 | 1 |(null)|
    | 2 | 10 | 3 |    2 |
    | 2 | 11 | 1 |(null)|
    
  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;
    
  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;
    
  5. 这是一个可以玩的方形小说:http://sqlfiddle.com/#!9/bd12ad/20

答案 2 :(得分:1)

您的问题是多条件的,您必须向Question表中的询问用户提出第一个问题:

select question_id,user_id from question

然后插入问题的答案,并在您的Java代码中进行一些检查(用户已经回答了与提出此问题的用户相同的问题,用户多次回答此问题)。

select question_id,user_id from question where user_id=asking-user_id // gets all questions and show on UI
select answer_id,user_id from answer where user_id=answering-user_id // checks the answers that particular user