怎么講,覺得SQL是一門 “一看就懂,一學(xué)就會(huì),一做就錯(cuò)”的語言. 這回是第…三…次SQL(上過兩次SQL的課,兩個(gè)月前看過《SQL》必知必會(huì),這回刷了leetcode和??途W(wǎng)上的SQL題)。 總的來說,的確不算很難得一門語言,不過從來不用SQL自然是邊學(xué)邊忘,看書也是一目十行,每次學(xué)的時(shí)候也沒怎么付出時(shí)間和精力,所以學(xué)的一直不好,但總歸是得應(yīng)付一下將要到來的面試的。
刷了差不多三四十道SQL了,還是有些題根本無從下手,通過翻書,看答案,最后總結(jié)出來為什么無從下手的原因是“課本例子太簡單,但是給的題目卻遠(yuǎn)遠(yuǎn)難于課本”。而且子查詢真是一個(gè)套一個(gè),這套一下那套一下,這join一下,那里笛卡爾積一下,再加上平時(shí)不寫,以及平時(shí)編程的習(xí)慣,導(dǎo)致寫了很多…….1. 無法通過編譯的SQL 2. 答案錯(cuò)誤的SQL。(每次先寫Select再寫FROM 子嵌套我真是傷腦筋…..)。
密集的刷了兩天之后,裝了個(gè)SQLiteStudio搗鼓了一通之后,坑都踩了一遍之后,簡單的SQL查詢已經(jīng)不會(huì)有太大的問題了。
無法通過編譯的SQL
group by + where
-
where + aggregation 函數(shù)
-- 同時(shí)不等號(hào)也是錯(cuò)的 WHERE s.salary!=max(s2.salary)) -
多個(gè)join 連用(只有l(wèi)eft join能連用)
-- 錯(cuò) SELECT DISTINCT e.last_name as last_name, e.first_name as first_name, dname.dept_name = dept_name; FROM (departments dname inner join dept_emp d on dname.dept_no = d.dept_no) right join employees e on e.emp_no = d.emp_no -
亂用之前提到的 where+ aggregation
-- 錯(cuò) SELECT emp_no, max(salary) FROM (SELECT e.emp_no as emp_no, s.salary as salary FROM salaries s inner join employees e , salaries s2 ON e.emp_no = s.emp_no WHERE s.salary!=max(s2.salary))--- 你就說這個(gè)query你自己看不看得懂吧...不要把簡單問題復(fù)雜化 SELECT emp_no, max(salary) FROM (SELECT e.emp_no as emp_no, s.salary as salary FROM salaries s inner join employees e , salaries s2 ON e.emp_no = s.emp_no) -
天真的覺得下一行的select會(huì)把最大salary的員工信息返回 ,但實(shí)際情況是 max(salary)只有一行,而select e.* 返回的只是員工的第一行,哈士奇狗頭….
SELECT e.* FROM( -- 天真的覺得下一行的select會(huì)把最大salary的員工信息返回 SELECT e.*,max(s.salary) FROM employees e INNER JOIN salaries s on e.emp_no=s.emp_no AND s.to_date='9999-01-01' WHERE s.salary NOT IN (SELECT max(salary) FROM salaries)) -
寫法混亂,多了不需要的東西
兩個(gè)d是怎么回事? 第二行的d根本沒用到啊,而且做了cartitian product后,e.emp_no多了好幾行
SELECT e.emp_no FROM employees e, dept_manager d WHERE e.emp_no NOT IN (SELECT e2.emp_no FROM dept_manager d inner join employees e2 on e2.emp_no=d.emp_no )正確寫法為
SELECT emp_no FROM employees WHERE emp_no NOT IN (SELECT e2.emp_no FROM dept_manager d inner join employees e2 on e2.emp_no=d.emp_no )
以為是錯(cuò)的其實(shí)是對(duì)的
-
可能寫算法寫魔怔了,總覺得SELECT max(*) 返回的是一個(gè)數(shù)組,即使只有一個(gè)數(shù)也應(yīng)該是數(shù)組,不能比較大小,得和返回值的第0個(gè)比較… 果然符合大一老師說的學(xué)語言時(shí)候容易出現(xiàn)的問題, 張冠李戴…..
-- 對(duì) where s.salary < (select max(salary) from salaries) -- 下面這個(gè)寫法...還真是對(duì)的,先記下,從sqllite的調(diào)試結(jié)果看應(yīng)該是對(duì)整個(gè)表排了序 SELECT s.*, d.dept_no,max(salary) FROM salaries s , dept_manager d-
-- 這個(gè)語句語法上不是錯(cuò)的,先執(zhí)行子查詢,返回salary,然后選emp_no,但是...其實(shí)他是錯(cuò)的,因?yàn)榭偸欠祷氐谝粋€(gè)emp_no SELECT emp_no,(SELECT salary FROM salaries) FROM employees;?
一些例題
題抄百遍,其義自見……
怎么講,上面犯的很多錯(cuò)誤,其實(shí)都是對(duì)DBMS的了解很淺顯造成的,比如right join 和right join相互嵌套(上課其實(shí)是講過為什么不能這么嵌套的原理的,可惜忘到一干二凈)
第二也是對(duì)group by 和join的原理不清楚,導(dǎo)致寫出臭臭長長的難以理解還錯(cuò)的SQL。 Inner join 是等值連接,outer join允許為空值,但其實(shí)都可以用cartitian的方式改寫,然后判斷條件寫在where 里,inner join的方式必須強(qiáng)制寫 on條件,這樣不會(huì)忘記。
補(bǔ)充關(guān)于子查詢的一些定義
在WHERE子句中使用子查詢能夠編寫出功能很強(qiáng)并且很靈活的 SQL語句。對(duì)于能嵌套的子查詢的數(shù)目沒有限制,不過在實(shí)際使用時(shí)由于 性能的限制,不能嵌套太多的子查詢。
在WHERE子句中使用子查詢(如這里所示),應(yīng)該保證SELECT語句具有與WHERE子句中相同數(shù)目的列。通常,子查詢將返回單個(gè)列并且與單個(gè)列匹配,但如果需要也可以使用多個(gè)列。
雖然子查詢一般與IN操作符結(jié)合使用,但也可以用于測(cè)試等于(=)、 不等于(<>)等。
-- orders 是一個(gè)計(jì)算字段,由圓括號(hào)的子查詢簡歷的,對(duì)每個(gè)客戶執(zhí)行一次,執(zhí)行5次,因?yàn)橛形鍌€(gè)客戶。 這個(gè)子查詢,涉及外部查詢的子查詢,叫做相關(guān)子查詢。任何時(shí)候列名有多義性,就必須用這種。 SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders WHERE customers.cust_id = orders.cust_id) AS orders FROM customers Order BY cust_name; -- 兩個(gè)SQL的區(qū)別,下面這個(gè)沒有限定表名,那么orders 的cust_id一直在和自身比較,所以總是返回訂單總數(shù) SELECT cust_name, cust_state, (SELECT COUNT(*) FROM orders WHERE cust_id = cust_id) AS orders FROM customers Order BY cust_name-
選出部門中工資最多的
-- 牛客網(wǎng)上一直過不了,不知道為啥,但我覺得??途W(wǎng)答案錯(cuò)了 SELECT DISTINCT d1.dept_no,d1.emp_no,s1.salary FROM dept_emp d1, salaries s1 WHERE d1.emp_no = s1.emp_no and s1.salary >= (SELECT max(s.salary) FROM salaries s ,dept_emp d WHERE s.emp_no =d.emp_no and d.dept_no = d1.dept_no and s.to_date = '9999-01-01') AND s1.to_date = '9999-01-01 -
選出工資比部門經(jīng)理多的
-- 錯(cuò)誤解法,先將人和經(jīng)理對(duì)應(yīng)起來,然后再對(duì)應(yīng)工資,選出最大。 -- 分析為什么為錯(cuò)呢? 1. 這是一個(gè)四重的連接,包括s1和s2的自聯(lián)結(jié) -- 經(jīng)過一步步調(diào)試,原來是忘了加上 s_todate='9999-01-01'的日期限定條件,所以做SQL一定要細(xì)心啊.... SELECT * FROM ( SELECT DISTINCT d.emp_no AS emp_no, ma.emp_no AS manager_no, s.salary AS emp_salary, s2.salary AS manager_salary FROM dept_emp d INNER JOIN dept_manager ma ON d.dept_no = ma.dept_no, salaries s , salaries s2 WHERE s2.emp_no = ma.emp_no AND s.emp_no = d.emp_no and d.dept_no = ma.dept_no ) WHERE emp_salary>manager_salary;?
補(bǔ)充聯(lián)結(jié)
# 自聯(lián)結(jié)
SELECT p1.prod_id, p1.prod_name
FROM products AS p1, products AS p2
WHERE p1.vend_id = p2.vend_id AND p2.prod_id = 'DTNTR';
# 和這個(gè)子查詢是一樣的,單選用自聯(lián)結(jié)快于子查詢
# question2: 里面的= 和 in 會(huì)有什么差別呢?
SELECT prod_id, prod_name
FROM products
# 注釋: 一個(gè)產(chǎn)品只有一個(gè)vend_id,所以這里用= 比較而沒有用 IN 是可以的
WHERE vend_id = (SELECT vend_id
FROM products
WHERE prod_id='DTNTR')
解答question 2
等號(hào)是用來查找與單個(gè)值匹配的所有數(shù)據(jù);IN 是 用來查找與多個(gè)值匹配的所有數(shù)據(jù);而 LIKE用來查找與一個(gè)模式匹配的所有數(shù)據(jù)。等號(hào) 確切知道所要查找的內(nèi)容,且為單一值時(shí),可以使用等號(hào)運(yùn)算符來進(jìn)行數(shù)據(jù)比較。等號(hào)運(yùn)算符中可以使用字符串、日期或數(shù)字。IN當(dāng)確切知道所要查找的內(nèi)容,且為多個(gè)值時(shí),可以使用 IN 子句來進(jìn)行數(shù)據(jù)比較。IN 子句中也可以使用數(shù)字、字符串或日期。
-- 選出不是經(jīng)理
SELECT DISTINCT e.emp_no
FROM employees e, dept_manager d
WHERE e.emp_no NOT IN (SELECT e2.emp_no FROM dept_manager d inner join employees e2 on e2.emp_no=d.emp_no )
ORDER BY e.emp_no ASC;
-- <>, 錯(cuò)誤寫法
SELECT DISTINCT e.emp_no
FROM employees e
WHERE e.emp_no <> (SELECT e2.emp_no FROM dept_manager d inner join employees e2 on e2.emp_no=d.emp_no )
ORDER BY e.emp_no ASC;
下面一些query的結(jié)果可以解釋
SELECT emp_no FROM employees

