【问题标题】:SQL select: two-dimensional select with variable column countSQL select:具有可变列数的二维选择
【发布时间】:2009-06-14 01:48:24
【问题描述】:
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 查询来创建此结果,对吧?

【问题讨论】:

  • 你的唯一性限制是什么?一个学生可以参加多个活动吗?一个学生可以多次参加同一个活动吗?单个学生是否可以多次参加同一活动并获得多次相同的分数?
  • 假设每个学生在一项活动中只有一个分数,并且每项活动应该只有一个分数。

标签: sql select


【解决方案1】:

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 可能会说,做这样的事情会混淆两个不同的步骤,当分开时最容易处理:

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

【讨论】:

    【解决方案2】:

    您基本上是在寻找数据透视表。如果您只想使用纯 ANSI SQL,那么没有办法做到这一点。 SQL 只生成具有可预测列数的结果集(不包括 select *'s)。

    但是,可能有一种特定于技术的方法可以做到这一点。您使用的是哪个数据库引擎? SQL Server 2005 有能力做一个透视表。

    【讨论】:

      【解决方案3】:

      此结果集形状违反了关系代数(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】:

        most frequently asked questions on SO 之一。

        ANSI SQL 不支持此功能的原因是结果集没有明确定义 - 它的列数将任意变化。

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

        【讨论】:

        • +1 "ANSI SQL 是结果集定义不明确" 一直在找的那句话...
        【解决方案5】:

        如果活动是已知的并且可以在查询中指定,那么它相对容易,如果有点冗长,假设你的 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

        【讨论】:

        • 您可能希望将第二个子查询结果命名为 football_score。
        【解决方案6】:

        在 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多