SQL(Structured Query Language)是一種專門用來與數據庫溝通的語言。算法
數據庫(database):保存有組織的數據的容器(一般是一個文件或一組文件)。sql
表(table):某種特定類型數據的結構化清單。數據庫
模式(schema):關於數據庫和表的佈局及特性的信息。服務器
列(column):關於數據庫和表的佈局及特性的信息。網絡
行(row):表中的一個記錄。數據庫設計
主鍵(primary key):一列(或一組列),其值可以惟一標識表中每一行。函數
爲了使用 SELECT 檢索表數據,必須至少給出兩條信息——想選擇什麼,以及從什麼地方選擇。佈局
結束 SQL 語句:多條 SQL 語句必須以分號(;)分隔。多數 DBMS 不須要在單條 SQL 語句後加分號,但也有 DBMS 可能必須在單條 SQL 語句後加上分號。固然,若是願意能夠老是加上分號。事實上,即便不必定須要,加上分號也確定沒有壞處。性能
SQL 語句和大小寫:請注意,SQL 語句不區分大小寫,所以 SELECT 與 select 是相同的。一樣,寫成 Select 也沒有關係。許多 SQL 開發人員喜歡對 SQL 關鍵字使用大寫,而對列名和表名使用小寫,這樣作使代碼更易於閱讀和調試。不過,必定要認識到雖然 SQL 是不區分大小寫的,可是表名、列名和值可能有所不一樣(這有賴於具體的 DBMS 及其如何配置)。測試
使用空格:在處理 SQL 語句時,其中全部空格都被忽略。
SELECT prod_name
FROM Products;
複製代碼
SELECT prod_id, prode_name, prod_price
FROM Products;
複製代碼
小心逗號:在選擇多個列時,必定要在列名之間加上逗號,但最後一個列名後不加。若是在最後一個列名後加了逗號,將出現錯誤。
SELECT *
FROM Products;
複製代碼
使用通配符:通常而言,除非你確實須要表中的每一列,不然最好別使用*通配符。雖然使用通配符能讓你本身省事,不用明確列出所需列,但檢索不須要的列一般會下降檢索和應用程序的性能。
SELECT DISTINCT vend_id
FROM Products;
複製代碼
# 檢索前 5 行
SELECT prod_name
FROM Products
LIMIT 5;
# 從第 3 行開始檢索 5 行
SELECT prod_name
FROM Products
LIMIT 5 OFFSET 3;
# or
SELECT prod_name
FROM Products
LIMIT 3, 5;
複製代碼
第 0 行:第一個被檢索的行是第 0 行,而不是第 1 行。所以,LIMIT 1 OFFSET 1 會檢索第 2 行,而不是第 1 行。
# 單行註釋
SELECT prod_name FROM Products; -- 行內註釋
/* 塊級註釋 */
SELECT prod_name
FROM Products
ORDER BY prode_name;
複製代碼
ORDER BY 子句的位置:在指定一條 ORDER BY 子句時,應該保證它是 SELECT 語句中最後一條子句。若是它不是最後的子句,將會出現錯誤消息。
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price, prod_name;
複製代碼
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY 2, 3;
複製代碼
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC;
SELECT prod_id, prod_price, prod_name
FROM Products
ORDER BY prod_price DESC, prod_name;
複製代碼
請注意,DESC 是 DESCENDING 的縮寫,這兩個關鍵字均可以使用。與 DESC 相對的是 ASC(或ASCENDING),在升序排序時能夠指定它。但實際上,ASC 沒有多大用處,由於升序是默認的(若是既不指定 ASC 也不指定 DESC,則假定爲 ASC)。
在 SELECT 語句中,數據根據 WHERE 子句中指定的搜索條件進行過濾。WHERE 子句在表名(FROM 子句)以後給出。
SELECT prod_name, prod_price
FROM Products
WHERE prod_price = 3.49;
SELECT prod_name, prod_price
FROM Products
WHERE prod_price < 10;
複製代碼
SQL 過濾與應用過濾:數據也能夠在應用層過濾。爲此,SQL 的 SELECT 語句爲客戶端應用檢索出超過實際所需的數據,而後客戶端代碼對返回數據進行循環,提取出須要的行。 一般,這種作法極其不妥。優化數據庫後能夠更快速有效地對數據進行過濾。而讓客戶端應用(或開發語言)處理數據庫的工做將會極大地影響應用的性能,而且使所建立的應用徹底不具有可伸縮性。此外,若是在客戶端過濾數據,服務器不得不經過網絡發送多餘的數據,這將致使網絡帶寬的浪費。
WHERE 子句的位置:在同時使用 ORDER BY 和 WHERE 子句時,應該讓 ORDER BY 位於 WHERE 以後,不然將會產生錯誤。
=
:等於>
:大於>=
:大於等於<>
:不等於!=
:不等於!>
:不大於!<
:不小於<
:小於<=
:小於等於BETWEEN
:在指定的兩個值之間IS NULL
:爲 NULL 值操做符兼容:某些操做符是冗餘的(如 <> 與 != 相同,!< 至關於 >=)。並不是全部 DBMS 都支持這些操做符。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id != 'DLL01’; 複製代碼
什麼時候使用引號:若是仔細觀察上述 WHERE 子句中的條件,會看到有的值括在單引號內,而有的值未括起來。單引號用來限定字符串。若是將值與字符串類型的列進行比較,就須要限定引號。用來與數值列進行比較的值不用引號。
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
複製代碼
SELECT prod_name, prod_price
FROM Products
WHERE prod_price IS NULL;
複製代碼
爲了進行更強的過濾控制,SQL 容許給出多個 WHERE 子句。這些子句有兩種使用方式,即以 AND 子句或 OR 子句的方式使用。
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' AND prod_price <= 4;
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'D 複製代碼
AND: 用在 WHERE 子句中的關鍵字,用來指示檢索知足全部給定條件的行。
OR:WHERE 子句中使用的關鍵字,用來表示檢索匹配任一給定條件的行。
# 優先匹配 AND 左右兩側的條件
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE vend_id = 'DLL01' OR vend_id = 'BRS01' AND prod_price >= 10;
# 優先匹配括號內的條件
SELECT prod_id, prod_price, prod_name
FROM Products
WHERE (vend_id = 'DLL01' OR vend_id = 'BRS01') AND prod_price >= 10;
複製代碼
SQL 在處理 OR 操做符以前,優先處理 AND 操做符。圓括號具備更高的優先級。
IN 操做符用來指定條件範圍,範圍中的每一個條件均可以進行匹配。
SELECT prod_name, prod_price
FROM Products
WHERE vend_id IN ( 'DLL01', 'BRS01' )
ORDER BY prod_name;
複製代碼
IN:WHERE 子句中用來指定要匹配值的清單的關鍵字,功能與 OR 至關。
WHERE 子句中 NOT 操做符有且只有一個功能,那就是否認其後所跟的任何條件。
SELECT prod_name
FROM Products
WHERE NOT vend_id = 'DLL01'
ORDER BY prod_name;
複製代碼
NOT:WHERE 子句中用來否認其後條件的關鍵字。
爲在搜索子句中使用通配符,必須使用 LIKE 操做符。LIKE 指示 DBMS,後跟的搜索模式利用通配符匹配而不是簡單的相等匹配進行比較。
通配符(wildcard):用來匹配值的一部分的特殊字符。
搜索模式(search pattern):由字面值、通配符或二者組合構成的搜索條件。
在搜索串中,% 表示任何字符出現任意次數(包括 0 個字符)。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'Fish%';
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '%bean bag%';
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE 'F%y';
複製代碼
下劃線的用途與 % 同樣,但它只匹配單個字符,而不是多個字符。
SELECT prod_id, prod_name
FROM Products
WHERE prod_name LIKE '__ inch teddy bear';
複製代碼
方括號([])通配符用來指定一個字符集,它必須匹配指定位置(通配符的位置)的一個字符。
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[JM]%' ORDER BY cust_contact;
SELECT cust_contact
FROM Customers
WHERE cust_contact LIKE '[^JM]%' ORDER BY cust_contact;
SELECT cust_contact
FROM Customers
WHERE NOT cust_contact LIKE '[JM]%' ORDER BY cust_contact;
複製代碼
SQL 的通配符頗有用。但這種功能是有代價的,即通配符搜索通常比前面討論的其餘搜索要耗費更長的處理時間。
存儲在數據庫表中的數據通常不是應用程序所須要的格式,下面舉幾個例子。
咱們須要直接從數據庫中檢索出轉換、計算或格式化過的數據,而不是檢索出數據,而後再在客戶端應用程序中從新格式化。
這就是計算字段能夠派上用場的地方了。與前幾課介紹的列不一樣,計算字段並不實際存在於數據庫表中。計算字段是運行時在 SELECT 語句內建立的。
SELECT vend_name + ' (' + vend_country + ')'
FROM Vendors
ORDER BY vend_name;
# or
SELECT vend_name || ' (' || vend_country || ')'
FROM Vendors
ORDER BY vend_name;
複製代碼
許多數據庫(不是全部)保存填充爲列寬的文本值,而實際上你要的結果不須要這些空格。爲正確返回格式化的數據,必須去掉這些空格。這可使用 SQL 的 RTRIM() 函數來完成。
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')'
FROM Vendors
ORDER BY vend_name;
複製代碼
RTRIM()
:去掉字符串右邊的空格。LTRIM()
:去掉字符串左邊的空格。TRIM()
:去掉字符串兩邊的空格。SELECT vend_name + ' (' + vend_country + ')' AS vend_title
FROM Vendors
ORDER BY vend_name;
複製代碼
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price
FROM OrderItems
WHERE order_num = 20008;
複製代碼
SQL 支持的算術操做符:
+
:加-
:減*
:乘/
:除事實上,只有少數幾個函數被全部主要的 DBMS 等同地支持。雖然全部類型的函數通常均可以在每一個 DBMS 中使用,但各個函數的名稱和語法可能極其不一樣。
大多數 SQL 實現支持如下類型的函數:
SELECT vend_name, UPPER(vend_name) AS upper_vend_name
FROM Vendors
ORDER BY vend_name;
SELECT vend_name LENGTH(vend_name) AS vend_name_length
FROM Vendors
ORDER BY vend_name;
複製代碼
經常使用的文本處理函數:
LEFT()
:返回字符串左邊的字符LENGTH()
:返回字符串的長度LOWER()
:將字符串轉換爲小寫LTRIM()
:去掉字符串左邊的空格RIGHT()
:返回字符串右邊的字符RTRIM()
:去掉字符串右邊的空格SOUNDEX()
:返回字符串的 SOUNDEX 值UPPER
:將字符串轉換爲大寫SOUNDEX 是一個將任何文 本串轉換爲描述其語音表示的字母數字模式的算法。SOUNDEX 考慮了相似的發音字符和音節,使得能對字符串進行發音比較而不是字母比較。
日期和時間採用相應的數據類型存儲在表中,每種 DBMS 都有本身的特殊形式。日期和時間值以特殊的格式存儲,以便能快速和有效地排序或過濾,而且節省物理存儲空間。
應用程序通常不使用日期和時間的存儲格式,所以日期和時間函數老是用來讀取、統計和處理這些值。因爲這個緣由,日期和時間函數在 SQL 中具備重要的做用。遺憾的是,它們很不一致,可移植性最差。
SELECT order_num
FROM Orders
WHERE strftime('%Y', order_date) = '2012';
複製代碼
經常使用數值處理函數:
ABS()
:返回一個數的絕對值COS()
:返回一個角度的餘弦EXP()
:返回一個數的指數值PI()
:返回圓周率SIN()
:返回一個角度的正弦SQRT()
:返回一個數的平方根TAN()
:返回一個角度的正切彙集函數(aggregate function):對某些行運行的函數,計算並返回一個值。
SQL 彙集函數:
AVG()
:返回某列的平均值COUNT()
:返回某列的行數MAX()
:返回某列的最大值MIN()
:返回某列的最小值SUM()
:返回某列值之和AVG() 經過對錶中行數計數並計算其列值之和,求得該列的平均值。AVG() 可用來返回全部列的平均值,也能夠用來返回特定列或行的平均值。
SELECT AVG(prod_price) AS avg_price
FROM Products;
SELECT AVG(prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';
複製代碼
COUNT() 函數進行計數。可利用 COUNT() 肯定表中行的數目或符合特定條件的行的數目。
COUNT() 函數有兩種使用方式:
SELECT COUNT(*) AS num_cust
FROM Products;
SELECT COUNT(cust_email) AS num_cust
FROM Customers;
複製代碼
MAX() 返回指定列中的最大值。
SELECT MAX(prod_price) AS max_price
FROM Products;
複製代碼
對非數值數據使用 MAX():雖然 MAX() 通常用來找出最大的數值或日期值,但許多(並不是全部)DBMS 容許將它用來返回任意列中的最大值,包括返回文本列中的最大值。在用於文本數據時,MAX() 返回按該列排序後的最後一行。
MIN() 的功能正好與 MAX() 功能相反,它返回指定列的最小值。
SELECT MIN(prod_price) AS min_price
FROM Products;
複製代碼
對非數值數據使用 MIN():雖然 MIN() 通常用來找出最小的數值或日期值,但許多(並不是全部)DBMS 容許將它用來返回任意列中的最小值,包括返回文本列中的最小值。在用於文本數據時,MIN() 返回該列排序後最前面的行。
SUM() 用來返回指定列值的和(總計)。
SELECT SUM(quantity) AS items_ordered
FROM OrderItems
WHERE order_num = 20005;
SELECT SUM(item_price * quantity) AS total_price
FROM OrderItems
WHERE order_num = 20005;
複製代碼
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01; 複製代碼
SELECT COUNT(*) AS num_items, MIN(prod_price) AS price_min, MAX(prod_price) AS price_max, AVG(prod_price) AS price_avg
FROM Products;
複製代碼
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id;
複製代碼
在使用 GROUP BY 子句前,須要知道一些重要的規定。
SELECT vend_id, COUNT(*) AS num_prods
FROM Products
GROUP BY vend_id HAVING COUNT(*) >= 2;
SELECT vend_Id, COUNT(*) AS num_prods
FROM Products
WHERE prod_price >= 4
GROUP BY vend_id HAVING COUNT(*) >= 2;
SELECT order_num, COUNT(*) AS items
FROM OrderItems
GROUP BY order_num HAVING COUNT(*) >= 3
ORDER BY items, order_num;
複製代碼
子句 | 說明 | 是否必須使用 |
---|---|---|
SELECT | 要返回的列或表達式 | 是 |
FROM | 從中檢索數據的表 | 僅在從表選擇數據時使用 |
WHERE | 行級過濾 | 否 |
GROUP BY | 分組說明 | 僅在按組計算彙集時使用 |
HAVING | 組級過濾 | 否 |
ORDER BY | 輸出排序順序 | 否 |
SELECT cust_id
FROM Orders
WHERE order_num IN (
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01');
SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (
SELECT cust_id
FROM Orders
WHERE order_num IN (
SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01'));
複製代碼
只能是單列:做爲子查詢的 SELECT 語句只能查詢單個列。企圖檢索多個列將返回錯誤。
子查詢和性能:這裏給出的代碼有效,而且得到了所需的結果。可是,使用子查詢並不老是執行這類數據檢索的最有效方法。
SELECT cust_name, cust_state, (
SELECT COUNT(*)
FROM Orders
WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;
複製代碼
相同的數據出現屢次決不是一件好事,這是關係數據庫設計的基礎。關係表的設計就是要把信息分解成多個表,一類數據一個表。各表經過某些共同的值互相關聯(因此才叫關係數據庫)。
關係數據能夠有效地存儲,方便地處理。所以,關係數據庫的可伸縮性遠比非關係數據庫要好。
將數據分解爲多個表能更有效地存儲,更方便地處理,而且可伸縮性更好。
簡單說,聯結是一種機制,用來在一條 SELECT 語句中關聯表,所以稱爲聯結。使用特殊的語法,能夠聯結多個表返回一組輸出,聯結在運行時關聯表中正確的行。
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
複製代碼
徹底限定列名:就像前一課提到的,在引用的列可能出現歧義時,必須使用徹底限定列名(用一個句點分隔表名和列名)。若是引用一個沒有用表名限制的具備歧義的列名,大多數 DBMS 會返回錯誤。
WHERE 子句做爲過濾條件,只包含那些匹配給定條件(這裏是聯結條件)的行。沒有 WHERE 子句,第一個表中的每一行將與第二個表中的每一行配對,而無論它們邏輯上是否能配在一塊兒。
笛卡爾積:由沒有聯結條件的表關係返回的結果爲笛卡兒積。檢索出的行的數目將是第一個表中的行數乘以第二個表中的行數。
# 錯誤示範
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products;
複製代碼
目前爲止使用的聯結稱爲等值聯結(equijoin),它基於兩個表之間的相等測試。這種聯結也稱爲內聯結(inner join)。
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
複製代碼
在使用這種語法時,聯結條件用特定的 ON 子句而不是 WHERE 子句給出。傳遞給 ON 的實際條件與傳遞給 WHERE 的相同。
SQL 不限制一條 SELECT 語句中能夠聯結的表的數目。建立聯結的基本規則也相同。首先列出全部表,而後定義表之間的關係。
SELECT prod_name, vend_name, prod_price, quantity
FROM OrderItems, Products, Vendors
WHERE Products.vend_id = Vendors.vend_id
AND OrderItems.prod_id = Products.prod_id
AND order_num = 20007;
複製代碼
性能考慮:DBMS 在運行時關聯指定的每一個表,以處理聯結。這種處理可能很是耗費資源,所以應該注意,不要聯結沒必要要的表。聯結的表越多,性能降低越厲害。
多作實驗:執行任一給定的 SQL 操做通常不止一種方法。不多有絕對正確或絕對錯誤的方法。性能可能會受操做類型、所使用的 DBMS、表中數據量、是否存在索引或鍵等條件的影響。所以,有必要試驗不一樣的選擇機制,找出最適合具體狀況的方法。
給列起別名的語法以下:
SELECT RTRIM(vend_name) + ' (' + RTRIM(vend_country) + ')' AS vend_title
FROM Vendors
ORDER BY vend_name;
複製代碼
給表起別名的語法:
SELECT cust_name, cust_contact
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';
複製代碼
須要注意,表別名只在查詢執行中使用。與列別名不同,表別名不返回到客戶端。
迄今爲止,咱們使用的只是內聯結或等值聯結的簡單聯結。如今來看三種其餘聯結:自聯結(self-join)、天然聯結(natural join)和外聯結(outer join)。
SELECT c1.cust_id, c1.cust_name, c1.cust_contact
FROM Customers AS c1, Customers AS c2
WHERE c1.cust_name = c2.cust_name
AND c2.cust_contact = 'Jim Jones';
複製代碼
此查詢中須要的兩個表其實是相同的表,所以 Customers 表在 FROM 子句中出現了兩次。雖然這是徹底合法的,但對 Customers 的引用具備歧義性,由於 DBMS 不知道你引用的是哪一個 Customers 表。解決此問題,須要使用表別名。
用自聯結而不用子查詢:自聯結一般做爲外部語句,用來替代從相同表中檢索數據的使用子查詢語句。雖然最終的結果是相同的,但許多 DBMS 處理聯結遠比處理子查詢快得多。應該試一下兩種方法,以肯定哪種的性能更好。
不管什麼時候對錶進行聯結,應該至少有一列不止出如今一個表中(被聯結的列)。標準的聯結(前一課中介紹的內聯結)返回全部數據,相同的列甚至屢次出現。天然聯結排除屢次出現,使每一列只返回一次。
天然聯結要求你只能選擇那些惟一的列,通常經過對一個表使用通配符(SELECT *),而對其餘表的列使用明確的子集來完成。
SELECT C.*, O.order_num, O.order_date, OI.prod_id, OI.quantity, OI.item_price
FROM Customers AS C, Orders AS O, OrderItems AS OI
WHERE C.cust_id = O.cust_id
AND OI.order_num = O.order_num
AND prod_id = 'RGAN01';
複製代碼
許多聯結將一個表中的行與另外一個表中的行相關聯,但有時候須要包含沒有關聯行的那些行。
SELECT Customers.cust_id, Orders.order_num
FROM Customers LEFT OUTER JOIN orders
ON Customers.cust_id = Orders.cust_id;
複製代碼
與內聯結關聯兩個表中的行不一樣的是,外聯結還包括沒有關聯行的行。在使用 OUTER JOIN 語法時,必須使用 RIGHT 或 LEFT 關鍵字指定包括其全部行的表(RIGHT指出的是 OUTER JOIN 右邊的表,而LEFT指出的是 OUTER JOIN左邊的表)。上面的例子使用 LEFT OUTER JOIN 從 FROM 子句左邊的表(Customers 表)中選擇全部行。爲了從右邊的表中選擇全部行,須要使用 RIGHT OUTER JOIN。
SELECT Customers.cust_id, Orders.order_num
FROM Customers RIGHT OUTER JOIN Orders
ON Orders.cust_id = Customers.cust_Id;
複製代碼
外聯結的類型:有兩種基本的外聯結形式:左外聯結和右外聯結。它們之間的惟一差異是所關聯的表的順序。換句話說,調整 FROM 或 WHERE 子句中表的順序,左外聯結能夠轉換爲右外聯結。所以,這兩種外聯結能夠互換使用,哪一個方便就用哪一個。
還存在另外一種外聯結,就是全外聯結(full outer join),它檢索兩個表中的全部行並關聯那些能夠關聯的行。與左外聯結或右外聯結包含一個表的不關聯的行不一樣,全外聯結包含兩個表的不關聯的行。
SELECT Customers.cust_id, Orders.order_num
FROM Orders FULL OUTER JOIN Customers
ON Orders.cust_id = Customers.cust_id;
複製代碼
SELECT Customers.cust_id, COUNT(Orders.order_num) AS num_ord
FROM Customers INNER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
複製代碼
彙集函數也能夠方便地與其餘聯結一塊兒使用。
SELECT Customers.cust_id, COUNT(Orders.order_num) AS num_ord
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id
GROUP BY Customers.cust_id;
複製代碼