【问题标题】:SQL inline view subquerySQL 内联视图子查询
【发布时间】:2016-05-07 08:58:11
【问题描述】:

是否可以从 WHERE 子句的子查询中引用“FROM”子句中定义的内联视图?

SELECT tmp.TeacherName,
       tmp.courseid,
       tmp.AvgAttendingStudents
FROM   (SELECT T.TeacherID              AS ID,
               T.TeacherName            AS Name,
               C.CourseID               AS CourseID,
               avg(L.AttendingStudents) AS AvgAttendingStudents
        FROM   Teachers AS T
               join Courses AS C
                 ON C.TeacherID = T.TeacherID
               join Lessons AS L
                 ON L.CourseID = C.CourseID
        GROUP  BY T.TeacherID,
                  C.CourseID) AS tmp
WHERE  tmp.AvgAttendingStudents = (SELECT max(AvgAttendingStudents)
                                   FROM   tmp AS tmp2
                                   WHERE  tmp2.TeacherID = tmp.TeacherID);  

在此示例中,我试图列出所有教师,并且我想为每个教师展示参加学生人数最多的课程(根据所有课程计算)。我尝试使用内联视图 (tmp) 来计算每门课程的平均参加学生人数,但我不知道是否可以在子查询 SELECT max(...) 中引用该视图。 我需要它来使用 Oracle,但不幸的是,目前我没有任何 Oracle 数据库可以尝试。我尝试使用 MySQL(因为我认为我没有使用任何特定于 Oracle 的功能),但正如预期的那样,我收到错误“表 'db.tmp' 不存在”。 甲骨文有可能吗?

这是我的示例架构:

CREATE TABLE Courses
  (
     CourseID   INTEGER PRIMARY KEY,
     CourseName VARCHAR(32),
     TeacherID  INTEGER
  );

CREATE TABLE Teachers
  (
     TeacherID   INTEGER PRIMARY KEY,
     TeacherName VARCHAR(32)
  );

CREATE TABLE Lessons
  (
     LessonDate        TIMESTAMP,
     CourseID          INTEGER,
     AttendingStudents INTEGER,
     PRIMARY KEY (LessonDate, CourseID)
  );  

(对不起,我的英语不好)

【问题讨论】:

    标签: sql oracle subquery inline-view


    【解决方案1】:

    您是对的,因为您不能以这种方式引用派生表(“内联视图”)。您需要将派生表(“内联视图”)重写为公用表表达式:

    您还有其他错误。在派生表中,您将 TeacherID 重命名为 ID 并将 TeacherName 重命名为 Name,因此您需要使用这些列名而不是“真实”列名。

    Oracle 也不支持 AS 作为表别名,因此您也需要摆脱这些。

    所以直接重写语句是:

    with tmp as (
      SELECT T.TeacherID              AS ID,
             T.TeacherName            AS Name,
             C.CourseID               AS CourseID,
             avg(L.AttendingStudents) AS AvgAttendingStudents
      FROM   Teachers T
             join Courses C
               ON C.TeacherID = T.TeacherID
             join Lessons L
               ON L.CourseID = C.CourseID
      GROUP  BY T.TeacherID,
                C.CourseID
    )
    SELECT tmp.name,
           tmp.courseid,
           tmp.AvgAttendingStudents
    FROM tmp
    where tmp.AvgAttendingStudents = (SELECT max(AvgAttendingStudents)
                                      FROM   tmp tmp2
                                      WHERE  tmp2.id = tmp.id);
    

    但是,由于group by 和聚合函数的无效使用,上述内容不会在 Oracle 中工作。以上将导致“ORA-00979: not a GROUP BY expression”,见SQLFiddle

    为此,您需要在 CTE 中使用窗口函数并摆脱 group by

    with tmp as (
      SELECT T.TeacherID              AS ID,
             T.TeacherName            AS Name,
             C.CourseID               AS CourseID,
             avg(L.AttendingStudents) over (partition by t.teacherid, c.courseid) AS avgattendingstudents 
      FROM   Teachers T
             join Courses C
               ON C.TeacherID = T.TeacherID
             join Lessons L
               ON L.CourseID = C.CourseID
    )
    SELECT tmp.name,
           tmp.courseid,
           tmp.AvgAttendingStudents
    FROM tmp
    where tmp.AvgAttendingStudents = (SELECT max(AvgAttendingStudents)
                                      FROM   tmp tmp2
                                      WHERE  tmp2.id = tmp.id);
    

    请参阅此SQLFiddle 以获取示例。


    请注意,您不能使用 MySQL 测试上述查询,因为它不支持 modern SQL 像公共表表达式或窗口函数。

    但您可以使用 SQLFiddle 示例用数据对其进行测试。

    【讨论】:

      【解决方案2】:

      您可以使用having子句,它可以为您提供一种约束聚合函数的方法。

      这里是一个例子:

          SELECT T.TeacherID              AS ID,
                 T.TeacherName            AS Name,
                 C.CourseID               AS CourseID,
                 avg(L.AttendingStudents) AS AvgAttendingStudents
          FROM   Teachers AS T
                 join Courses AS C
                   ON C.TeacherID = T.TeacherID
                 join Lessons AS L
                   ON L.CourseID = C.CourseID
          GROUP  BY T.TeacherID,
                    T.TeacherName
                    C.CoursesID
          HAVING  avg(L.AttendingStudents) = (SELECT max(AvgAttendingStudents)
                                     FROM   Teachers AS tmp2
                                     WHERE  tmp2.TeacherID = T.TeacherID);
      

      我只是删除了您的第一个嵌套查询并将 AvgAttendingStudents 更改为 avg(L.AttendingStudents) (因为您不能在 Have 子句中使用变量)并在 Group 子句中添加选定的属性,我不测试但这里的方式做这个把戏。

      不要忘记添加在组子句中选择的未聚合变量。

      这里是having clause 的文档。

      【讨论】:

      • teachers 表中没有列AvgAttendingStudents。而且Oracle不支持AS作为表别名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-28
      • 1970-01-01
      • 2022-01-20
      • 2017-11-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多