SQL - 不同类别的列

时间:2010-08-31 17:23:21

标签: 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
----------------------
St1      A    B    A
St2      B    B    C
St3      B    A    B

我尝试了不同的东西,但我不知道如何制作这样的打印输出。任何帮助表示赞赏!

6 个答案:

答案 0 :(得分:11)

使用:

  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

答案 1 :(得分:1)

我曾经问过类似question一段时间。你需要类似于数据透视表的东西,但是,这在SQLite中是不可用的(据我所知)。

答案 2 :(得分:1)

我相信如果您要扩展此系统以包含更多信息,那么您可以从重新编写数据库中受益,我会像这样构建它:

表名 =粗体

列名 =斜体

<强>学生

  • SID (主键)
  • 有关学生的其他信息

<强>测试

  • TID (主键)
  • 有关测试的其他信息

测试成绩

  • GID (主键)
  • TID (外键)
  • SID (外键)
  • 等级

这种结构基于一种称为数据库规范化的想法(如果你谷歌它,你会得到很多信息)。我将在下面给你一个部分摘要,但如果你要做很多SQL,你应该自己阅读:

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

e.g。学生1有SID 1,他的所有测试在SID栏中都有1。同样适用于学生2,3,4等。

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

要从这些表中检索您想要的内容,我会使用它(用PHP编写):

$sql = 'SELECT * FROM Tests ORDER BY TID';
$tempresult = mysql_query($sql);
while($temprow = mysql_fetch_array($tempresult)){
    echo $temprow['TID'];
}

$sql = 'SELECT * FROM Students';
$result = mysql_query($sql);
while($row = mysql_fetch_array($result)){
   echo '\n'.$row['SID'];
   $sql = 'SELECT * FROM Grades WHERE SID='.$row['SID'].' ORDER BY TID';
   $result2 = mysql_query($sql);
   while($row2 = mysql_fetch_array($result2)){
      echo ' '.$rows['Grade'];
   }
}

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

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

答案 3 :(得分:1)

有两种方法可以做到这一点,两者都是(在纯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是该值)。以下适用于MSS2005:

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

这将更高效,而且更优雅。列列表仍然无法动态确定AFAIK,但如果您从应用程序代码进行此查询,或使用MS SQL Server中的sp_executesql内置存储过程从另一个存储过程生成查询,则它们很容易生成功能

答案 4 :(得分:0)

试试这个

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”。

答案 5 :(得分:0)

我想在@OMG_Ponies的答案中添加一些解释,因为它对SQL no-super-users(比如我自己)很有用

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

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 when ... then ... end可以获得所需的列

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 

但是,我们发现有必要按“学生”字段对结果进行分组。当我们分组时,我们必须指定一个聚合函数来指定在具有相同值“student”的多个行的情况下要保留的值。在这种情况下,我们使用“max”函数来丢弃null。

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
-----------------------------
7    St1      A    B     A      
8    St2      B    B     C  
9    St3      B    A     B

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