SQL-不同类别的列

SQL-不同类别的列,sql,sqlite,pivot,Sql,Sqlite,Pivot,我是SQL新手。我有一个包含不同考试数据的数据库,例如: Student Test Grade -------------------- St1 T1 A St2 T1 B St3 T1 B St1 T2 B St2 T2 B St3 T2 A St1 T3 A St2 T3 C St3 T3 B 然后,我想使用测试(T1、T2和T3)作为列打印一份报告: Student T1 T2 T3 --

我是SQL新手。我有一个包含不同考试数据的数据库,例如:

Student Test Grade
--------------------
St1    T1   A
St2    T1   B
St3    T1   B
St1    T2   B
St2    T2   B
St3    T2   A
St1    T3   A
St2    T3   C
St3    T3   B
然后,我想使用测试(T1、T2和T3)作为列打印一份报告:

Student  T1   T2   T3
----------------------
St1      A    B    A
St2      B    B    C
St3      B    A    B
我尝试过不同的方法,但我被困在如何制作这样的打印输出上。感谢您的帮助

使用:

  SELECT t.student,
         MAX(CASE WHEN t.test = 'T1' THEN t.grade END) AS T1,
         MAX(CASE WHEN t.test = 'T2' THEN t.grade END) AS T2,
         MAX(CASE WHEN t.test = 'T3' THEN t.grade END) AS T3
    FROM TABLE t
GROUP BY t.student

不久前我问了一个类似的问题。您需要类似于透视表的东西,但在SQLite中不可用(据我所知)。

我相信,如果您打算扩展此系统以包含更多信息,您可以从修改数据库中获益,我会这样构建它:

表名=粗体

列名=斜体

学生

  • SID(主键)
  • 关于该学生的其他信息
测试

  • TID(主键)
  • 关于测试的其他信息
考试成绩

  • GID(主键)
  • 工业贸易署(外键)
  • SID(外键)
  • 等级
这种结构基于一种叫做数据库规范化的思想(如果你用谷歌搜索它,你会得到很多信息)。我将在下面对您进行部分总结,但如果您打算大量使用SQL,您应该自己阅读:

首先要知道的是,主键只是一个唯一的标识符,它通常不是信息的一部分(但是,因为它是唯一的,每个数据的主键必须有不同的值),外键是一种从另一个表中的行引用一个表中的行的方法,使用referencee的主键:例如,这里每个年级的外键SID根据其主键SID引用单个学生

e、 g.学生一的SID为1,其所有测试的SID列中均为1。学生2、3、4等也一样

规范化的基本思想是,所有唯一的数据只存储一次,然后在使用它的其他地方引用(如果您看看示例中键的结构,所有学生信息都存储在一个表中,然后在他们的考试成绩中引用,而不是在每个成绩中重复)

要从这些表中检索所需内容,我将使用以下内容(用PHP编写):

您可以在echo语句中为此添加格式,还可以打印您选择添加的任何额外信息。如果你有任何问题,请问他们

编辑:我已经阅读了其他方法,并且同意他们的方法很可能优于其他方法,我唯一不确定的是pivot表是否可以扩展以处理不同数量的测试,如果可以(或者您不需要),那么我建议他们的方法,否则,我觉得这可能会在您的应用程序中占有一席之地。

试试这个

SELECT Student, MAX(CASE WHEN Test = 'T1' THEN Grade END) AS T1,
   MAX(CASE WHEN Test = 'T2' THEN Grade END) AS T2,
   MAX(CASE WHEN Test = 'T3' THEN Grade END) AS T3 FROM tablename  GROUP BY Student

使用表名而不是“tablename”。

有几种方法可以做到这一点,这两种方法(在纯SQL中,而不是在生成SQL命令的代码中)都需要知道并固定列数。最容易实现的是:

SELECT eg.Student,
(SELECT Grade from ExamGrade eg1 WHERE eg1.Student = eg.Student AND Test = 'T1') AS T1
(SELECT Grade from ExamGrade eg2 WHERE eg2.Student = eg.Student AND Test = 'T2') AS T2
(SELECT Grade from ExamGrade eg3 WHERE eg3.Student = eg.Student AND Test = 'T3') AS T3
FROM ExamGrade eg
这在包括SQLite在内的任何环境中都可以使用,并且可以使用标量值函数GetTest()使其更加优雅,该函数将获取学生和测试编号并返回分数。然而,在任何情况下,这既不是性能,也不是封闭的改变;它将查询N个测试的N平方次表,如果添加第4个测试,则必须更改此查询以将其包含在报告中

