首要任务是将所有这些表正确连接在一起并规范化数据。然后,您可以在确保源数据正确后询问如何pivot行。
源数据因两个结构性问题而变得复杂:
-
table_timetable 表中的主题列在多个列中。为了规范化这些数据,我们可以为每个主题列查询表 1 次并将结果合并在一起
-
table_timetable 表中的学生存储为 CSV 字符串值,MariaDB 没有内置函数来查询此类数据,但我们可以使用 CTE 将名称拆分为单独的行,在这个 SO 问题中对此进行了探讨:SQL (Maria DB) split string separated by comma to rows
对于家里的每个人,如果您有类似的要求而没有这两个并发症,那么这个简化的小提琴可能会有所帮助:https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=9dfce7084321699d1262bb50ca724d9e
以下查询将规范化记录集:
with recursive cte as (
select `Date`,subj1,subj2,subj3,' ' as stname, concat(stnames, ',') as stnames, 1 as lev
from table_timetable
union all
select `Date`,subj1,subj2,subj3,substring_index(stnames, ',', 1),
substr(stnames, instr(stnames, ',') + 2), lev + 1
from cte
where stnames like '%,%'
)
, bySubject as (
select `Date`,subj1 as subj, stname
from cte
where lev > 1
union all
select `Date`,subj2 as subj, stname
from cte
where lev > 1
union all
select `Date`,subj3 as subj, stname
from cte
where lev > 1
)
select `Date`,subj,stname
from bySubject
对于我的测试数据库,结果如下:
|日期 |主题 |stname |
|-----------|-------|--------|
|2021-08-16 |Subj1- |stname1|
|2021-08-16 |Subj1- |stname1|
|2021-08-16 |Subj1- |stname2|
|2021-08-17 |Subj2- |stname1|
|2021-08-17 |Subj2- |stname2|
|2021-08-17 |Subj1- |stname2|
|2021-08-18 |Subj1- |stname1|
|2021-08-18 |Subj4- |stname2|
|2021-08-18 |Subj3- |stname1|
|2021-08-19 |Subj1- |stname1|
...
在这里查看小提琴:https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=f31cab287fdd575054e3af457a9cf14d
最后将这些数据 PIVOT 到每个学生的列中...这是 MariaDB 真正让我们失望的地方,没有对 PIVOT 的内置支持,您必须自己手动构建结果。您可以使用更多的 CTE,但如果您的应用程序能够构造 SQL,则更简单的查询是简单地使用 CASE 语句来构造每一列:
with recursive cte as (
select `Date`,subj1,subj2,subj3,' ' as stname, concat(stnames, ',') as stnames, 1 as lev
from table_timetable
union all
select `Date`,subj1,subj2,subj3,substring_index(stnames, ',', 1),
substr(stnames, instr(stnames, ',') + 2), lev + 1
from cte
where stnames like '%,%'
)
, bySubject as (
select `Date`,subj1 as subj, stname
from cte
where lev > 1
union all
select `Date`,subj2 as subj, stname
from cte
where lev > 1
union all
select `Date`,subj3 as subj, stname
from cte
where lev > 1
)
select teacher.`teach-id`, teach, teacher
, MAX(CASE WHEN stname = 'stname1' THEN attendance.Date END) AS stname1
, MAX(CASE WHEN stname = 'stname2' THEN attendance.Date END) AS stname2
, MAX(CASE WHEN stname = 'stname3' THEN attendance.Date END) AS stname3
, MAX(CASE WHEN stname = 'stname4' THEN attendance.Date END) AS stname4
, MAX(CASE WHEN stname = 'stname5' THEN attendance.Date END) AS stname5
FROM bySubject tt
/*INNER JOIN table_students s ON tt.stname = s.student*/
INNER JOIN table_stpresents attendance ON tt.Date = attendance.Date AND tt.stname = attendance.student
INNER JOIN table_toteach teacher ON tt.subj = teacher.teach
WHERE attendance.present = '- yes -'
GROUP BY teacher.`teach-id`, teach, teacher
ORDER BY `teach-id`;
注意:我在table_student 上注释掉了联接,因为您不需要该表中的任何信息,实际上很难将其包含在此输出中,您最好离开如果您需要,您的应用程序逻辑就是解析列名。
您可能会使用连接来添加过滤条件,因此我将其留在那里供您探索。
这会导致:
| teach-id |
teach |
teacher |
stname1 |
stname2 |
stname3 |
stname4 |
stname5 |
| 1.0 |
Subj1- |
Tname1 |
2021-08-18 |
2021-08-18 |
2021-08-19 |
2021-08-19 |
2021-08-18 |
| 1.2 |
Subj2- |
Tname2 |
2021-08-18 |
2021-08-18 |
2021-08-19 |
2021-08-19 |
2021-08-18 |
| 1.5 |
Subj3- |
Tname3 |
2021-08-18 |
2021-08-18 |
2021-08-16 |
2021-08-19 |
2021-08-18 |
| 2.0 |
Subj4- |
Tname4 |
2021-08-18 |
2021-08-18 |
2021-08-19 |
|
2021-08-18 |
这是PIVOT 的一种更简单的形式,如果您在代码中生成查询,则需要维护,请注意您只需为每个学生列重复 max 语句。
这个最终解决方案在以下数据库中fiddle:https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=f07c943d3d92ea0de8d91021728ab7c0
其他方法见Pivoting in MariaDB