【问题标题】:Need to PIVOT 1 table in 4 JOINS需要在 4 个 JOINS 中 PIVOT 1 个表
【发布时间】:2021-08-18 11:00:38
【问题描述】:

所以,这是一个非常具体的问题,我希望你们明白我想在那里做什么:

(玛丽亚数据库)

我的桌子:

table_toteach:

teach-id teach teacher .... ....
1.0 Subj1- Tname1
1.2 Subj2- Tname1

table_students:

student ... ...
stname1 -- --
stname2 -- --
stname3 -- --
stname4 -- --
(x-amount of stname) -- --

table_stpresents:

这是一个出勤记录,每个学生每天都有 1 行。如果学生在一天开始时在场,那么他们应该参加了当天安排的每个课程。

student Date present
stname1 Date1 - yes -
stname2 Date1 - no -
x amount of stname Date1 -yes-
stname1 Date2 -no-
stname2 Date2 -yes-
x amount of stname Date2 -yes-

table_timetable:

学生姓名在此以逗号分隔字符串 (CSV) 值的形式引用,每天每个时间表课程有 1 行

date subj1 subj2 subj3 stnames
Date1 Subj1- Subj3- Subj2- stname1, stname2, stname5
DateX Subj3- Subj2- Subj1- stname1, stname4, stname3, stname5

预期结果:

根据学生在场的天数和时间表记录,为每个科目显示 1 行,其中每个学生有一列并包含 LDUS,该科目教给该学生的最近日期对应那个学生。

通过选择不同的主题组来更改表格列数(此表格将成为自定义 UI 的一部分)

注意:我的代码可以动态生成sql代码,所以不需要动态 SQL解决方案。

teach-id teach teacher stname1 stname2 ..... stnameX
1.0 Subj1- Tname1 -LDUS- -LDUS- -LDUS-
1.2 Subj2- Tname2 -LDUS- -LDUS- -LDUS-

更新

我已经准备了一些示例数据:https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=4bf2542240eaa86e4f4624c1e376215a

【问题讨论】:

  • 用您正在使用的数据库标记您的问题。还要指定结果集中的列数。
  • 您永远不会在面向生产的应用程序中使用SELECT *,但您会不会……下次请通过编辑您的帖子来回复 cmets。
  • 如果每一列都需要代表一个学生,那么对于不同的班级,可能会有不同数量的列。如果你需要一个可变列的解决方案,那么我们需要使用动态SQL,这是一个全新的球赛,如果列数是固定的,或者你的代码可以动态生成SQL,那么我们可以提供一个更简单的解决方案跨度>
  • 感谢您的快速回复!我的代码能够动态生成 SQL
  • 我不会撒谎,这是一个可怕的模式来做这种类型的查询。我们是否假设每个科目只有 1 位老师?还是subj 对每个老师来说都是独一无二的?通常时间表会将teach-id 作为返回老师的唯一链接...我根本不会为此使用 PIVOT,它会使您的查询过于复杂,而是让我们使用 CASE 语句

标签: sql join mariadb pivot


【解决方案1】:

首要任务是将所有这些表正确连接在一起并规范化数据。然后,您可以在确保源数据正确后询问如何pivot行。

源数据因两个结构性问题而变得复杂:

  1. table_timetable 表中的主题列在多个列中。为了规范化这些数据,我们可以为每个主题列查询表 1 次并将结果合并在一起
  2. 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

【讨论】:

  • 哇,非常感谢你尝试了一些东西,但我说时间表错了,我的错!时间表有一个名为 Students 的列,并且 1 个字段中有很多 Student。时间表每行使用 3 个主题(3 列),对不起,让我把事情弄清楚 对不起,错误地解决了这个问题,谢谢!时间表:tech1 tech2 tech3 teacher stnames date Subj1 SubjX SubjY Tname1 nameX, nameY, nameZ 2021-08-18 Subj2 SubjX SubjY Tname1 nameX, nameY, nameZ 2021-08-18 我在家时会在 2 小时内回复 非常感谢
  • 此答案与您的帖子相匹配,并且目前的情况将使社区受益。请发表一篇包含架构的新帖子,也许您可​​以像我使用与您的实际运行时匹配的示例数据集一样创建一个小提琴。省略不相关的柯林斯。链接到此,我将提供量身定制的答案。你必须做一些工作;)
  • 所以我做了这些表格,它们实际上是怎样的:dbfiddle.uk/…
  • table_timetable 真的有多个主题栏吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
  • 2021-11-11
  • 2019-11-19
  • 2019-08-26
  • 2019-12-17
  • 2012-11-23
相关资源
最近更新 更多