SELECT e2.emp_no FROM employees e2 inner join dept_manager d on e2.emp_no=d.emp_no

SELECT emp_no, emp_no = (SELECT e2.emp_no FROM employees e2 inner join dept_manager d on e2.emp_no=d.emp_no) AS a, (SELECT e2.emp_no FROM employees e2 inner join dept_manager d on e2.emp_no=d.emp_no) as e2_emp_no
FROM employees ;

最大第二大前K大的問題
基本思路有 排序+limit, count() +where (大于xxx的個(gè)數(shù)/小于xx的個(gè)數(shù)), max+(去掉最大的子集)
-- 尋找入職第三晚的員工, 此為不去重的寫法
SELECT * FROM employees ORDER BY hire_date DESC LIMIT 2,1;
-- 尋找最晚入職的三個(gè)員工, 此為去重的寫法,應(yīng)該根據(jù)題意確定是否要用distinct
SELECT *
FROM employees
WHERE hire_date > (SELECT DISTINCT hire_date
FROM employees
ORDER BY hire_date ASC
LIMIT 2,1)
SELECT *
FROM employees e1
WHERE 2=(SELECT COUNT(*)
FROM employees e2
WHERE e1.hire_date < e2.hire_date);
-- 尋找部門工資數(shù)目上前三高的,所以加了DISTINCT,
-- 不能用GROUP BY + LIMIT
SELECT d.name AS department, e.name AS employee, e.salary AS salary
FROM employee e INNER JOIN Department d on d.id = e.departmentID
WHERE (SELECT COUNT(DISTINCT e2.salary) FROM employee e2 WHERE e2.salary>e.salary AND e2.departmentId = e.departmentId) <=2
ORDER BY department ASC, salary DESC
補(bǔ)充COUNT()函數(shù),使用COUNT(*)對(duì)表中行的數(shù)目進(jìn)行計(jì)數(shù),不管表列中包含的是空 值(NULL)還是非空值。使用 COUNT(column) 對(duì)特定列中具有值的行進(jìn)行計(jì)數(shù), 忽略NULL值。
補(bǔ)充 HAVING,HAVING和WHERE的差別這里有另一種理解方法,WHERE在數(shù)據(jù)分組前進(jìn)行過濾,HAVING在數(shù)據(jù)分組后進(jìn)行過濾。這是一個(gè)重要的區(qū)別,WHERE排除的行不包括在分組中。這可能會(huì)改變計(jì)算值,從而影響HAVING子句中基于這些值過濾掉的分組。
補(bǔ)充 Set Comparison >some, >all , 并不是都支持(雖然學(xué)的課本是支持的,但最近在sqlstudio中用這個(gè)不支持)
-
補(bǔ)充if函數(shù)
if(t.status! = "completed", t.status, NULL) -
補(bǔ)充 if…else
SELECT OrderID, Quantity, CASE WHEN Quantity > 30 THEN "The quantity is greater than 30" WHEN Quantity = 30 THEN "The quantity is 30" ELSE "The quantity is under 30" END AS QuantityText FROM OrderDetails;?
總結(jié)
- 第三次SQL的學(xué)習(xí)時(shí)間大概為兩天,整個(gè)學(xué)習(xí)效果只能說 比之前好吧,總結(jié)的經(jīng)驗(yàn)是,一定要一層一層剝,從最小的SQL開始寫,然后一步步疊加,不要亂寫,也不要錯(cuò)寫,也不要冗余,尤其注重條件是否漏了
- 目前遇到的比較難的題目是,求分組里面前K大的,注意重復(fù)值要不要處理。
- 分清楚 = 和 IN, 兩個(gè)的概念是不一樣的
- 有個(gè)很奇怪的問題還在困擾我,sql的子查詢里面的別名怎么沒有 編程里的作用域呢 ??,真是強(qiáng)行一個(gè)概念套一個(gè)概念啊,張冠李戴,有時(shí)間還是去看看吧。
- 如果真有人看到了這,唯一的提醒是,牛客網(wǎng)給了一些SQL的例題,但是那里面有些判題的OJ是錯(cuò)的,而且題目要求的也很模糊,LeetCode上的全部自己驗(yàn)證過了,都是對(duì)的。 以及自己電腦上可以裝一個(gè)SQLiteStudio.
有時(shí)間還需要提升的地方
- DBMS是如何處理 MAX()之類的計(jì)算,是整個(gè)表排序之后選第一個(gè)么? 還是說那種類似于編程的選擇排序?
- DBMS怎么處理GROUP BY的,也是整個(gè)表排序么? 還是只把group by值相同的放一起,然后內(nèi)部無序? 如果要求每組里面最大的可不可以用DESC關(guān)鍵字之后選第一行呢?
- 字符串的各種處理函數(shù)如正則還沒有學(xué)
- 常用的函數(shù)還沒有學(xué)
- DBMS的JDBC之類的還沒有接觸,以及關(guān)系型數(shù)據(jù)庫的設(shè)計(jì)要點(diǎn),各種范式
- 還需要學(xué)一下function怎么寫
- DBMS architecture 和query optimization