MySQL:在与表A连接的结果中将表B的行作为列。比这更聪明/更好的方法?

MySQL:在与表A连接的结果中将表B的行作为列。比这更聪明/更好的方法?,mysql,row,left-join,identity-column,Mysql,Row,Left Join,Identity Column,我做了家庭作业,在网上和StackOverflow上查找信息,但没有找到问题的答案。我看了看: (不明白) (不同,问题似乎不一样) 我用它来构建我的查询: 我有一张“课程”表,如下所示: ID title --------- 1 Math 2 Latin 3 English 4 Science lesson_id usr_id test_type course_id ---------------------------------------- 1

我做了家庭作业,在网上和StackOverflow上查找信息,但没有找到问题的答案。我看了看:

(不明白) (不同,问题似乎不一样)

我用它来构建我的查询:

我有一张“课程”表,如下所示:

ID  title
---------
1   Math
2   Latin
3   English
4   Science 
lesson_id  usr_id  test_type   course_id
----------------------------------------
1          100       o          1
1          100       w          1
1          24        o          1
1          36        w          1
SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(CASE WHEN test_type = ''',
      test_type,
      ''' THEN ''true'' ELSE ''false'' END) AS ',
      test_type
    )
  ) INTO @sql
FROM Results;


SET @sql 
  = CONCAT('SELECT l.id,
      l.title, ', @sql, ' 
     from lessons l
     left join results r
       on l.id = r.lesson_id
       and r.usr_id = 100
     group by l.id, l.title');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
我还有第二个表“results”,它存储了用户在每节课中通过的测试类型。目前有两种类型的测试:o和w(将来可能会有更多)。表格如下所示:

ID  title
---------
1   Math
2   Latin
3   English
4   Science 
lesson_id  usr_id  test_type   course_id
----------------------------------------
1          100       o          1
1          100       w          1
1          24        o          1
1          36        w          1
SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(CASE WHEN test_type = ''',
      test_type,
      ''' THEN ''true'' ELSE ''false'' END) AS ',
      test_type
    )
  ) INTO @sql
FROM Results;


