關(guān)系型數(shù)據(jù)庫和SQL
- SQL語言的三個部分
- DML:Data Manipulation Language,數(shù)據(jù)操縱語言,檢索、修改、增加、刪除數(shù)據(jù)庫(表)中的數(shù)據(jù)
- DDL:Data Definition Language,數(shù)據(jù)定義語言,創(chuàng)建和修改數(shù)據(jù)庫(表)本身
- DCL:Data Control Language,維護數(shù)據(jù)庫安全
- 關(guān)系型數(shù)據(jù)庫基本術(shù)語
- relational(關(guān)系):表示各表彼此關(guān)聯(lián)
- record(記錄):表中的行(row)
- field(字段):表中的列(column)
- primary key:表的主鍵,通常為自增型(auto-increment),本身沒有特殊含義,只用于保證每一行都有一個唯一的值
- foreign key:表的外鍵,確保這一列有一個有效的值,通常會把某個其他表的共同列,通常是主鍵作為外鍵,比如訂單表中的客戶列
- column(field)的數(shù)據(jù)類型(更詳細的數(shù)據(jù)類型介紹參見SQL數(shù)據(jù)類型)
- 數(shù)字
- bit:位,只允許0和1
- integer:整數(shù),不含小數(shù)位
- decimal:浮點數(shù),含小數(shù)位
- real number:實數(shù)
- 字符:string/character string類型,在SQL語句中需要用引號括起來
- 日期/時間:用于表示日期和時間,在SQL語句中需要用引號括起來,允許對所涉及的日期進行特殊運算
- NULL:空值,非數(shù)據(jù)類型,而是在允許包含空值的列中表示空置
基本數(shù)據(jù)檢索
- SQL語句不區(qū)分大小寫,且可以寫成任意多行
- 可以把重要關(guān)鍵字作為單獨一行凸顯語義
- 從表中選擇所有:
SELECT * FROM tablename - 指定列:
SELECT columnname FROM tablename - 指定多個列:
SELECT column1, column2 FROM tablename -
帶空格的列名:Microsoft SQL Server使用方括號
[],MySQL使用重音符`,Oracle使用雙引號
計算字段和別名
- 計算字段
- 直接量:與表中數(shù)據(jù)沒有任何關(guān)系的值叫做literal value(直接量),字符串直接量需要加引號,數(shù)字直接量不需要
- 算數(shù)運算:允許使用列數(shù)據(jù)與直接量或其它列數(shù)據(jù)進行加減乘除運算,比如
SELECT QuantityPurchased * PricePerItem FROM Orders - 連接字段:把字符數(shù)據(jù)連接或組合到一起,Microsoft SQL Server使用加號
+,MySQL使用CONCAT()函數(shù)連接字符串,Oracle使用雙豎線||
- 別名
- 列的別名:用于修改列(表頭)標題或為計算字段提供列(表頭)標題,比如
SELECT f_n AS 'first_name' from customers - 表的別名:通常有三種情況需要修改表名稱,a.不好理解或復(fù)雜的表名,b.從多個表中進行選擇,c.使用子查詢;
SELECE something from someshittablename AS another_table_name
- 列的別名:用于修改列(表頭)標題或為計算字段提供列(表頭)標題,比如
使用函數(shù)
- Scalar function:標量函數(shù),針對單行中的數(shù)據(jù)執(zhí)行
- Aggregate function:聚合函數(shù),針對較大的數(shù)據(jù)集合進行操作
- 字符函數(shù)
-
LEFT/RIGHT (string, numberOfCharactors):從左/右取字符串數(shù)據(jù)的指定位數(shù),在Oracle中以SUBSTR替代 -
SUBSTRING (string, start, end):取得字符串數(shù)據(jù)的子字符串,在Oracle中以SUBSTR替代 -
LTRIM/RTRIM (string):刪除字符串數(shù)據(jù)左側(cè)/右側(cè)的空格 -
CONCAT (string1, string2, string3 ...):拼接多個字符串,Oracle中只允許拼接兩個字符串 -
UPPER/LOWER (string):返回字符串的大/小寫
-
- 復(fù)合函數(shù):函數(shù)的嵌套使用被稱為復(fù)合函數(shù),比如
RIGHT(RTRIM(something)) AS 'something' - 日期時間函數(shù)
-
GETDATE/NOW/CURRENT_DATE ():三個函數(shù)都用于獲取當前時間,對應(yīng)Microsoft SQL Server/MySql/Oracle三家數(shù)據(jù)庫的實現(xiàn) -
DATEPART (date_part, date_value):單獨返回某個部分的時間,date_part為需要返回的時間部分,date_value為原始時間,MySQL的實現(xiàn)為DATE_FORMAT(date_value, date_format),date_value為原始時間,date_format為類似于%d這樣的格式用于告訴函數(shù)需要返回哪部分時間,date_part的有效值為:year/quarter/month/dayofyear/day/month/hour/minute/second -
DATEDIFF (date_part, start_date, end_date):用于計算任意兩個不同日期間相差的時間,在MySQL中該函數(shù)之允許計算天數(shù)差異,所以date_part參數(shù)不可用,僅需要傳入兩個日期即可
-
- 數(shù)值函數(shù)(數(shù)學(xué)函數(shù))
-
ROUND (NumbericValue, DecimalPlaces):對任意數(shù)進行四舍五入,NumbericValue表示要四舍五入的數(shù),DecimalPlaces表示從第幾位開始四舍五入(即需要保留到第幾位),以十分位為0,向左為負數(shù),向右為正數(shù) -
RAND ([seed]):產(chǎn)生一個隨機數(shù) ,可選的seed參數(shù)為一個整數(shù),用于每次返回相同的值 -
PI ():返回數(shù)學(xué)運算中的pi值
-
- 轉(zhuǎn)換函數(shù)
-
CAST (expression AS data_type):將數(shù)據(jù)從一種類型轉(zhuǎn)換為另一種類型,expression表示數(shù)據(jù)或函數(shù)表達式,data_type表示需要轉(zhuǎn)換到的數(shù)據(jù)類型,一般情況下SQL在做計算時會進行自動的類型轉(zhuǎn)換,所以很少用到這個函數(shù),它的典型使用場景是當一個日期被保存成了字符串,需要轉(zhuǎn)換為真正的日期數(shù)據(jù):CAST('2017-5-1', AS DATETIME),Oracle中該函數(shù)的參數(shù)形式會不一樣 -
ISNULL/IFNULL/NVL (column_data_maybe_null, if_null_will_use_this_data ):將NULL值轉(zhuǎn)換為一個有意義的值,對應(yīng)Microsoft SQL Server/MySql/Oracle三家數(shù)據(jù)庫的實現(xiàn)
-
排序數(shù)據(jù)
排序數(shù)據(jù)的語法如下:
SELECT
column1,
column2
FROM table1, table2
ORDER BY column3, column2 DESC
-
ORDER BY句子總是在FROM子句之后,FROM子句總是在SELECT關(guān)鍵字之后 -
SELECT和ORDER BY后面指定的列,可能是完全不同的一些列 - 使用關(guān)鍵字
ASC和DESC來升序/降序排列 -
ORDER BY后指定了多列,則首先按照第一列排序,如遇相同則相同的行按第二列排序,以此類推 - 根據(jù)計算字段排序時,如果計算字段已經(jīng)出現(xiàn)在
SELECT關(guān)鍵字后,則只需要在ORDER BY子句中指定計算字段的別名即可(經(jīng)測試在MySQL中如果計算字段別名帶有空格則暫時無法在此引用,因為不能使用引號),如果計算字段沒有出現(xiàn)在SELECT關(guān)鍵字后,則可直接在ORDER BY子句中指定一個計算字段,例如:SELECT title, rental_duration, rental_rate FROM film ORDER BY rental_duration * rental_rate DESC - 按升序排列時,大部分的SQL數(shù)據(jù)庫都會是按
NULL(Oracle中排在最后,可使用NULLS FIRST關(guān)鍵字來強制最先)-數(shù)字-字符(字符中首先顯示數(shù)字字符,再顯示普通字符,除Oracle外不區(qū)分大小寫)來進行排序,反之亦然。
基于列的邏輯
- 基于列的邏輯主要用于根據(jù)邏輯條件改變想要展現(xiàn)給用戶的輸出
- 簡單格式:判斷某列的值為某個具體值后將其轉(zhuǎn)換為一個其它值
SELECT column1, column2
CASE column3
WHEN value1 THEN result1
WHEN value2 THEN value2
(repeat WHEN-THEN any number of times)
[ELSE defaul_result]
END
column4
FROM tablename
- 查詢格式:判斷一列或多列中的某個值是否符合某個條件而將其轉(zhuǎn)換為一個其它值并顯示在一列中
SELECT
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
(repeat WHEN-THEN any number of times)
[ELSE defaul_result]
END AS custom_column_name,
FROM tablename
# 最新版的MySQL語法與書中的語法有細微差別:
# ELSE子句最后不需要逗號
SELECT
title,
CASE
WHEN rental_duration = 3 THEN 'Three Day Left'
WHEN rental_rate = 0.99 THEN 'Cheapest'
ELSE 'Normal'
END AS 'Rental Status'
FROM film
基于行的邏輯
- 基于行的邏輯主要用于獲取滿足特定條件的數(shù)據(jù)
- 應(yīng)用查詢條件
- SQL中的查詢條件從
WHERE子句開始 -
WHERE子句總是在FROM和ORDER BY子句之間,實際上任何“子句”都必須按照這個順序來SELECT columnList FROM tableList WHERE condition ORDER BY columnList
- SQL中的查詢條件從
-
WHERE子句操作符,以下這些操作符都可以在基于列的邏輯CASE WHEN condition語句中使用- 等于:
= - 不等于:
<> - 大于:
> - 小于:
< - 大于等于:
>= - 小于等于:
<=
SELECT first_name, last_name FROM actor WHERE age > 18 - 等于:
- 限制行
- 使用
TOP/LIMIT/ROWNUM(對應(yīng)Microsoft SQL Server、MySQL和Oracle)限制行數(shù)(關(guān)鍵字TOP返回的行,并不是真的隨機樣本,而是根據(jù)數(shù)據(jù)庫中的物理存儲方式限定了前幾行數(shù)據(jù)而已)# Microsoft SQL Server SELECT TOP number columnList FROM table # MySQL SELECT columnList FROM table LIMIT number # Oracle SELECT columnList FROM table WHERE ROWNUM <= number - 結(jié)合
SORT BY子句做“Top N”查詢(基于特定分類,得到帶有最大/小值的一定數(shù)量的行)# 本月賣得最好的莎士比亞的三本書 # MySQL SELECT title AS 'Book Title', current_month_sale AS 'Quantity Sold' FROM books WHERE author = 'Shakespear' LIMIT 3 ORDER BY current_month_sale DESC # Oracle中的TOP N查詢需要用到子查詢,后文會細講 SELECT * FROM (SELECT title AS 'Book Title', current_month_sale AS 'Quantity Sold' FROM books ORDER BY current_month_sale) WHERE ROWNUM <= 3
- 使用
布爾邏輯(更復(fù)雜的基于行的邏輯)
- 使用與
AND、或OR、非NOT三個關(guān)鍵字在WHERE子句中表示布爾邏輯。與其它語言的計算順序一樣,AND的優(yōu)先級最高,OR其次,NOT優(yōu)先級最低,也可以使用()來改變?nèi)叩挠嬎沩樞?# 這個例子僅為展示布爾邏輯,實際使用不應(yīng)該這么繞 SELECT first_name, last_name, age FROM actors WHERE NOT( (age < 18 OR age > 60) AND last_name = 'Jhon' ) - 還有兩個表示范圍的操作符
BETWEEN和IN,用于替代column >= range_bottom AND column <= range_top和column = value1 OR column = value2這樣的特例,簡化SQL語句的編寫# BETWEEN,等價于 age >= 18 AND age <= 60 SELECT first_name, last_name, age FROM actors WHERE age BETWEEN 18 AND 60 # IN,等價于 state = 'IL' AND state = 'NY' SELECT customer_name, state FROM orders WHERE state IN ('IL', 'NY') - 使用
IS NULL在WHERE子句中判斷一個列是否為空,也可以與函數(shù)ISNULL(column, value)結(jié)合使用# 選取重量為0或者沒有填寫重量的產(chǎn)品 SELECT product_description, weight FROM products WHERE weight = 0 OR weight IS NULL # 使用ISNULL等價的寫法 SELECE product_description, weight FROM products WHERE ISNULL(weight, 0) = 0 # IS NULL和ISNULL SELECT product_description, ISNULL(weight, 0) AS 'weight' FROM products WHERE weight = 0 OR weight IS NULL
模糊匹配
-
WHERE子句可以使用LIKE操作符來查找針對列值的某部分匹配- 包含某關(guān)鍵字:
SELECT title FROM film WHERE title LIKE '%love%' - 以某關(guān)鍵字開頭:
SELECT title FROM film WHERE title LIKE 'love%' - 以某關(guān)鍵字結(jié)尾
SELECT title FROM film WHERE title LIKE '%love' - 包含某關(guān)鍵字但不以其開頭也不以其結(jié)尾(未能在MySQL 4中驗證,只驗證通過了單獨的不以某字符串開頭,或者不以某字符串結(jié)尾兩種情況)
SELECT title FROM film WHERE title LIKE '% love %'
- 包含某關(guān)鍵字:
-
通配符符號 含義 % 任意個任意字符 _ 一個任意字符 [characterlist] 一個指定字符列表中的字符(在MySQL和Oracle中沒有) [^charactorlist] 一個非指定字符列表中的字符(在MySQL和Oracle中沒有) 符號 含義 % 任意個任意字符 _ 一個任意字符 [characterlist] 一個指定字符列表中的字符(在MySQL和Oracle中沒有) [^charactorlist] 一個非指定字符列表中的字符(在MySQL和Oracle中沒有) -
NOT操作符可以和LIKE操作符組合使用,例如SELECT first_name, last_name FROM actor WHERE first_name LIKE '%ARY%' AND last_name NOT LIKE '[MG]ARY' - 按照讀音匹配(不常用也不好用)
-
SOUNDEX函數(shù):能夠輸出一個表示字符讀音的四字代碼(以首字母開頭,然后刪去剩余字符中所有元音和字母y,最后轉(zhuǎn)換為一個三位數(shù)的數(shù)字用于表示讀音,最后輸出類似S530) -
DIFFERENCE函數(shù):可以和SOUNDEX函數(shù)一起使用(僅Microsoft SQL Server支持),檢查兩個字符的SOUNDEX相似度并返回一個表示讀音相近度的數(shù)字,(兩個字符的SOUNDEX值共有四個字符,每有一個位置的字符相等,則結(jié)果自增1,所以DIFFERENCE函數(shù)的返回值只有0到4五個可能的數(shù)字,越大越相近,越小越不同
-
匯總數(shù)據(jù)
- 消除重復(fù):使用
DISTINCT關(guān)鍵字來刪除輸出重復(fù)的行# 查看所有藝術(shù)家(沒有顯示相同藝術(shù)家的行) SELECT DISTINCT artist FROM songs ORDER BY artist # 查看所有藝術(shù)家和專輯的唯一組合(沒有顯示同一藝術(shù)家和同一專輯的行,每一行中藝術(shù)家和專輯的組合是唯一的) SELECT DISTINCT artist, album FROM songs ORDER BY artist, album -
聚合函數(shù):標量函數(shù)只能針對單個的數(shù)字或值進行計算,而聚合函數(shù)則可以用于分組數(shù)據(jù)函數(shù) 解釋 SUM合計、加總 AVG平均值 MIN最小值 MAX最大值 COUNT數(shù)量 函數(shù) 解釋 SUM合計、加總 AVG平均值 MIN最小值 MAX最大值 COUNT數(shù)量 # 總值、均值、最大值、最小值 SELECT SUN(fee) AS 'Total Gym Fees' AVG(grade) AS 'Average Quiz Score' MIN(grade) AS 'Minimum Quiz Score' MAX(grade) AS 'Maximum Quiz Score' FROM grades WHERE grade_type = 'quiz' # 返回所有選中行的數(shù)目 SELECT COUNT(*) AS 'Count of Homework Rows' FROM grades WHERE grade_type = 'homework' # 返回指定列中存在值的行的數(shù)目 SELECT COUNT(grade) AS 'Count of Homework Rows' FROM grades WHERE grade_type = 'homework' # 與DISTINCT配合返回指定列中唯一值的行數(shù) SELECT COUNT(DISTINCT fee_type) AS 'Number of Fee Types' FROM Fees - 分組數(shù)據(jù):以指定列為依據(jù)對所有選中行進行分組,重新劃分了行的顯示規(guī)則
- 單列分組
# 統(tǒng)計每個分級下的電影數(shù)量 SELECT rating, COUNT(rating) AS 'Rating Count' FROM film GROUP BY rating - columnlist中的所有列,要么是
GROUP BY子句中的列,要么是在聚合函數(shù)中使用的列,因為所有內(nèi)容都在組中出現(xiàn),不在組中的內(nèi)容沒有辦法處理,這種情況下MySQL與其它兩種數(shù)據(jù)庫不同,它只會得出錯誤的結(jié)果,而不會報錯 - 多列分組:組的概念可以擴展,從而根據(jù)多列進行分組
# 統(tǒng)計不同租金率下的不同分級電影的數(shù)量 SELECT rating, rental_rate, COUNT(rating) AS 'Rating Count' FROM film GROUP BY rating, rental_rate - 在沒有分組的情況下,聚合函數(shù)(
SUM、AVG、MIN、MAX、COUNT)統(tǒng)計的是所有行的數(shù)據(jù),在有分組的情況下,這些聚合函數(shù)則僅會統(tǒng)計組內(nèi)的數(shù)據(jù),當然實際上也是最終顯示的表的每一行的聚合 -
GROUP BY子句中的columnlist順序沒有意義,但ORDER BY子句中的順序是有意義的,一般按照排序的優(yōu)先順序來列出這些列會很有幫助(也即SELECT中的columnlist與ORDER BY中的columnlist保持一致)
- 單列分組
- 基于分組應(yīng)用查詢條件:
WHERE子句中的查詢條件是針對單獨的行來應(yīng)用的,如果存在GROUP BY分組,就需要使用HAVING關(guān)鍵字了# 查看分級中所有電影平均時長大于100分鐘的分級中電影的數(shù)量 SELECT rating AS '分級', COUNT(title) AS '電影數(shù)量', AVG(length) AS '平均時長' FROM film GROUP BY rating HAVING AVG(length) > 100 ORDER BY 電影數(shù)量 DESC - 至此,
SELECT語句的一般格式如下:SELECT columnlist FROM tablelist WHERE condition GROUP BY columnlist HAVING condition ORDER BY COLUMNLIST
用內(nèi)連接來組合表
- 關(guān)系型數(shù)據(jù)庫最重要的成就是能夠把數(shù)據(jù)組織到任意多個相互關(guān)聯(lián)的表中,但同時這些又是彼此獨立的;人們可以分析業(yè)務(wù)實體然后進行適當?shù)臄?shù)據(jù)庫設(shè)計,這樣就可以具有最大的靈活性;關(guān)系型數(shù)據(jù)庫可以以任何你想要的方式把代表業(yè)務(wù)實體的表連接到一起,從而實現(xiàn)“關(guān)系”
-
類似“客戶”和“訂單”這樣兩個獨立的實體信息,至少應(yīng)該要拆分到兩個表中(訂單表很有可能需要繼續(xù)拆分成多個表),可以使用實體關(guān)系圖(entity-relationship diagram)來表示可視化地表示兩個表以及他們之間存在的隱性關(guān)系,實體(entity)指的是表,關(guān)系(relationship)指的是這些表中數(shù)據(jù)元素之間所畫的線
實體關(guān)系圖 - 內(nèi)連接
- 使用關(guān)鍵字
INNER JOIN來指定想要連接的第二個表,使用ON來指定兩個表的共同列由于共同列名稱是一樣的,所以需要在列名前面使用表名作為命名空間來區(qū)分兩個表中獨立的列# 簡單地依據(jù)customer_id將顧客表和訂單表拼接到一個表中 SELECT * FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id - 內(nèi)連接只會返回關(guān)聯(lián)的兩個表之間相匹配的數(shù)據(jù),表在
FROM和INNER JOIN之間的順序僅會影響哪個表的列先顯示,不會影響行的順序 - SQL不是過程式語言,不會指定任務(wù)的先后順序,而只是指定需要的邏輯并讓數(shù)據(jù)庫內(nèi)部機制去決定如何執(zhí)行任務(wù)。
- 僅使用
FROM和WHERE也可以指定表的內(nèi)連接,這是內(nèi)連接的另一種范式,但因其沒有顯示地表示出連接的邏輯,所以不推薦使用(所以其實INNER JOIN ON的唯一作用僅僅是表達語義而已)SELECT * FROM customers, orders WHERE customers.customer_id = orders.customer_id - 可以通過顯式地指定表的別名和列的別名(注意Oracle中表的別名與其他兩個庫的區(qū)別,前文有提及),來去除內(nèi)連接后的重復(fù)列或者只顯示需要的列,這是推薦的做法:
SELECT c.customer_id AS 'Customer Id', c.first_name AS 'First Name', c.last_name AS 'Last Name', o.order_id AS 'Order Id', o.quantity AS 'Quantity', o.price_per_item AS 'Price' FROM customers AS 'c', INNER JOIN
- 使用關(guān)鍵字
用外連接來組合表
- SQL中表連接的默認類型就是內(nèi)連接,所以可以只使用
JOIN來指定一個內(nèi)連接 - 外連接有三種類型:左連接
LEFT OUTER JOIN,右連接RIGHT OUTER JOIN,全連接FULL OUTER JOIN,其中關(guān)鍵字OUTER并不是必須的。 -
所以總結(jié)起來有4種類型的連接:連接類型 全稱 簡寫 用途 內(nèi)連接 INNER JOINJOIN兩個表都是主表(primary table),共同列中所有的行都必須同時在這兩個表中才會被選中 左連接 LEFT OUTER JOINLEFT JOIN左表為主表,右表為從表(secondary table),選中共同列中所有在主表中的行,不管它是否出現(xiàn)在從表 右連接 RIGHT OUTER JOINRIGHT JOIN左表為從表,右表為主表,規(guī)則同左連接 全連接 FULL OUTER JOINFULL JOIN兩個表都是從表,共同列中的行只要出現(xiàn)在任意一個表中都會被選中 連接類型 全稱 簡寫 用途 內(nèi)連接 INNER JOINJOIN兩個表都是主表(primary table),共同列中所有的行都必須同時在這兩個表中才會被選中 左連接 LEFT OUTER JOINLEFT JOIN左表為主表,右表為從表(secondary table),選中共同列中所有在主表中的行,不管它是否出現(xiàn)在從表 右連接 RIGHT OUTER JOINRIGHT JOIN左表為從表,右表為主表,規(guī)則同左連接 全連接 FULL OUTER JOINFULL JOIN兩個表都是從表,共同列中的行只要出現(xiàn)在任意一個表中都會被選中 - 在實體關(guān)系圖中,單向箭頭表示表之間的連接是單向的,箭頭終點的表中有一列所有行都能在箭頭起點的表中找到,但反過來則不一定,比如,不是所有的客戶都有訂單,且一個客戶可能有多個訂單,但所有的訂單都會有客戶信息(甚至可以說所有的訂單有且只有一個客戶信息),退貨信息與訂單的關(guān)系類似
- 當連接主表和從表時,我們需要主表中所有的行,即使在從表中的共同列沒有任何行與之匹配
- 使用
IS NOT NULL和IS NULL來過濾空行或顯示空行# 過濾了沒有訂單的客戶和有退貨的訂單 SELECT customers.first_name AS 'First Name', customers.last_name AS 'Last Name', orders.order_date AS 'Order Date', orders.order_amount AS 'Order Amt' FROM customers LEFT JOIN orders ON orders.customer_id = customers.customre_id LEFT JOIN refunds ON orders.order_id = refunds.order_id WHERE orders.order_id IS NOT NULL AND refunds.refund_id IS NULL ORDER BY customers.customer_id, orders.order_id - 右連接與左連接唯一的不同就是主從表在關(guān)鍵字前后的位置,所以基本上沒有必要使用右連接,建議只使用左連接,因為人直覺上往往認為先列出的表更為重要
- 當設(shè)計有多個表的復(fù)雜
FROM子句時,建議僅使用關(guān)鍵字LEFT JOIN并且避免使用圓括號 - 全連接會顯示所有行,即使沒有在其他表中找到任何一個匹配。但在實際工作中很少會用到全連接,因為表之間的這種類型的關(guān)系是非常少見的。
自連接和視圖
- 自連接:處理那些本質(zhì)上是自引用的表(表中的一列指向自己的另一列,比如員工表中的manager列指向自己的employee_id,員工的經(jīng)理也是員工),為其創(chuàng)建多個視圖
- 可以使用四種表連接中的任意一種來實現(xiàn)自連接,唯二的區(qū)別就是
ON子句中,非自連接的共同列來自兩個表,自連接的共同列來自同一個表,所以這時候需要在FROM關(guān)鍵字和JOIN關(guān)鍵字后為該表各自創(chuàng)建一個別名用以在ON子句中進行區(qū)分
# 列出personnel表中所有員工的經(jīng)理名字 SELECT employees.employee_name AS 'Employee Name', managers.employee_name AS 'Maneger Name' FROM personnel AS 'employees' LEFT JOIN personnel AS 'managers' ON employees.manager_id = managers._employee_id ORDER BY employee.employee_id - 可以使用四種表連接中的任意一種來實現(xiàn)自連接,唯二的區(qū)別就是
- 視圖
- 視圖只是保存在數(shù)據(jù)庫中的
SELECT語句,它不包含任何數(shù)據(jù)。 - 隨著時間的流逝,訪問數(shù)據(jù)的需求會有所變化,但有時很難去重新組織數(shù)據(jù)庫中的數(shù)據(jù)以滿足新的需求。視圖允許為數(shù)據(jù)庫中已經(jīng)存在的數(shù)據(jù)創(chuàng)建新的虛擬視圖(或理解為虛擬的表)而無需重新組織數(shù)據(jù),這為我們增加了始終能保持數(shù)據(jù)庫設(shè)計不斷更新的能力。
- 因為視圖沒有保存物理數(shù)據(jù),所以在視圖中不能包含
ORDER BY子句
- 視圖只是保存在數(shù)據(jù)庫中的
- 創(chuàng)建視圖
# 創(chuàng)建視圖的語法: CREATE VIEW view_name AS select_statement # 一個創(chuàng)建視圖的例子,注意不能有ORDER BY子句 CREATE VIEW customers_orders_refunds AS SELECT customers.first_name AS 'First Name', customers.last_name AS 'Last Name', orders.order_date AS 'Order Date', orders.order_amount AS 'Order Amt' FROM customers LEFT JOIN orders ON orders.customer_id = customers.customre_id LEFT JOIN refunds ON orders.order_id = refunds.order_id WHERE orders.order_id IS NOT NULL AND refunds.refund_id IS NULL - 引用視圖
# 創(chuàng)建視圖 CREATE VIEW view_name AS select_statement # 引用視圖 SELECT * from view_name- 當引用視圖中的列的時候,需要指定列的別名,而列的別名是在創(chuàng)建視圖時指定的
# 創(chuàng)建視圖 CREATE VIEW customers_view AS SELECT first_name AS 'First Name', last_name AS 'Last Name' FROM customers # 引用視圖中的列 SELECT `First Name`, `Last Name`, FROM customers_view WHERE `Last Name` = 'Lopez'
- 當引用視圖中的列的時候,需要指定列的別名,而列的別名是在創(chuàng)建視圖時指定的
- 視圖的優(yōu)點
- 視圖可以減少復(fù)雜度:將復(fù)雜的
SELECT語句封裝為一個視圖 - 視圖可以增加復(fù)用性:封裝那些總是相連的表
- 視圖可以正確地格式化數(shù)據(jù):如果一個表中的某些數(shù)據(jù)總是需要格式化,可以將其封裝到視圖中
- 視圖可以創(chuàng)建計算的列:如果需要一個含有大量的計算字段的表,也可將其封裝到視圖中
- 視圖可以用來重新命名列的名稱:如果一個表中的列名總是需要重命名,可以將其封裝到視圖中
- 視圖可以創(chuàng)建數(shù)據(jù)子集:如果總是只需要看到某個表的某些子集,可以將它們封裝到不同的視圖
- 視圖可以用來加強安全性限制:如果一個表中的某些數(shù)據(jù)希望對某些用戶做訪問限制,可以使用視圖將它們挑出來然后僅將視圖的權(quán)限給那些用戶而不是整個表的權(quán)限
- 視圖可以減少復(fù)雜度:將復(fù)雜的
- 修改視圖:使用
ALTER關(guān)鍵字修改一個已經(jīng)創(chuàng)建的視圖,重新指定被封裝到其中的SELECT語句# 整個被封裝到視圖的select語句都需要重新指定 ALTER VIEW view_name AS new_select_statement # 與Microsoft SQL Server和MySQL不同,Oracle在修改視圖之前,需要使用DROP VIEW view_name先刪除視圖- 同樣,修改視圖與創(chuàng)建視圖一樣,只是修改了視圖的定義,它本身不會返回任何數(shù)據(jù)
- 刪除視圖:使用
DROP VIEW view_name來刪除視圖
子查詢
- 包含在其他查詢中的查詢叫做子查詢,子查詢可以用在
SELECT、INSERT、UPDATE、DELETE語句 - 在
SELECT語句中子查詢可以有三種用法:- 一個一般的
SELECT語句格式如下:SELECT column_list FROM table_list WHERE condition GROUP BY column_list HAVING condition ORDER BY column_list - 當子查詢是
table_list的一部分時,它指定了一個數(shù)據(jù)源 - 當子查詢時
condition的一部分時,它成為查詢條件的一部分 - 當子查詢是
column_list的一部分時,它創(chuàng)建了一個單個的計算的列
- 一個一般的
- 使用子查詢指定數(shù)據(jù)源:把一個子查詢指定為
FROM子句的一部分時,它立即創(chuàng)建了一個新的數(shù)據(jù)源,并被當做一個獨立的表或視圖來引用,與視圖的區(qū)別是視圖是永久保存在數(shù)據(jù)庫中的,而子查詢只是臨時的# 使用子查詢指定數(shù)據(jù)源的一般格式 SELECT column_list FROM [table_list] [JOIN] subquery AS custom_subquery_name # 從address表,city表和country表中列出五個地址對應(yīng)的城市和國家 SELECT address AS 'Address', city AS 'City', country AS 'Country' FROM address LEFT JOIN( SELECT city.city, city.city_id, country.country, country.country_id FROM city LEFT JOIN country ON city.country_id = country.country_id ) AS city_and_country ON address.city_id = city_and_country.city_id ORDER BY address LIMIT 5 - 使用子查詢指定查詢條件:把一個子查詢指定為
WHERE子句中IN操作符的右值,可以以更復(fù)雜的邏輯來為IN操作符創(chuàng)建一個可選列表;注意,當子查詢用于指定查詢條件時,僅能返回單一的列# 使用子查詢指定查詢條件的一般格式 SELECT column_list FROM table_list WHERE column IN subquery SELECT column_list FROM table_list WHERE subquery match_some_comdition # 列出所有使用現(xiàn)金支付的客戶名稱 SEELCT customer_name AS 'Customer Name' FROM costomers WHERE customer_id IN ( SELECT customer_id FROM orders WHERE order_type = 'cash' ) # 列出訂單金額少于20美元的客戶列表 SELECT customer_name AS 'Customer Name' FROM customers WHERE ( SELECT SUM(orderAmount) FROM orders WHERE customers.customer_id = orders.customer_id ) < 20 - 使用子查詢作為計算列:把一個子查詢作為column_list中的一項,將其用作一個計算的列
# 使用子查詢作為計算列的一般格式 SELECT column_list, subquery_result AS 'Result Alia' FROM table_list # 查詢客戶及其訂單數(shù)量 SELECT customer_name AS 'Customer Name', ( SELECT COUNT(order_id) FROM orders WHERE customers.customer_id = orders.customer_id ) AS 'Number of Orders' FROM customers ORDER BY customers.customer_id - 關(guān)聯(lián)子查詢:無法獨立運行的子查詢?yōu)殛P(guān)聯(lián)子查詢,可以獨立運行的子查詢?yōu)榉顷P(guān)聯(lián)子查詢。非關(guān)聯(lián)子查詢完全獨立與外圍查詢語句,只會計算和執(zhí)行一次,而關(guān)聯(lián)子查詢需要針對返回的每一行逐行計算,且每次執(zhí)行子查詢的時候得到的結(jié)果可能都不一樣,上文中查詢客戶及其訂單數(shù)量中的子查詢即為關(guān)聯(lián)子查詢,它使用了外圍查詢的數(shù)據(jù)來源
customers表 -
EXISTS操作符:用于確定一個關(guān)聯(lián)子查詢中是否存在數(shù)據(jù)# 查詢下過訂單的用戶 SELECT customer_name AS 'Customer' FROM customers WHERE EXISTS ( SELECT * FROM orders WHERE customers.customer_id = orders.customer_id )
集合邏輯
在前文中,連接JOIN可以將來自兩個表的列組合到一個表中,子查詢則是將一條SELECT語句的結(jié)果提供給第另一條SELECT語句使用。然而有時候我們希望將來自兩個表的行組合到一個表中,這時候就需要使用SQL中的集合邏輯UNION,來做合并查詢。
-
UNION-合并兩條SELECT語句,選取在A或B中的數(shù)據(jù),如果同時存在在A或B中,僅顯示一條
使用SELECT order_date AS 'Date', 'order' AS 'Type', order_amount AS 'amount' FROM orders WHERE custom_id = 2 UNION SELECT return_date AS 'Date', 'return' AS 'type', return_amount AS 'amount' FROM returns WHERE custom_id = 2 ORDER BY dateUNION需要遵守3個規(guī)則(實際就一條規(guī)則:相同列):- 兩個
SELECT語句中的列的數(shù)量必須相等 - 兩個
SELECT語句中的列排列順序必須相同 - 兩個
SELECT語句中的列數(shù)據(jù)類型必須相同
- 兩個
-
UNION ALL-合并兩條SELECT語句,選取在A或B中的數(shù)據(jù),即使同時存在在A或B中,都將顯示在結(jié)果中SELECT DISTINCT order_date AS 'Date' FROM orders UNION ALL SELECT DISTINCT return_date AS 'Date' FROM returns ORDER BY Date # UNION 確保來自兩個表的行沒有重復(fù)數(shù)據(jù),但 UNION ALL 允許來自兩個表的行可以有相同數(shù)據(jù) # DISTINCT 確保來自同一個表(或者說同一個SELECT語句)的行沒有重復(fù)數(shù)據(jù) # 所以上面的語句選取的數(shù)據(jù)可能會存在重復(fù)數(shù)據(jù),但重復(fù)的數(shù)據(jù)并不來自兩個表而是來自同一個表,并且僅會重復(fù)一次 -
INTERSECT-合并兩條SELECT語句,選取同時出現(xiàn)在A和B中的行(MySql不支持該操作符)SELECT order_date AS 'Date' FROM orders INTERSECT SELECT return_date As 'Date' FROM returns ORDER BY Date -
EXCEPT-合并兩條SELECT語句,選取僅出現(xiàn)在A或僅出現(xiàn)在B中的的數(shù)據(jù)(MySql和Oracle不支持該操作符,但Oracle提供了等價的MINUS操作符)SELECT order_date AS 'Date' FROM orders EXCEPT SELECT return_date AS 'Date' FROM returns ORDER BY Date
存儲過程和參數(shù)
到目前為止,前文所有的SQL語句都是單獨使用,然而很多時候,你會希望SQL語句能夠像函數(shù)一樣,定義一次,然后重復(fù)調(diào)用,并且可以使用參數(shù)來增加靈活性。這時,你就可以使用存儲過程來實現(xiàn)這一目的。
- 創(chuàng)建存儲過程:創(chuàng)建存儲過程不會執(zhí)行任何內(nèi)容,只是直接創(chuàng)建了一個過程,以便后續(xù)執(zhí)行它。與表和視圖一樣,創(chuàng)建好的存儲過程在管理工具中是可以查看的
-- Microsoft SQL Server CREATE PROCEDURE ProcedureName (OptionalPrameterDeclarations) AS BEGIN SQLStatements END -- MySQL DELIMITER $$ -- 規(guī)定END語句使用的分隔符,默認為分號 CREATE PROCEDURE ProcedureName (OptionalPrameterDeclarations) BEGIN SQLStatements; -- 每一條SQL語句都必須使用分號分隔,即使只有一條 END$$ DELIMITER ; -- 將分隔符改回分號 - 存儲過程的參數(shù):例如存儲一個選取特定用戶的SQL過程,可以使用參數(shù)來指定用戶的ID
-- Microsoft SQL Server CREATE PROCUDURE CustomerProcudure (@custId INT) AS BEGIN SELECT * FROM customers WHERE customer_id = @custId END -- MySQL DELIMITER $$ CREATE PROCEDURE CustomerProcudure (custId INT) BEGIN SELECT * FROM customers WHERE CUSTOMER_ID = custId; END DELEMITER ; - 執(zhí)行存儲過程
-- Microsoft SQL Server EXEC CustomerProcudure @custId = 2 -- MySQL CALL CustomerProcudure (2) - 修改和刪除存儲過程:在Microsoft SQL Server中,修改過程和創(chuàng)建過程幾乎一樣,只需要把
CREATE關(guān)鍵字替換為ALTER關(guān)鍵字即可;然而在MySQL中,雖然也存在ALTER命令,但它的功能十分簡單,所以一般我們選擇先刪除存儲過程然后再重新創(chuàng)建-- 刪除存儲過程 DROP PROCEDURE ProcedureName - 函數(shù)與存儲過程的兩點區(qū)別
- 存儲過程可以有任意數(shù)目的輸出,而函數(shù)只有單一的返回值
- 只能通過調(diào)用程序來執(zhí)行存儲過程,而函數(shù)可以在SQL語句中使用
修改數(shù)據(jù)
- 修改策略:使用“軟刪除(使用表中特定的列來標記該行數(shù)據(jù)是否有效)”技術(shù)替代真正的刪除;插入新行時在特定列中標記準確的插入日期和時間以便出錯時對其進行刪除;使用單獨的表來保存事務(wù)所要更新的數(shù)據(jù)通常是明智的選擇。請永遠記住,SQL中沒有撤銷命令。
- 插入數(shù)據(jù):使用
INSERT命令來插入指定數(shù)據(jù),注意不需要為自增型的列指定數(shù)據(jù),數(shù)據(jù)庫會自動處理它;另外,Oracle不允許一次插入多行數(shù)據(jù),需要分開書寫- 插入
INSERT語句中指定的具體數(shù)據(jù)-- 向customer表插入兩條新數(shù)據(jù) INSERT INTO customers (first_name, last_name, state) -- 只要列名是正確的,它們的順序無所謂 -- 當這里的列名順序與數(shù)據(jù)庫中的物理順序一致時可省略它們,但強烈不建議這么做 VALUES ('Virginia', 'Jones', 'OH'), -- VALUES關(guān)鍵字后的數(shù)據(jù)列,要與INSERT INTO后的列相對應(yīng) ('Clark', 'Woodland', 'CA') - 插入用一條
SELECT語句指定的數(shù)據(jù)-- 將customer_transaction中的RI州的用戶插入到customer表中 INSERT INTO customer (first_name, last_name, state) SELECT fn, ln, state -- 這里選中列的順序需要與INSERT INTO 語句中的順序一致 FROM customer_transactions WHERE state = 'RI'
- 插入
- 刪除數(shù)據(jù):使用
DELETE命令來刪除一條數(shù)據(jù),通常是一整行(刪除某行中的列沒有意義,那屬于修改數(shù)據(jù)的范疇)-- 刪除數(shù)據(jù)的一般寫法 DELETE FROM table_name WHERE conditions -- 可以使用SELECT語句來驗證刪除結(jié)果 SELECT COUNT (*) -- 使用聚合函數(shù)COUNT來統(tǒng)計被刪除數(shù)據(jù)的數(shù)量以確認是否全部都被刪除了 FROM table_name WHERE conditions -- 清空一個表中的所有數(shù)據(jù),可以使用TRUNCATE TABLE語句 TRUNCATE TABLE customers -- 上面的語句與下面的DELETE語句效果基本相同 DELETE FROM customers -- 唯一不同在于,TRUNCATE TABLE語句重置了自增列,而DELETE語句沒有 - 更新(修改)數(shù)據(jù):刪除數(shù)據(jù)只需要指定刪除的行即可,但更新數(shù)據(jù)是針對具體行中的具體列,所以需要首先指定更新哪些列,然后指定更新這些列中的哪些行
- 使用指定的具體數(shù)據(jù)更新數(shù)據(jù)
-- 更新數(shù)據(jù)的一般格式 UPDATE table SET column1 = expression1, column2 = expression2 -- repeat any number of time WHERE conditions -- 如果沒有指定行,該句會把所有行的指定列都更新一次 - 使用子查詢中的數(shù)據(jù)修改數(shù)據(jù)(使用一個表中的數(shù)據(jù)來更新另一個表中的數(shù)據(jù))
-- 一般格式 UPDATE table -- 指定要更新的表 SET table.column_1 = -- 指定需要更新的列1 ( SELECT another_table.column_1 -- 子查詢從另一表中獲取數(shù)據(jù),并通過主鍵(也可是其它)來進行匹配 FROM another_table WHERE another_table.auto_increment_primary_key = table.auto_increment_primary_key ) SET table.column_2 = -- 指定需要更新的列2 ( SELECT another_table.column_2 FROM another_table WHERE another_table.auto_increment_primary_key = auto_increment_primary_key.column_2 ) WHERE EXISTS -- 指定需要更新的行,使用子查詢指定只更新table中存在于another_table中的行 ( SELECT * FROM another_table WHERE another_table.auto_increment_primary_key = table.auto_increment_primary_key )
- 使用指定的具體數(shù)據(jù)更新數(shù)據(jù)
維護表
- 回顧SQL語言的三種組成部分:數(shù)據(jù)操縱語言(Data Manipulation Language,DML,對數(shù)據(jù)庫中或者更詳細地說是表中的數(shù)據(jù)進行增刪改查操作)、數(shù)據(jù)定義語言(Data Definition Language,DDL,對數(shù)據(jù)庫中的表及其索引進行增刪改查)、和數(shù)據(jù)控制語言(Data Control Language,DCL,維護數(shù)據(jù)庫安全)。本章主要講述DDL,但前文也已經(jīng)用到過DDL,視圖
VIEW、過程PROCEDURE需要用到的都是DDL - 添加或修改表和索引的SQL語句很復(fù)雜,但是我們無需了解細節(jié),數(shù)據(jù)庫軟件通常提供了圖形化的工具來修改表的結(jié)構(gòu),而不一定需要使用SQL語句
- 表屬性:表(Table)是數(shù)據(jù)庫中最重要的對象,數(shù)據(jù)庫中所有數(shù)據(jù)物理地存儲在表中,沒有了表數(shù)據(jù)庫中的一切也就沒有意義了。前文已經(jīng)介紹過一些表的屬性,主鍵、外鍵、數(shù)據(jù)類型、自增型列等等
- 表的列
- 列名:表中的每個列都必須有唯一的列名
- 數(shù)據(jù)類型:決定列可以包含什么樣的數(shù)據(jù)
- 是否自增型:表中每增加一行,該列會以升序序列自動賦值(術(shù)語auto-increment是MySQL中的的特定用法,Oracle沒有自增型屬性)
- 默認值
- 表的列
- 主鍵和索引
- 主鍵:只能指定一個列作為主鍵,目的是保證這個列包含唯一值(所以不允許它們包含
NULL值);實際上主鍵可以跨越多個列,叫做復(fù)合主鍵(當希望使用電影名稱列來作為主鍵時可能會存在重復(fù)名稱, 這時可以使用電影名稱+年份兩個列作為復(fù)合主鍵來唯一地定義每部電影) - 索引:索引是一種物理結(jié)構(gòu),目的是當SQL語句中包含該列的時候,可以加速數(shù)據(jù)檢索,缺點是需要更多的磁盤空間,并且會減慢更新數(shù)據(jù)時的速度
- 主鍵:只能指定一個列作為主鍵,目的是保證這個列包含唯一值(所以不允許它們包含
- 外鍵:
- 外鍵定義:外鍵是從一個表中的一個列到另一個不同的表中的列的直接引用,含有外鍵的表為“子表”,被外鍵引用的表被稱為“父表”
- 外鍵級聯(lián)(Cascade):當父表有更新或刪除時,會自動更新或刪除子表中的關(guān)聯(lián)行
- Set Null:當父表有更新或刪除時,如果影響到子表,是否把子表中關(guān)聯(lián)行的外鍵設(shè)置為
NULL
- 創(chuàng)建表:使用
CREATE TABLE語句來創(chuàng)建表及其屬性(列),不同數(shù)據(jù)庫之間存在差異:
使用-- Microsoft SQL Server CREATE TABLE my_table ( column_1 INT IDENTITY (1, 1) PRIMARY KEY NOT NULL, -- 列名column_1,INT類型,自增型,主鍵,不能為NULL column_2 NOT NULL REFERENCES related_table (first_column), -- 列名column_2,INT類型,不能為NULL,外鍵,關(guān)聯(lián)到related_table表的first_column列 column_3 VARCHAR (25) NULL, -- 列名column_3,VARCHART類型,可以是NULL column_4 FLOAT NULL DEFAULT (10) -- 列名column_4,F(xiàn)LOAT類型,可以是NULL ) -- My SQL CRAET TABLE my_table column_1 INT AUTO_INCREMENT PRIMARY KEY NOT NULL, column_2 INT NOT NULL, column_3 VARCHAR (25) NULL, column_4 FLOAT NULL DEFAULT 10 NULL, CONSTRAINT FOREIGN KEY (column_2) REFERENCE 'related_table' (first_column) -- 指定外鍵 -- Oracle CREATE TABLE my_table ( column_1 INT PRIMARY KEY NOT NULL, -- Oracle不允許有自增型的列 column_2 INT NOT NULL, column_3 VARCHAR2 (25) null, column_4 FLOAT DEFAULT 10 NULL CONSTRAINT "foreign_key" FOREIGN KEY (column_2) REFERENCES related_table (first_column) )ALTER TABLE語句修改表的具體屬性,該語句的復(fù)雜性及數(shù)據(jù)庫差異巨大,這里不再展開;使用DROP TABLE table_name語句來刪除一個表-- 修改表 ALTER TABLE my_table DROP COLUMN column_3 -- 刪除表 DROP TABLE my_table - 創(chuàng)建索引:使用
CREATE INDEX語句,用來在創(chuàng)建表之后創(chuàng)建索引,使用ALTER INDEX語句來添加和修改索引-- 創(chuàng)建索引 CREATE INDEX index_2 ON my_table (column_4) -- 刪除索引 DROP INDEX index_2 ON my_table
數(shù)據(jù)庫設(shè)計原理與顯示數(shù)據(jù)的策略(略)
- 關(guān)系型數(shù)據(jù)庫是一個數(shù)據(jù)集合,數(shù)據(jù)庫中的表以某些方式相互關(guān)聯(lián)。
- SQL語句僅僅是使用數(shù)據(jù)庫的工具,數(shù)據(jù)庫設(shè)計則是另外一個更為重要的話題?!禨QL初學(xué)者指南》中對這個話題進行了簡單的概括:規(guī)范化及其替代方法,這里將不再展開。
- 關(guān)于這個話題建議閱讀另外的一些書籍:《SQL必知必會》、《高性能MySQL》
