資料庫系統概念(第六版)課後習題答案-第三章SQL(實踐習題)
3.1 使用大學模式,用SQL寫出如下查詢。(建議在一個數據庫上實際執行這些查詢,使用我們在本書的Web網站db-book.com上提供的樣本資料,上述網站還提供瞭如何建立一個數據庫和載入樣本資料的說明。)
Classroom(building, room_number, capacity) Department(dept_name, building, budget) Course(course_id, title, dept_name, credits學分)課程資訊 Instructor(ID, name, dept_name, salary)教員資訊登記 Section(course_id, sec_id, semester, year Teaches(ID, course_id, sec_id, semester, year)授課資訊 Student(ID, name, dept_name, tot_cred學分) Takes(ID, course_id, sec_id, semester, year, grade)選課資訊 Advisor(s_ID, i_ID)輔導員資訊 Time_slot(time_slot_id, day, start_time, end_time) Prereq(course_id, prereq_id |
A. 找出Comp.Sci. 系開設的具有3個學分的課程名稱。
Selecttitle
Formcourse
Wheredept_name = ‘Comp.Sci.’ and credits = 3;
B. 找出名叫Einstein的教師所教的所有學生的標識,保證結果中沒有重複。
原始答案:
Selects_ID
From(select name, s_ID
Forminstructor, advisor
Whereinstructor.ID = advisor.i_ID)
Where name = ‘Einstein’
修改後的答案:
修改原因:上述篩選的是名叫Einstein的教師作為輔導員名下的學生。題目應該理解為名叫Einstein的教師開設的課程中教授的學生,所以修改SQL查詢語句如下:
授課老師的關係:
Section naturaljoin teaches natural join instructor
(course_id, sec_id, semester, year,building, room_number, time_slot_id, ID, name, dept_name, salary)
學生選課的關係
Studentnatural join takes
(ID,name, dept_name, tot_cred, course_id, sec_id,semester, year, grade)
決定一門課程的主碼是(course_id, sec_id, semester, year)
所以老師的關係要和學生的關係以課程的主碼進行連線,然後篩選出特定老師教的學生。
完整SQL語句如下:
Select distinctstudent.ID
From (Section naturaljoin teaches natural join instructor)
Join(Student natural join takes)
Using (course_id, sec_id, semester, year)
Where section.name = ‘Einstein’;
C. 找出教師的最高工資
用集合的比較語句實現:
Selectsalary
Forminstructor
Wheresalary >= all (select salary
Frominstructor);
用聚集函式實現:
Select max(salary)
Frominstructor;
D. 找出工資最高的所有教師(可能有不止一位教師具有相同的工資)。
SelectID, name
Frominstructor
Wheresalary = (select max(salary)
Frominstructor);
E. 找出2009年秋季開設的每個課程段的選課人數。
原始答案:
Selectcourse_id, count(distinct ID)
Fromtakes natural join section
Wheresemester= ‘Fall’ and year = 2009
Group bycourse_id;
修改後的答案:
Selectcourse_id,sec_id, count(distinct ID)
Fromtakes natural join section
Wheresemester= ‘Fall’ and year = 2009
Group bycourse_id, sec_id;
添加了對於sec_id屬性的篩選,因為在2009年秋季同一個課程可能開設兩次,我們必須把課程段考慮在內。
F. 從2009年秋季開設的所有課程段中,找出最多的選課人數。
原始答案:
Withmax_student (course_id, sec_id,num) as
(Select course_id,sec_id, count (distinctID)
From takes natural join section
Where semester= ‘Fall’ and year =2009
Group by course_id, sec_id
)
Select max(num)
From max_student;
注意with……as……定義臨時關係的用法
使用另一種寫法
Select max (enrollment)
From (Select count (distinct ID) as enrollment
From takes natural join section
Where semester= ‘Fall’ and year =2009
Group by course_id, sec_id);
G. 找出2009年秋季擁有最多選課人數的課程段。
Withmax_student (course_id, sec_id,num) as
(Select course_id,sec_id, count (distinctID)
From takes natural join section
Where semester= ‘Fall’ and year =2009
Group by course_id, sec_id
)
Selectcourse_id, sec_id
Frommax_student
Where num = (select max(num)
From max_student);
3.2 假設給你一個關係grade_points(grade, points),它提供從takes關係中用字母表示的成績等級到數字表示的得分之間的轉換。例如,“A”等級可指定為對應於4分,“A-”對應於3.7分,“B+”對應於3.3分,“B”對應於3分,等等。學生在某門課程(課程段)上所獲得的等級分值被定義為該課程段的學分乘以該生得到的成績等級所對應的數字表示的得分。給定上述關係和我們的大學模式,用SQL寫出下面的每個查詢。為簡單起見,可以假設沒有任何takes元組在grade上取null值。
A.根據ID為12345的學生所選修的所有課程,找出該生所獲得的等級分值的總和。
Select sum(credits*points)
From takes natural joincourse natural join grade_points
Where ID = ‘12345’
為了防止該生什麼課都沒選,可以將上面答案改良成下面這種:
(Select sum(credits*points)
From takes natural join course natural join grade_points
Where ID = ‘12345’)
Union
(Select 0
From student
Where takes.ID = ‘12345’ and not exists (select*
From takes
Where takes.ID= ‘12345’));
注意並運算的使用以及檢測存在性的使用
B. 找出上述學生等級分值的平均值(GPA),即用等級分值的總和除以相關課程學分的總和。
Select (sum (credits*points))/ (sum(credits))
From takes natural joincourse natural join grade_points
Where ID = ‘12345’;
C. 找出每個學生的ID和等級分值的平均值。
Select ID, (sum(credits*points)) / (sum(credits))
From takes natural joincourse natural join grade_points
Group by ID;
3.3 使用大學模式,用SQL寫出如下插入、刪除和更新語句。
A.給Comp.Sci.系的每位教師漲10%的工資。
Updateinstructor
Setsalary = salary*1.1
Wheredept_name = ‘Comp.Sci.’;
B. 刪除所有未開設過(即沒有出現在section關係中)的課程。
Deletefrom course
Wherecourse.course_id not in (select course_id
Form section);
C.把每個在tot_cred屬性上取值超過100的學生作為同系的教師插入,工資為10000美元。
Insertinto instructor
SelectID, name, dept_name, 10000
Fromstudent
Wheretot_cred > 10000;
3.4 考慮圖3-18中的保險公司資料庫,其中加下劃線的是主碼。為這個關係資料庫構造出如下SQL查詢:
A.找出2009年其車輛出過交通事故的人員總數。
原始答案:
Selectcount (distinct driver_id)
From participatednatural join accident
Wheredate = 2009;
修改答案:
注意時間(date)並不代表年份(year),所以修改答案如下
Selectcount (distinct driver_id)
Fromparticipated natural join accident
Wheredate between date ‘2009-00-00’ and date ‘’2009-12-31
B.向資料庫中增加一個新的事故,對每個必須的屬性可以設定任意值。
這個題目可以先假定一些引數,完成資料庫增加操作後,所有的屬性均可以修改。
Insertinto accident
Values(4007, ‘2009-09-01’, ‘Berkeley’)
Insertinto Participated
Select 4007, c.license , P.driver_id, 3000
From (person as p) natural join (owns aso) natural join (car as c)
Where p.name =’Jones’;
C.刪除“John Smith”擁有的馬自達車(Mazda)。
Deletefrom car
Wheremodel = ‘Mazda’ and license in (select license
From owns natural join person
Where name = ‘John Smith’);
Person(driver_id,name,address)投保人 |
Car(license,model,year)投保車 |
Accident(report_number,date,location)事故資訊 |
Owns(driver_id,license)投保人與投保車關係 |
Participated(report_number,license,driver_id,damage_amount賠償金)事故詳情 |
3.5 假設有關係marks(ID, score),我們希望基於如下標準為學生評定等級:如果score<40得F;如果40≤score<60得C;如果60≤score<80得B;如果80≤score得A。寫出SQL查詢完成下列操作:
A.基於marks關係顯示每個學生的等級。
SelectID,
Case
When score<40 then ‘F’
When score<60 then ‘C’
When score<80 then ‘B’
Else ‘A’
End
Frommarks;
B.找出各等級的學生數。
With grades as (Select ID,
Case
When score<40 then ‘F’
When score<60 then ‘C’
When score<80 then ‘B’
Else‘A’
End as grade
From marks)
Selectgrade count (distinct ID)
Fromgrades
Group bygrade;
3.6 SQL的like運算子是大小寫敏感的,但字串上的lower()函式可用來實現大小寫不敏感的匹配。為了說明是怎麼用的,寫出這樣一個查詢:找出名稱中包含了“sci ”子串的系,忽略大小寫。
Selectdept_name
Fromdepartment
Where lower(dept_name) like ‘%sci%’;
3.7 考慮SQL查詢
Selectdistinct p.a1
Fromp,r1,r2
Wherep.a1=r1.a1 or p.a1=r2.a1
在什麼條件下這個查詢選擇的p.a1值要麼在r1中,要麼在r2中?仔細考察r1或r2可能為空的情況。
P, r1,r2均非空
3.8 考慮圖3-19中的銀行資料庫,其中加下劃線的是主碼。為這個關係資料庫構造出如下SQL查詢:
A. 找出銀行中所有有賬戶但無貸款的客戶。
原始答案
Selectcustomer_name
From depositor
Wherecustomer_name not in (select customer name
From borrower);
換一種寫法
(selectcustomer_name
Fromdepositor)
Except
(selectcustomer_name
Fromborrower)
注意Except差運算的使用方式
B. 找出與“Smith”居住在同一個城市、同一個街道的所有客戶的名字。
原始答案:
Selectcustomer_name
Fromcustomer
Where(customer_street,customer_city) = (select customer_street,customer_city
From customer
Where customer_name = ‘Smith’);
另一種寫法:
SelectF.customer_name
From(customer as F) join (customer as S) using(ustomer_street,customer_city)
WhereS.customer_name = ‘Smith’;
C. 找出所有支行的名稱,在這些支行中都有居住在“Harrison”的客戶所開設的賬戶。
Selectdistinct branch_name
Fromaccount natural join depositor natural join customer
Wherecustomer_city = ‘Harrison’;
Branch ( branch_name, branch_city , assets 資產)分行資訊 |
Customer (customer_name, customer_street, customer_city)客戶資訊 |
Loan (loan_number, branch_name, amount)貸款資訊 Borrower (customer_name, loan_number)貸款人資訊 Account(account_number, branch_name, balance)賬戶資訊 Depositor(customer_name, account_number)賬戶人資訊 |
圖3-19
3.9 考慮圖3-20的僱員資料庫,其中加下劃線的是主碼。為下面每個查詢寫出SQL表示式:
Employee(employee_name,street,city) Works(employee_name,company_name,salary) Company(company_name,city) Managers(employee_name,manager_name) |
圖3-20
A.找出所有為“First Bank Corporation”工作的僱員名字及其居住城市。
Select employee_name , city
From employee natural joinworks
Where company_name = ‘FirstBank Corporation’;
B. 找出所有為“First Bank Corporation”工作且薪金超過10000美元的僱員名字、居住街道和城市。
Select employee_name ,street,city
From employee natural joinworks
Where company_name = ‘FirstBank Corporation’ and salary >10000;
C. 找出資料庫中所有不為“First Bank Corporation”工作的僱員。
(Select employee_name
From employee)
Except
(select employee_name
From employee natural joinworks
Where company_name = ‘FirstBank Corporation’);
D.找出資料庫中工資高於“Small Bank Corporation”的每個僱員的所有僱員。
Select employee_name
From works
Where salary > (selectmax(salary)
From works
Where company_name = ‘Small Bank Corporation’
Group by company_name);
E. 假設一個公司可以在好幾個城市有分部。找出位於“Small Bank Corporation”所有所在城市的所有公司。
原始答案
Select company_name
Form Company
Where city in (select city
From company
Where company_name = ‘SmallBank Corporation’);
修改後答案
原始答案中最後生成的公司名稱會包含Small Bank Corporation,可能我們並不想包含Small Bank Corporation公司
(Select company_name
Form Company
Where city in (select city
From company
Where company_name = ‘SmallBank Corporation’))
Except
(select company_name
From company
Where company_name = ‘Small Bank Corporation’);
F. 找出僱員最多的公司。
With A as(selectcompany_name, count(employee_name) as num
From works
Group by company_name)
Select company_name, max(num)
From A;
另一種寫法
Select company_name
From works
Group by company_name
Having count (employee_name) >= all(selectcount(employee_name)
From works
Group by company_name);
G.找出平均工資高於“First Bank Corporation”平均工資的那些公司。
With A as (select company_name, avg(salary) as avg_salary
From works
Group by company_name)
Select company_name
From A
Where avg_salary > (select avg_salary
From A
Where company_name = ‘First Bank Corporation’);
3.10 考慮圖3-20的關係資料庫,給出下面每個查詢的SQL表示式:
A. 修改資料庫使“Jones”現在居住在“Newtown”市。
Update employee
Set city = ‘Newtown’
Where employee_name = ‘Jones’;
B. 為“FirstBank Corporation”所有工資不超過100000美元的經理增長10%的工資,對工資超過100000美元的只增長3%
Updata works as T
Set T.salary = t.salary *1.03
Where T.employee_name in (selectmanager_name
From manages)
And T.company_name = ‘FirstBank Corporation’
And t.salary >100000;
Updata works as T
Set T.salary = t.salary *1.1
Where T.employee_name in (selectmanager_name
From manages)
And T.company_name = ‘FirstBank Corporation’
And t.salary <=100000;
另一種寫法
Update works
Set salary= salary*(case
When salary>100000 then 1.03
Else 1.1)
Where employee_namein (select manager_name
Frome manager)
And company_name = ‘First BankCorporation’;