使用第二个表中的字段动态选择

xkrw2x1b  于 2021-06-15  发布在  Mysql
关注(0)|答案(2)|浏览(369)

这个问题在这里已经有了答案

mysql将行透视为动态列数(1个答案)
去年关门了。
我有三张table:
带有学生姓名的表1(字段=studentid、firstname、lastname)
带有教师姓名的表2(字段=teacherid、firstname、lastname、initials*)(*initials字段包含每个教师的缩写:“jd”代表“john doe”,“fb”代表“fred bloggs”,等等)
表3列出了每位老师给每位学生的分数(字段=studentid、teacherid、grade)
我想生成一个查询,列出所有的学生,连同他们的成绩;比如:

FirstName | LastName | JD | FB
==============================
Paul      | Smith    |  7 |  8
Nancy     | Brown    |  5 |  4
Chris     | Nobody   |  6 |  9

问题是firstname和lastname后面的列应该根据表2(给出成绩的教师)的内容“动态”生成。换言之,如果一个新老师被添加到该表中(比如,zack zanny),那么(在他给所有学生评分之后)相同的查询应该会产生如下结果:

FirstName | LastName | JD | FB | ZZ
===================================
Paul      | Smith    |  7 |  8 |  3
Nancy     | Brown    |  5 |  4 |  1
Chris     | Nobody   |  6 |  9 |  2

我说清楚了吗?
我搜索过这个论坛和其他论坛,也尝试过阅读mysql pivot表,但我肯定做错了什么。。。任何指点都将不胜感激。提前谢谢。

2izufjch

2izufjch1#

mysql本机不支持动态数据透视,您必须使用动态sql:
查询teachers表以列出所有id和缩写
使用结果构建sql查询
运行查询
这篇文章给出了一个工作示例,我根据您的用例改编如下:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(IF(g.TeacherID = ''',
      TeacherID,
      ''', g.grade, NULL)) AS ',
      Initials
    )
  ) INTO @sql
FROM table_2;

SET @sql = CONCAT('SELECT 
        s.firstname,
        s.lastname,
        ', @sql, ' 
    FROM
        table_3 g
        INNER JOIN table_1 s ON s.studentid = g.studentid
    GROUP BY 
        s.studentid,
        s.firstname,
        s.lastname';

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
0qx6xfy6

0qx6xfy62#

不能编写基于找到的数据值动态扩展其列的sql查询。在准备查询时,选择列表中的列必须固定。
因此,在编写查询之前,您需要知道所有的教师id。一种方法是查询它们:

SELECT TeacherID FROM Table_2;

然后根据该查询的结果,应用程序代码需要使用所需的列数构建pivotsql查询。换句话说,使用循环,在select列表中为前一个查询返回的每个teacherid追加一列。

SELECT st.FirstName, st.LastName,
  MAX(CASE TeacherId WHEN 'JD' THEN Grade END) AS `JD`,
  MAX(CASE TeacherId WHEN 'FB' THEN Grade END) AS `FB`,
  MAX(CASE TeacherId WHEN 'ZZ' THEN Grade END) AS `ZZ`
FROM Table_1 AS st
LEFT OUTER JOIN Table_3 AS gr ON st.StudentId = gr.StudentId
GROUP BY st.FirstName, st.LastName;

max()函数只是为了满足聚合查询的规则:select列表中的每一列都必须在groupby子句中按名称命名,否则就必须在max()这样的聚合函数中。我假设每个老师每个学生有一个年级,所以我也可以使用min()甚至avg()。
要进行pivot查询,必须在查询中显式地编写列,但是可以通过首先发现存在哪个教师id来稍微自动化该过程。
另一种方法是编写一个查询,将学生与成绩关联起来,这意味着你的应用程序将为每个学生获取多行数据——为每个老师获取一行数据。

SELECT st.FirstName, st.LastName, TeacherId, Grade
FROM Table_1 AS st
LEFT OUTER JOIN Table_3 AS gr ON st.StudentId = gr.StudentId;

然后你在你的应用程序中循环结果,并以表格的方式呈现它。
没有办法绕过它,要做一个pivot,要么在准备查询之前编写一些构建查询的应用程序代码,要么使用固定查询,但在获取原始数据之后编写一些应用程序代码来制表结果。
不管怎样,代码不是火箭科学。

相关问题