显示至少16门课程的SQL语句

时间:2015-02-03 02:50:19

标签: mysql sql oracle

我有四种模式:

takes(ID,course_id,sec_id,semester,year)
student(ID,name,dept_name,tot_credit)
course(course_id,title,dept_name,credits)
department(dept_name,building,budget)

我想创建一个查询,查找每个天文学生的姓名和身份,其名称以字母“T”开头,并且至少没有参加过16门天文学课程。

我能做到这一点的最简单方法是什么?

我已经写了这个开头位

SELECT name, id
FROM student
WHERE dept_name='Astronomy' AND name LIKE '%T%'

我不太确定如何完成此任务。

非常感谢任何帮助:)

这是结果

NAME                 ID    CLASS_TAKEN
-------------------- ----- -----------
Tolle                38279          12 
Teo                  62268          13 
Tolle                93223          13 
Tsukamoto            17707           5 
Titi                 11576           9 
Teo                  91772          12 
Toraichi             50387          11 
Tewari               80754          14 
Tiroz                64091          14 

 9 rows selected 

我需要使用ID为91772和Tewari 80754的Teo

4 个答案:

答案 0 :(得分:1)

鉴于我对要求和评论的解读,很明显问题不是很明确。 :-)你在寻找的是学生们(天文学系提供的课程总数) - (学生所采用的天文学课程数量)> = 16.那么,我们如何找到这些价值观?首先,让我们从天文学部门提供的课程总数开始。这很简单:

SELECT COUNT(*) AS ASTRONOMY_COURSE_COUNT
  FROM COURSE
  WHERE DEPT_NAME = 'ASTRONOMY'

现在,第二部分是确定每个学生所服用的天文学部门的课程数量。要做到这一点,我们需要从学生开始,加入学生所参加的课程(TAKES表),然后加入COURSES表,找出每个课程所属的部门。像下面这样的东西应该这样做:

SELECT s.ID, s.NAME, COUNT(*) AS STUDENT_ASTRO_COUNT
  FROM STUDENT s
  INNER JOIN TAKES t
    ON t.ID = s.ID
  INNER JOIN COURSE c
    ON c.COURSE_ID = t.COURSE_ID
  WHERE c.DEPT_NAME = 'ASTRONOMY' AND
        s.NAME LIKE 'T%'
  GROUP BY s.ID, s.NAME;

好的,现在我们需要把它放在一起。你已经为Oracle和MySQL标记了这个问题所以我猜你会接受任何一个数据库的有效语法;因此,我将使用Oracle公用表表达式语法将所有内容组合在一起:

WITH ASTRONOMY_COURSES AS (SELECT COUNT(*) AS ASTRONOMY_COURSE_COUNT
                             FROM COURSE
                             WHERE DEPT_NAME = 'ASTRONOMY'),
     STUDENT_ASTRO_COURSES AS (SELECT s.ID,
                                      s.NAME,
                                      COUNT(*) AS STUDENT_ASTRO_COUNT
                                 FROM STUDENT s
                                 INNER JOIN TAKES t
                                   ON t.ID = s.ID
                                 INNER JOIN COURSE c
                                   ON c.COURSE_ID = t.COURSE_ID
                                 WHERE c.DEPT_NAME = 'ASTRONOMY' AND
                                       s.NAME LIKE 'T%'
                                 GROUP BY ID)
SELECT s.ID,
       s.NAME,
       s.STUDENT_ASTRO_COUNT,
       a.ASTRONOMY_COURSE_COUNT - s.STUDENT_ASTRO_COUNT AS UNTAKEN_COUNT
  FROM STUDENT_ASTRO_COURSES s
  CROSS JOIN ASTRONOMY_COURSES a
  WHERE a.ASTRONOMY_COURSE_COUNT - s.STUDENT_ASTRO_COUNT >= 16;

请注意,CROSS JOIN用于将子查询放在一起。这意味着每个子查询的所有行都连接到另一个子查询的所有行 - 但是在这种情况下,ASTRONOMY_COURSES子查询只返回一行,我们正在做的是将ASTRONOMY_COURSE_COUNT值附加到由STUDENT_ASTRO_COURSES子查询。

至少应该让你非常接近。根据需要修改。

未在动物身上进行测试 - 您将成为第一个! : - )

分享并享受。

答案 1 :(得分:0)

您的查询需要引用更多的表而不仅仅是student表。

您的表格似乎缺少student已采用course的一些重要信息。有一个名为takes的表格,但takesstudent之间似乎没有任何关系。

首先,弄清楚如何列出学生以及他们所采取的天文学课程。每行都会标识studentcourse

SELECT s.id     AS student_id
     , s.name   AS student_name
     , t.???
  FROM student s
  JOIN ??? t
    ON t.student_id = s.id
 WHERE ...

您可能还需要添加其他"加入"到另一张桌子,以确定学生所选择的课程是天文学课程。

