關係模型(Relational model)由 E.F.Codd 博士於 1970 年提出,以集合論中的關係概念爲基礎;不管是現實世界中的實體對象仍是它們之間的聯繫都使用關係表示。咱們在數據庫系統中看到的關係就是二維表(Table),由行(Row)和列(Column)組成。所以,也能夠說關係表是由數據行構成的集合。sql
關係模型由數據結構、關係操做、完整性約束三部分組成。數據庫
關係模型中的數據結構就是關係表,包括基礎表、派生表(查詢結果)和虛擬表(視圖)。編程
經常使用的關係操做包括增長、刪除、修改和查詢(CRUD),使用的就是 SQL 語言。其中查詢操做最爲複雜,包括選擇(Selection)、投影(Projection)、並集(Union)、交集(Interp)、差集(Exception)以及笛卡兒積(Cartesian product)等。數據結構
完整性約束用於維護數據的完整性或者知足業務約束的需求,包括實體完整性(主鍵約束)、參照完整性(外鍵約束)以及用戶定義的完整性(非空約束、惟一約束、檢查約束和默認值)。編程語言
咱們今天的主題是關係操做語言,也就是 SQL。ide
面向集合
SQL(結構化查詢語言)是操做關係數據庫的標準語言。SQL 很是接近英語,使用起來很是簡單。它在設計之初就考慮了非技術人員的使用需求,咱們一般只需說明想要的結果(What),而將數據處理的過程(How)交給數據庫管理系統。因此說,SQL 纔是真正給人用的編程語言!函數
接下來咱們具體分析一下關係的各類操做語句;目的是爲了讓你們可以瞭解 SQL 是一種面向集合的編程語言,它的操做對象是集合,操做的結果也是集合。設計
SELECT在關係數據庫中,關係、表、集合三者一般表示相同的概念。3d
下面是一個簡單的查詢語句:code
SELECT employee_id, first_name, last_name, hire_date FROM employees;
它的做用就是從 employees 表中查詢員工信息。顯然,咱們都知道 FROM 以後是一個表(關係、集合)。不只如此,整個查詢語句的結果也是一個表。因此,咱們能夠將上面的查詢做爲表使用:
SELECT * FROM (SELECT employee_id, first_name, last_name, hire_date FROM employees) t;
括號內的查詢語句被稱爲派生表,咱們給它指定了一個別名叫作 t。一樣,整個查詢結果也是一個表;這就意味着咱們能夠繼續嵌套,雖然這麼作很無聊。
咱們再看一個 PostgreSQL 中的示例:
-- PostgreSQL SELECT * FROM upper('sql'); | upper | |-------| | SQL |
upper() 是一個大寫轉換的函數。它出現再 FROM 子句中,意味着它的結果也是一個表,只不過是 1 行 1 列的特殊表。
SELECT 子句用於指定須要查詢的字段,能夠包含表達式、函數值等。SELECT 在關係操做中被稱爲投影(Projection),看下面的示意圖應該就比較好理解了。
除了 SELECT 以外,還有一些經常使用的 SQL 子句。
WHERE 用於指定數據過濾的條件,在關係運算中被稱爲選擇(Selection),示意圖以下:
ORDER BY 用於對查詢的結果進行排序,示意圖以下:
總之,SQL 能夠完成各類數據操做,例如過濾、分組、排序、限定數量等;全部這些操做的對象都是關係表,結果也是關係表。
在這些關係操做中,有一個比較特殊,就是分組。
GROUP BY分組( GROUP BY)操做和其餘的關係操做不一樣,由於它改變了關係的結構。來看下面的示例:
SELECT department_id, count(*), first_name FROM employees GROUP BY department_id;
該語句的目的是按照部門統計員工的數量,可是存在一個語法錯誤,就是 first_name 不能出如今查詢列表中。緣由在於按照部門進行分組的話,每一個部門包含多個員工;沒法肯定須要顯示哪一個員工的姓名,這是一個邏輯上的錯誤。
因此說,GROUP BY 改變了集合元素(數據行)的結構,建立了一個全新的關係。分組操做的示意圖以下:
儘管如此,GROUP BY 的結果仍然是一個集合。
UNIONSQL 面向集合特性最明顯的體現就是 UNION(並集運算)、INTERSECT(交集運算)和 EXCEPT/MINUS(差集運算)。
這些集合運算符的做用都是將兩個集合併成一個集合,所以須要知足如下條件:
兩邊的集合中字段的數量和順序必須相同;
兩邊的集合中對應字段的類型必須匹配或兼容。
具體來講,UNION 和 UNION ALL 用於計算兩個集合的並集,返回出如今第一個查詢結果或者第二個查詢結果中的數據。它們的區別在於 UNION 排除告終果中的重複數據,UNION ALL 保留了重複數據。下面是 UNION 操做的示意圖:
INTERSECT 操做符用於返回兩個集合中的共同部分,即同時出如今第一個查詢結果和第二個查詢結果中的數據,而且排除告終果中的重複數據。INTERSECT 運算的示意圖以下:
EXCEPT 或者 MINUS 操做符用於返回兩個集合的差集,即出如今第一個查詢結果中,但不在第二個查詢結果中的記錄,而且排除告終果中的重複數據。EXCEPT 運算符的示意圖以下:
除此以外,DISTINCT 運算符用於消除重複數據,也就是排除集合中的重複元素。
JOINSQL 中的關係概念來自數學中的集合理論,所以 UNION、INTERSECT 和 EXCEPT 分別來自集合論中的並集(∪\cup∪)、交集(∩\cap∩)和差集(∖\setminus∖)運算。須要注意的是,集合理論中的集合不容許存在重複的數據,可是 SQL 容許。所以,SQL 中的集合也被稱爲多重集合(multiset);多重集合與集合理論中的集合都是無序的,可是 SQL 能夠經過 ORDER BY 子句對查詢結果進行排序。
在 SQL 中,不只實體對象存儲在關係表中,對象之間的聯繫也存儲在關係表中。所以,當咱們想要獲取這些相關的數據時,須要使用到另外一個操做:鏈接查詢(JOIN)。
常見的 SQL鏈接查類型包括內鏈接、外鏈接、交叉鏈接等。其中,外鏈接又能夠分爲左外鏈接、右外鏈接以及全外鏈接。
內鏈接(Inner Join)返回兩個表中知足鏈接條件的數據,內鏈接的原理以下圖所示:
左外鏈接(Left Outer Join)返回左表中全部的數據;對於右表,返回知足鏈接條件的數據;若是沒有就返回空值。左外鏈接的原理以下圖所示:
右外鏈接(Right Outer Join)返回右表中全部的數據;對於左表,返回知足鏈接條件的數據,若是沒有就返回空值。右外鏈接與左外鏈接能夠互換,如下二者等價:
t1 RIGHT JOIN t2 t2 LEFT JOIN t1
全外鏈接(Full Outer Join)等價於左外鏈接加上右外鏈接,同時返回左表和右表中全部的數據;對於兩個表中不知足鏈接條件的數據返回空值。全外鏈接的原理以下圖所示:
交叉鏈接也稱爲笛卡爾積(Cartesian Product)。兩個表的交叉鏈接至關於一個表的全部行和另外一個表的全部行兩兩組合,結果的數量爲兩個表的行數相乘。交叉鏈接的原理以下圖所示:
其餘類型的鏈接還有半鏈接(SEMI JOIN)、反鏈接(ANTI JOIN)。
集合操做將兩個集合合併成一個更大或更小的集合;鏈接查詢將兩個集合轉換成一個更大或更小的集合,同時得到了一個更大的元素(更多的列)。不少時候集合操做均可以經過鏈接查詢來實現,例如:
SELECT department_id FROM departments UNION SELECT department_id FROM employees;
等價於:
SELECT COALESCE(d.department_id, e.department_id) FROM departments d FULL JOIN employees e ON (e.department_id = d.department_id);
咱們已經介紹了許多查詢的示例,接下來看看其餘的數據操做。
DMLDML 表示數據操做語言,也就是插入、更新和刪除。如下是一個插入語句示例:
CREATE TABLE test(id int); -- MySQL、SQL Server 等 INSERT INTO test(id) VALUES (1),(2),(3); -- Oracle INSERT INTO test(id) (SELECT 1 AS id FROM DUAL UNION ALL SELECT 2 FROM DUAL UNION ALL SELECT 3 FROM DUAL);
咱們經過一個 INSERT 語句插入了 3 條記錄,或者說是插入了一個包含 3 條記錄的關係表。由於,UNION ALL 返回的是一個關係表。VALUES 一樣是指定了一個關係表,在 SQL Server 和 PostgreSQL 中支持如下語句:
SELECT * FROM ( VALUES(1),(2),(3) ) test(id);
前面咱們已經說過,FROM 以後是一個關係表,因此這裏的 VALUES 也是同樣。因爲咱們常常插入單條記錄,並無意識到其實是以表爲單位進行操做。
一樣,UPDATE 和 DELETE 語句也都是以關係表爲單位的操做;只不過咱們習慣了說更新一行數據或者刪除幾條記