如果Student和Test的组合是唯一的,并且您在一个具有Pivot功能的数据库中工作(显然SQLite没有),那么您可以将Pivot查询用于几乎任何聚合器(具有单个值的集合的MAX/MIN/AVG/SUM就是该值)。2005年的工作如下:

SELECT Student, T1, T2, T3
FROM (Select Student, Test, Grade FROM ExamGrade) As SourceQuery
PIVOT (MAX(Grade) FOR Test IN (T1, T2, T3)) AS PivotTable

这将更具性能,也更优雅。列列表仍然无法动态确定,但如果您是从应用程序代码进行此查询,则生成列列表非常简单,或者使用MS SQL Server中的sp_executesql内置存储过程从另一个存储过程或函数生成查询。

我想对@OMG_Ponies的答案添加一些解释,因为它可能对SQL无超级用户(如我自己)有用

让我们创建一个示例表并添加虚拟数据:

CREATE TABLE t (
   t_ID integer primary key autoincrement not null,
   student integer,
   test text,
   grade text
);

INSERT INTO t 
   (student, test, grade)
VALUES
   ('St1', 'T1', 'A'),
   ('St2', 'T1', 'B'),
   ('St3', 'T1', 'B'),
   ('St1', 'T2', 'B'),
   ('St2', 'T2', 'B'),
   ('St3', 'T2', 'A'),
   ('St1', 'T3', 'A'),
   ('St2', 'T3', 'C'),
   ('St3', 'T3', 'B');
因此,我们有以下几点:

t_ID student test grade
-------------------------
 1   St1     T1     A
 2   St2     T1     B
 3   St3     T1     B
 4   St1     T2     B
 5   St2     T2     B
 6   St3     T2     A
 7   St1     T3     A
 8   St2     T3     C
 9   St3     T3     B
使用语句
case时。。。然后。。。结束
可以获得所需的列

SELECT 
   t_ID,
   student,
   (case when t.test = 'T1' then t.grade end) as T1,
   (case when t.test = 'T2' then t.grade end) as T2,
   (case when t.test = 'T3' then t.grade end) as T3
FROM t
   order by student
结果

t_ID student  T1    T2     T3
----------------------------------
1    St1      A    NULL  NULL       
4    St1     NULL   B    NULL   
7    St1     NULL  NULL   A
2    St2      B    NULL  NULL   
5    St2     NULL   B    NULL   
8    St2     NULL  NULL   C
3    St3      B    NULL  NULL   
6    St3     NULL   A    NULL   
9    St3     NULL  NULL   B 
t_ID student  T1   T2   T3
-----------------------------
7    St1      A    B     A      
8    St2      B    B     C  
9    St3      B    A     B
然而,我们发现有必要按字段“学生”对结果进行分组。分组时,我们必须指定一个聚合函数,以指定在多行具有相同值“student”的情况下保留哪个值。在本例中,我们使用“max”函数来丢弃空值

SELECT 
   t_ID,
   student,
   max(case when t.test = 'T1' then t.grade end) as T1,
   max(case when t.test = 'T2' then t.grade end) as T2,
   max(case when t.test = 'T3' then t.grade end) as T3
FROM t
GROUP BY student
ORDER BY student
结果

t_ID student  T1    T2     T3
----------------------------------
1    St1      A    NULL  NULL       
4    St1     NULL   B    NULL   
7    St1     NULL  NULL   A
2    St2      B    NULL  NULL   
5    St2     NULL   B    NULL   
8    St2     NULL  NULL   C
3    St3      B    NULL  NULL   
6    St3     NULL   A    NULL   
9    St3     NULL  NULL   B 
t_ID student  T1   T2   T3
-----------------------------
7    St1      A    B     A      
8    St2      B    B     C  
9    St3      B    A     B

最后一点。由于我们还没有按t_ID分组,也没有为其指定聚合函数,因此您应该假设每行的t_ID的值是每组的随机值。小心点

“代码”按钮的格式设置将允许您使用固定宽度的字体,帮助这些表格排列整齐,更具可读性嗨,您太棒了!非常感谢你!结果很完美!干杯-MikeThank非常感谢您提供的详细信息!我真的很感激!谢谢大家!-迈克