資料庫學習(二)
本次學習過程中依然有許多細節需要注意,也讓我複習到了許多已經忘卻的知識,現總結如下。
1.WHERE和HAVING
這個知識點本不應該成為阻礙,但是由於最近沒有細心學習sql,確實犯了一些小錯誤。總的來說,就是WHERE只能用於在表格中存在的欄位的篩選,而HAVING可以用於一些自定義的欄位。
比如,按照以下這段程式碼,是查詢不出任何結果的,而且還會報錯
select product_name,sale_price,purchase_price,(sale_price - purchase_price) as diffe from product where diffe >= 500;
而如果將WHERE改成HAVING,則可以達到目標。
select product_name,sale_price,purchase_price,(sale_price - purchase_price) as diffe
from product
having diffe >= 500;
2.三值邏輯與null
這其實算是sql中一個老生常談的問題,卻也是我一直把握不好的問題之一。現在我嘗試說明我對null的理解。
首先,我們必須明確一件事,即sql中的布林值並不只有true和false,它還有第三個值,即unknown,這種邏輯體系被稱為三值邏輯。
三值邏輯應該算是sql的特色之一,至少在我的程式設計過程中還沒有見到過另一種語言採用這種邏輯。既然要解釋三值邏輯,就順便提下大名鼎鼎的“四值邏輯”。即《SQL進階教程》中說的“兩種NULL”——“未知”(unknown)和“不適用”(not applicable)。
截止到目前,未知指的就是不知道,但是可以通過一定的條件知道,比如只要和我一起寫個圖書管理系統就一定能知道我會不會SQL;而不適用指的就是,不管怎麼樣都不可能,一篇部落格是不可能自己跳起來寫SQL的。
算上true和false,此處一共提到了四種邏輯,在資料庫的發展歷程中,也的確有很多人提出過四值邏輯。但是最終經過各種考慮,SQL採用了三值邏輯,即true,false和unknown
從現在開始,我們就可以引進另一個邏輯真值——unknown;必須理解的是,這裡的unknown與上文所說的未知並不是一回事,前者是一個邏輯真值,即真的存在的邏輯值,而後者是NULL中的一種情況,是一個邏輯上的概念。到此,我們一共有了三個邏輯值,true,false,unknown。
作為邏輯值,就一定會有與或非等邏輯運算,現盜圖如下:
對於這個邏輯來說,我們應該注意以下幾點:
- AND運算中,false > unknown > true
- OR運算中,true > unknwon >false
- NOT運算中,unknown的非還是unknown
接下來,我將結合SQL來說明三值邏輯。
我們應該瞭解的第一件事是,NULL不是一個值,即NULL不是一個邏輯真值,邏輯真值是unknown而不是NULL。這或許有點繞,但是我們依然要理解這個事實,即NULL本身不屬於任何型別,它僅僅是作為一個代號,就像老叮噹是我的外號,但是用老叮噹這個名字去公安局一定查不到身份證號一樣。所以在SQL中,無論是任何型別的值都不能直接與NULL做比較,因為這樣做的結果一定是unknown。而資料庫的查詢結果一定是判斷結果為true的行,邏輯為unknown的行是不可能返回的。所以,在資料庫中我們應該使用is null
,對於is null
的理解是,我們不要將其分開看,而應該將其看作一個整體。
到現在為止,我們應該有能力解決這三個問題。
而再深入一步,我們應該看到因為第三種邏輯unknown的引入,資料庫的世界會與我們真實世界的邏輯存在一定的不同,或者說,與我們通常習慣的邏輯存在一定差異。
比如,在我們的理解中,一件商品的價格一定大於等於5000或者小於5000,這幾乎可以三歲小孩都知道的道理,按照這種邏輯,我們是可以查出所有的商品的。即
select * from product
where purchase_price >= 5000 or purchase_price < 5000;
可以看出,這裡只出現了六件商品,而實際上應該是八件商品。其原因就在於unknown的存在,由於purchase_price
這一列中有兩個NULL值,而大於等於或者小於等邏輯值與其判斷後返回值都是unknown,所以這兩行無法返回。對於三值邏輯,程式碼應該如下:
select * from product
where purchase_price >= 5000 or purchase_price < 5000 or purchase_price is null;
其實對於SQL來說,unknwon帶來的邏輯混亂還有許多,所以在寫一些程式碼的時候,必須仔細推敲,進行檢查,才能達到自己的要求。
當然,三值邏輯和NULL背後的內容還有許多,遠遠比我所寫的複雜,如果以後有時間,一定要再寫一些關於這方面的內容。
3.關於NULL的排序
即使NULL是個符號,但是在排序的時候還會參與進來,真是個磨人的小妖精哈哈哈哈。
在MySQL中,NULL預設為最小值,即ASC排序時在最前面,DESC排序時在最後。
這就帶來了一個問題,如果我DESC的時候就是想讓NULL排在前面或者ASC的時候就是想讓NULL排在最後面怎麼辦?
首先,我們要看看最正常的情況是什麼樣子
select * from product
order by purchase_price;
可以看出由於NULL預設最小,它排在了purchase_price
的最前面,而如果想讓它去後面也很簡單,即通過“是否為NULL值”進行一次排序,程式碼如下
select * from product
order by isnull(purchase_price),purchase_price;
之所以可以這麼寫,是因為在程式世界中,True通常為0,False通常為1,即這兩個邏輯值之間也是可以排序的,這樣就可以達到對NULL的排序效果。
以下為本次打卡的練習題:
--2.1 編寫一條SQL語句,從product(商品)表中選取出“登記日期(regist在2009年4月28日之後”的商品,查詢結果要包含product name和regist_date兩列。
select product_name as "商品名",regist_date as "登記日期"
from product
where regist_date > '2009-04-28';
--2.2請說出對product 表執行如下3條SELECT語句時的返回結果。
SELECT *
FROM product
WHERE purchase_price = NULL;
SELECT *
FROM product
WHERE purchase_price <> NULL;
SELECT *
FROM product
WHERE product_name > NULL;
--由於與NULL進行比較,所以都是空集。
--2.3程式碼清單2-22(2-2節)中的SELECT語句能夠從product表中取出“銷售單價(saleprice)比進貨單價(purchase price)高出500日元以上”的商品。請寫出兩條可以得到相同結果的SELECT語句。
select product_name,sale_price,purchase_price
from product
where sale_price >= purchase_price + 500;
select product_name,sale_price,purchase_price,(sale_price - purchase_price) as diffe
from product
having diffe >= 500;
--2.4請寫出一條SELECT語句,從product表中選取出滿足“銷售單價打九折之後利潤高於100日元的辦公用品和廚房用具”條件的記錄。查詢結果要包括product_name列、product_type列以及銷售單價打九折之後的利潤(別名設定為profit)。
select product_name,product_type,(sale_price * 0.9 - purchase_price) as profit,'日元' as '單位'
from product
having profit > 100;
--2.5請指出下述SELECT語句中所有的語法錯誤。
SELECT product id,SUM(product name)
--本SELECT語句中存在錯誤。
FROM product
GROUP BY product_type
WHERE regist_date > '2009-09-01';
--(1)不知道沒有寫下劃線算不算語法錯誤哈哈哈哈
--(2)對字串進行SUM()沒有實際意義
--(3)GROUP BY字句應該在WHERE子句之後
--(4)封號沒有用半形符號
--(5)在SELECT子句中不應該使用單獨的product_id
--2.6請編寫一條SELECT語句,求出銷售單價(sale_price列)合計值是進貨單價(purchase prilce列)合計值1.5倍的商品種類。執行結果如下所示。
select product_type,sum(sale_price) as sale_sum,sum(purchase_price) as pur_sum
from product
group by product_type
having sale_sum > pur_sum * 1.5
order by sale_sum desc;
--2.7此前我們曾經使用SELECT語句選取出了product(商品)表中的全部記錄。當時我們使用了ORDERBY子句來指定排列順序,但現在已經無法記起當時如何指定的了。請根據下列執行結果,思考ORDERBY子句的內容。
select * from product
order by if(isnull(regist_date),1,0) desc,regist_date desc,sale_price asc;
本次習題質量非常高,十分感謝DataWhale團隊的成員!