【问题标题】:Most common values for a group dependent on a select query依赖于选择查询的组的最常见值
【发布时间】:2017-07-15 14:14:59
【问题描述】:

我对如何在 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 问题给出的最常见答案。

例如,对于上表:

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

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

【问题讨论】:

  • 因此将其分解为多个集合并考虑“当前”记录 ..1) 选择 where question_id = current_question_id 和 user_id current_user_Id。 2)创建临时表,其中问题列表当前问题,按问题ID汇总记录数排序/排序。如果您想包含原始用户的答案,可能会在结果中添加 1... 然后从那里开始。但这是我的思考过程……将其分解为一系列可以重新组合在一起的临时表。

标签: mysql


【解决方案1】:

虽然@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 中解决,而是使用依赖于内部如何处理查询的知识来解决。

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

    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;
    

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

【讨论】:

    【解决方案2】:

    你想要一条鱼吗?还是想学钓鱼?

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

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

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

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

    使用派生表:

    SELECT ...
        FROM ( select#2 )
    

    【讨论】:

      【解决方案3】:

      您的问题是多条件的,您必须从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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-23
        • 2012-06-21
        • 2012-04-01
        • 2011-10-07
        • 1970-01-01
        • 1970-01-01
        • 2013-02-19
        相关资源
        最近更新 更多