SET @sql 
  = CONCAT('SELECT l.id,
      l.title, ', @sql, ' 
     from lessons l
     left join results r
       on l.id = r.lesson_id
       and r.usr_id = 100
     group by l.id, l.title');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
在上表中,用户100通过了数学测试o和w。 在上表中,用户24通过了数学测试o。 在上表中,用户36通过了数学测试w

现在,我想为用户100获取一份报告,我想有如下内容:

ID  title      o       w
------------------------------
1    Math       TRUE    TRUE
2    Latin      FALSE   FALSE
3    English    FALSE   FALSE
4    Science    FALSE   FALSE
对于用户36:

ID  title      o       w
------------------------------
1    Math       FALSE    TRUE
2    Latin      FALSE   FALSE
...
对于用户24:

ID  title      o       w
------------------------------
1    Math       TRUE    FALSE
2    Latin      FALSE   FALSE
...
任何其他用户:

ID  title      o       w
------------------------------
1    Math       FALSE   FALSE
2    Latin      FALSE   FALSE
...
我可以通过以下查询来实现这一点:

SELECT l.id, l.title, COALESCE(s.o,FALSE) AS o, COALESCE(t.w, FALSE) AS w FROM lessons l 
LEFT JOIN (
    SELECT r.lesson_id,
    CASE
        WHEN r.test_type='o' THEN TRUE
        ELSE FALSE
    END AS o
    FROM results r WHERE r.itype='o' AND r.usr_id=100
    ) AS s ON l.id=s.lesson_id 
LEFT JOIN (
    SELECT rr.lesson_id,
    CASE
        WHEN rr.test_type='w' THEN TRUE
        ELSE FALSE
    END AS w 
    FROM results rr WHERE rr.test_type='w' AND rr.usr_id=100
    ) AS t ON l.id=t.lesson_id
WHERE l.course_id=1 ORDER BY l.id ASC;
虽然这段代码似乎可以工作(仍在忙于测试),但我不喜欢它,因为它需要两个连接,每个连接都是从一个表中选择的,将来可能会非常大。此查询将经常运行

  • 你能提出一个更好的方法来实现这一点吗(更好可以意味着更聪明、更直接、更快或其他设计……或者任何其他建议:-)

  • 如果我需要坚持这一点,您能否推荐一些参考资料,比如如何设置最佳索引以快速运行此查询?链接,你用过的文章

  • 在创建更多测试类型时会发生什么?我的查询只使用w和o,如果明天还有更多呢?有没有一种通用的方法来做这类事情

  • 欢迎提供任何帮助/建议


    Philippe

    我认为这是一个简单的聚合查询。聚合是按课程id和标题进行的。然后,计算仅旋转o和w的结果:

    select l.lesson_id, l.title,
           max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
           max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
    from lessons l left outer join
         results r
         on l.lesson_id = r.lesson_id
    where r.user_id = <whatever>
    group by l.lesson_id, l.title
    order by 1
    
    选择l.lesson\u id,l.title,
    max(r.test_type='o'然后'TRUE'或'FALSE'结束时的情况)为o,
    最大值(r.test_type='w'然后'TRUE'或'FALSE'结束时的情况)为w
    从课上我离开了外接
    结果r
    在l.lesson_id=r.lesson_id上
    其中r.user\u id=
    按l.lesson_id、l.title分组
    按1订购
    
    用户在此级别进行选择是一个问题,因为它需要来自结果,由于左外部联接,结果可能为空。以下是一个解决问题的变体:

    select l.lesson_id, l.title,
           max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
           max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
    from lessons l left outer join
         results r
         on l.lesson_id = r.lesson_id and
            r.user_id = <whatever>
    group by l.lesson_id, l.title
    order by 1
    
    选择l.lesson\u id,l.title,
    max(r.test_type='o'然后'TRUE'或'FALSE'结束时的情况)为o,
    最大值(r.test_type='w'然后'TRUE'或'FALSE'结束时的情况)为w
    从课上我离开了外接
    结果r
    关于l.lesson_id=r.lesson_id和
    r、 用户id=
    按l.lesson_id、l.title分组
    按1订购
    

    通过将条件移动到“on”子句,我们可以保证所有课程都将保留。

    您尝试执行的操作在其他数据库中称为
    透视。不幸的是,MySQL没有,因此您必须使用聚合函数和
    CASE
    语句创建一个

    如果您知道只有两个
    test\u type
    值,则可以硬编码:

    select l.id,
      l.title,
      max(case when test_type = 'o' then 'true' else 'false' end) as o,
      max(case when test_type = 'w' then 'true' else 'false' end) as w
    from lessons l
    left join results r
      on l.id = r.lesson_id
      and r.usr_id = 100  -- the user id will change for each person
    group by l.id, l.title
    

    现在,如果您想动态执行此操作,这意味着您不知道要转置的
    test\u类型的数量,那么您应该阅读以下文章:

    您的代码如下所示:

    ID  title
    ---------
    1   Math
    2   Latin
    3   English
    4   Science 
    
    lesson_id  usr_id  test_type   course_id
    ----------------------------------------
    1          100       o          1
    1          100       w          1
    1          24        o          1
    1          36        w          1
    
    SET @sql = NULL;
    SELECT
      GROUP_CONCAT(DISTINCT
        CONCAT(
          'MAX(CASE WHEN test_type = ''',
          test_type,
          ''' THEN ''true'' ELSE ''false'' END) AS ',
          test_type
        )
      ) INTO @sql
    FROM Results;
    
    
    SET @sql 
      = CONCAT('SELECT l.id,
          l.title, ', @sql, ' 
         from lessons l
         left join results r
           on l.id = r.lesson_id
           and r.usr_id = 100
         group by l.id, l.title');
    
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    

    请参见

    我尝试了该查询,并提出了一些其他问题:1.如果用户是100(如上例所示),则似乎只返回第一行(其中o和w为true)。理想情况下,我也希望有所有其他的课程,我会再次加入这个课程。。。但这将使更多的人加入,虽然是一个小的。2.我理解max(),很有趣。但是你能解释一下为什么是分组和点菜吗?谢谢,太好了!它起作用了。看来我可以从更多的SQL阅读中获益。“使用SQL和Excel进行数据分析”正在回家的路上。非常感谢。