我有四种模式:
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
答案 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
的表格,但takes
和student
之间似乎没有任何关系。
首先,弄清楚如何列出学生以及他们所采取的天文学课程。每行都会标识student
和course
。
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