要包括未参加任何天文学课程的学生,您可以使用外部联接,而不是内部联接。 (这意味着在LEFT之前包含JOIN关键字,并将谓词从WHERE子句重定位到ON子句。(WHERE子句中的谓词只能通过非NULL值来满足才会否定连接的外部 - 。

一旦你有一个返回该集合的查询(学生以及他们已经采取的任何天文学课程),你可以添加一个GROUP BY条款到"崩溃"一组行成一行。 (看起来你想要排队"由学生分组#34;)

然后可以使用COUNT()SUM()等聚合函数来获取每个组的行数。 (如果您不想计算课程的重新学习时间(对于学生而言是重复的#34;课程),您可以使用COUNT(DISTINCT t.foo)表格。< / p>

然后可以将HAVING子句添加到查询中,以将聚合表达式返回的值与常量值进行比较,以仅返回满足特定条件的行。


<强>后续

假设:

CREATE TABLE course
( id         INT UNSIGNED NOT NULL COMMENT 'PK'
, title      VARCHAR(30)  NOT NULL COMMENT 'course title'
, dept_name  VARCHAR(30)  NOT NULL COMMENT 'FK ref dept.name'
, credits    DECIMAL(5,2)          COMMENT 'credit hours'
, PRIMARY KEY (id)
);

CREATE TABLE student
( id         INT UNSIGNED NOT NULL COMMENT 'PK'
, name       VARCHAR(30)  NOT NULL COMMENT 'student name'
, dept_name  VARCHAR(30)  NOT NULL COMMENT 'FK ref dept.name'
, tot_credit INT                   COMMENT '?'
, PRIMARY KEY (id)
);

CREATE TABLE takes
( student_id INT UNSIGNED NOT NULL COMMENT 'FK ref student.id'
, course_id  INT UNSIGNED NOT NULL COMMENT 'FK ref course.id'
, sec_id     INT UNSIGNED NOT NULL COMMENT '?'
, semester   INT UNSIGNED NOT NULL COMMENT '?'
, year       INT UNSIGNED NOT NULL COMMENT '?'
, PRIMARY KEY (student_id, course_id, sec_id, semester, year)
, CONSTRAINT FK_takes_course FOREIGN KEY (course_id) REFERENCES course (id)
, CONSTRAINT FK_takes_student FOREIGN KEY (student_id) REFERENCES student (id)
);

查询获取学生名单......

SELECT s.id
     , s.name
  FROM student s
 WHERE s.name LIKE 'T%'
   AND s.dept_name = 'ASTRONOMY'

获取学生名单以及他们所学的课程,并返回他们所采取的天文课程的ID ...

SELECT s.id     AS student_id
     , s.name   AS student_name
     , c.id     AS course_id
  FROM student s
  LEFT
  JOIN takes t
    ON t.student_id = t.id
  LEFT
  JOIN course c
    ON c.id = t.course_id
   AND c.dept_name = 'ASTRONOMY'
 WHERE s.name LIKE 'T%'
   AND s.dept_name = 'ASTRONOMY'

使用GROUP BY将每个学生的行折叠为一个,并使用聚合函数来获取计数或总计。

SELECT s.id                  AS student_id
     , s.name                AS student_name
     , SUM(c.credits)        AS total_astronomy_credits_taken
     , COUNT(c.id)           AS count_astronomy_courses_taken
     , COUNT(DISTINCT c.id)  AS count_distinct_astronomy_courses_taken
  FROM student s
  LEFT
  JOIN takes t
    ON t.student_id = t.id
  LEFT
  JOIN course c
    ON c.id = t.course_id
   AND c.dept_name = 'ASTRONOMY'
 WHERE s.name LIKE 'T%'
   AND s.dept_name = 'ASTRONOMY'
 GROUP
    BY s.id
     , s.name

要从此结果集中省略行,请添加HAVING子句。例如,要排除total_astronomy_credits_taken大于或等于12的行......

HAVING total_astronomy_credits_taken >= 12

如果要按特定顺序返回行,请在ORDER BY子句中指定

 ORDER BY s.id

如果要使用零替换聚合中的NULL值,可以在IFNULL(foo,0)函数中扭曲聚合表达式,例如

     , IFNULL(COUNT(c.id),0)    AS count_astronomy_courses_taken

答案 2 :(得分:0)

你需要使用所有表吗?

表格department与学生没有关联, 表格takes与学生没有关联, 表格course与学生没有关联。 如果学生列出了所有天文学的总学分,我认为可以使用:

select name, id, MAX(tot_credit) as credits
from student
where dept_name='Astronomy' and name like 'T%'
group by name, id
having MAX(tot_credit)<=16

PS - 你的架构不好;缺少PK-FK参考

答案 3 :(得分:-2)

试试这个:

select a.name, a.id, count(b.ID) as class_taken   
from student a inner join takes b
on a.ID = b.ID
inner join course c
on b.course_id = c.course_id   
where a.dept_name='Astronomy' and substring(a.name,1,1) = 'T'
group by a.name, a.id
having count(b.ID) < 17