SQL select:具有可变列数的二维选择

时间:2009-06-14 01:48:25

标签: sql select

CREATE TABLE activities(activityid, name);
CREATE TABLE activity_scores(activityid, studentid, score);   
CREATE TABLE students (id, name);

有没有办法编写一个SELECT查询,以这种格式为每个学生生成一个结果?

studentid | studentname | activity1_score | activity2_score | activity3_score [...]

使用多个查询执行此操作非常简单:

for each studentid in "select id from students":
    print studentid
    for each activity in "select activityid from activities":
        select score from activity_scores where studentid = studentid
        print score

(伪代码,我知道这不准确)

当然,有一种方法可以使用单个SELECT查询创建此结果,对吗?

6 个答案:

答案 0 :(得分:3)

MySQL,SQLite和其他RDBMS都有一些名为GROUP_CONCAT的东西 哪个应该做你想要的(没有测试 - 不知道你在activity_scores表上的连接条件):

SELECT   students.studentid
         , students.studentname
         , GROUP_CONCAT(activity_scores.score)

FROM     activity_scores 
         INNER JOIN activities 
         ON activities.activityid = activity_scores.activityid
         INNER JOIN students 
         ON students.studentid = activities.studentid

GROUP BY students.studentid 
         , students.studentname

但是smarter folks than me可能会说做这样的事情会混淆两个不同的步骤,这些步骤在分开时最容易处理:

  • 数据访问/收集
  • 数据显示

答案 1 :(得分:2)

您基本上在寻找数据透视表。如果您只想使用纯ANSI SQL,则无法执行此操作。 SQL只生成具有可预测列数的结果集(不计算select *')。

但是,可能有一种特定于技术的方法。您使用的是哪个数据库引擎? SQL Server 2005能够执行数据透视表。

答案 2 :(得分:1)

如果活动是已知的并且可以在查询中指定,那么相对容易,如果有点冗长,假设您的SQL方言支持它。例如:

SELECT s.id, s.name,
  (SELECT score FROM activity_scores as JOIN activities a ON a.activityid = as.activityid WHERE studentid = s.id AND a.activityname = 'Basketball') basketball_score,
  (SELECT score FROM activity_scores as JOIN activities a ON a.activityid = as.activityid WHERE studentid = s.id AND a.activityname = 'Football') football_score,
  ...

基本上这称为交叉表查询。如果你想动态地做它然后更难,你可能需要求助于代码或存储过程,因此将取决于你的数据库。这是一个example using SQL Server

答案 3 :(得分:1)

此结果集形状冒犯了关系代数(SQL所基于的)。请参阅SQL反模式帖子#2 What are the most common SQL anti-patterns?

您应该发出此查询并在客户端上格式化结果:

SELECT s.name, a.name, x.score
FROM Activity_Score as x
  JOIN Students s
  ON x.StudentID = s.StudentID
  JOIN Activity a
  ON x.ActivityID = a.ActivityID
ORDER BY s.name, a.name, x.score

答案 4 :(得分:1)

其中一个most frequently asked questions on SO

ANSI SQL不支持这一原因的原因是结果集没有明确定义 - 它将具有任意数量的列。

但是,知道列数,可以生成代码来执行此操作,我总是使用PIVOT运算符为SQL Server 2005生成代码的示例解决方案(列列表是静态的,这就是为什么这个需要使用动态SQL完成动​​态SQL,然后执行它。

答案 5 :(得分:0)

在SQL Server 2005/2008中,您可以尝试将每个学生的活动/分数作为xml集返回。不理想,但有效。类似的东西:

SELECT s.name, 
(select a.name, x.score FROM FROM Activity_Score as x  
JOIN Activity a  ON x.ActivityID = a.ActivityID 
WHERE x.StudentID = s.StudentID FOR XML AUTO) Activities
FROM Students s
ORDER BY s.name