天天看點

「SQL資料分析系列」1. SQL背景知識介紹

「SQL資料分析系列」1. SQL背景知識介紹

來源 |  Learning SQL Generate, Manipulate, and Retrieve Data, Third Edition

作者 | Alan Beaulieu

譯者 | Liangchu

校對 | gongyouliu

編輯 | auroral-L

全文共8325字,預計閱讀時間50分鐘。

第一章  SQL背景知識

一、資料庫簡介

      1.非關系型資料庫

      2.關系模型 

      3.一些術語

二、什麼是SQL

      1.SQL語句的類别

      2.SQL:非過程化語句

      3.SQL示例

三、什麼是MySQL

四、不隻是SQL

五、内容提要

在我們開始學習本書的内容之前,先回顧一下資料庫技術的曆史,以便更好地了解關系資料庫和SQL語言是如何演變的。是以,我會先介紹一些基本的資料庫概念,看看計算機資料存儲和檢索的發展史。

注意:對于那些急于開始寫查詢語句的讀者,可以直接跳到第三章,但我建議在此之後還是再回到前兩章看看,以便更好地了解SQL語言的曆史和實用性。

一、資料庫簡介

資料庫指的是一組相關資訊的集合。例如,電話簿可以被看做一個資料庫,其中包含某地區所有居民的姓名、電話号碼和位址等資訊。雖然電話簿是一個十分普及和常用的資料庫,但是它存在以下問題:

• 查找一個人的電話号碼可能很費時,特别是電話簿包含大量條目時;

• 電話簿隻按姓/名來索引,是以查找居住在特定位址的人的姓名的時候,雖然理論上可行,但對該資料庫來說并不實用;

• 從列印電話簿的那一刻起,随着居民在該地區的流動、更改電話号碼或遷移到同一地區内的另一個地方等動作的發生,電話簿上的資訊變得越來越不準确。

電話簿的上述缺點也在任何手動編制的資料存儲系統上存在,例如存儲在檔案櫃中的病曆等。由于這些紙質資料庫很繁瑣,是以最初開發的一些計算機應用程式就是資料庫系統,它通過計算機來存儲和檢索資料。因為資料庫系統以電子方式而不是紙質方式存儲資料,是以它能夠更快速地檢索資料、以多種方式索引資料并向使用者社群提供最新資訊。

早期的資料庫系統管理存儲在錄音帶上的資料。由于錄音帶的數量通常要遠遠超過錄音帶讀取器,是以技術人員的任務就是在需要特定資料時手動裝卸錄音帶。而且由于那個時代的計算機記憶體非常小,通常情況下對于同一資料的并發請求需要多次讀取錄音帶上的資料。盡管這些資料庫系統比紙質資料庫有了顯著的改進,但它們與當今技術所能實作的相差甚遠。(現代資料庫系統可以管理數PB(拍位元組(Petabytes),計算機存儲容量機關,也常用PB來表示。1PB=1024TB==2^50位元組)的資料,由伺服器叢集通路,每個伺服器在高速記憶體中緩存數萬GB(gb也叫吉位元組(GB、Gigabyte,在中國又被稱為吉咖位元組或京位元組或十億位元組或戟),常簡寫為G,是一種十進制的資訊計量機關。)的資料,不過這些内容可能有些超前了)

1.非關系型資料庫 

注意:本節包含一些早期非關系型資料庫系統的背景資訊。對于渴望深入學習SQL的讀者,可以跳過這幾頁進入下一部分。

在計算機化資料庫系統的前幾十年中,資料以各種方式存儲和呈現給使用者。例如,在層次資料庫系統中,資料表示為一個或多個樹結構。如圖(1-1)顯示了George Blake和Sue Smith銀行賬戶的相關資料,它們通過樹結構表示:

「SQL資料分析系列」1. SQL背景知識介紹

George和Sue都有自己的樹結構,包含他們的賬戶以及交易資訊。層次資料庫系統提供了定位特定客戶樹的工具,并能周遊該樹以找到所需的帳戶或交易資料。樹中的每個節點可以有零個或一個父節點,以及零個、一個或多個子節點。這種配置被稱作單根層次結構(single-parent hierarchy)。

另一種管理資料的方法稱為網狀資料庫系統,它表現為多個記錄集合,集合之間通過連接配接定義不同記錄之間的關系。如圖(1-2)顯示了該系統中George和Sue的賬戶資訊:

「SQL資料分析系列」1. SQL背景知識介紹

為了找到轉移到Sue的貨币市場賬戶(money market account,MoneyMkt)的交易記錄,需要執行以下步驟:

1.查找Sue Smith的客戶記錄;

2.根據Sue Smith的客戶記錄連結到她的賬戶清單;

3.周遊賬戶鍊,直到找到貨币市場賬戶;

4.通過連結從貨币市場記錄找到其交易清單。

上圖(1-2)最右邊的一組産品記錄(Products)展示了網狀資料庫系統的一個有趣特性。請注意,每個産品記錄(Checking、Savings等)都指向該産品類型的帳戶記錄清單。是以,可以從多個位置(客戶記錄和産品記錄)通路帳戶記錄,這使得網狀資料庫具備多層次結構。

層次資料庫系統和網狀資料庫系統如今仍然存在,不過通常在大型機領域中使用。此外,層次資料庫系統在目錄服務領域有了新的應用,比如微軟的Active Directory和開源Apache目錄伺服器。然而,從20世紀70年代開始,一種新的表示資料的方法開始生根發芽,這種方法更加嚴謹,并且易于了解和實作。

2.關系模型

1970年,IBM研究實驗室的E.F.Codd博士發表了一篇題為“大型共享資料庫的資料關系模型(A Relational Model of Data for Large Shared Data Banks)”的論文,提出将資料表示為一組表。備援資料用于連結不同表中的記錄,而不是使用指針在相關實體之間導航。如圖(1-3)顯示了George和Sue的賬戶資訊在這種情況下的表示方式:

「SQL資料分析系列」1. SQL背景知識介紹

上圖(1-3)中的四個表代表了迄今為止讨論的四個實體:Customer、Product、Account和Transaction。縱觀上圖(1-3)Customer表的頂部,你可以看到三列:cust_id(包含客戶的ID号)、fname(包含客戶的名字)和lname(包含客戶的姓氏)。繼續向下看Customer表,可以看到兩個記錄行,一行包含George Blake的資料,另一行包含Sue Smith的資料。一個表能包含的最大列數因伺服器而異,但這個資料通常足夠大,是以不用擔心(比如,Microsoft 的SQL Server允許每個表最多包含1024列)。一個表可能包含的行數與其說是受到資料庫伺服器的限制,不如說是受到實體限制(即有多少磁盤驅動器空間可用)和可維護性(即在表中記錄數量達到怎樣的規模後仍然可以保持易用性)的問題。

關系資料庫中的每個表都包含一項作為每行唯一辨別的資訊(稱為主鍵),它與其他資訊一起完整描述該條目。再看Customer表,cust_id列為每個客戶儲存了不同的編号;例如,George Blake可以由顧客ID 1來唯一辨別,這個辨別符永遠不會被配置設定給其他客戶,是以定位Customer表中George Blake的資料時并不需要其他的資訊。

注意:每個資料庫伺服器都提供了一種機制,用于生成唯一的數字以用作主鍵值,是以你不必操心哪些數字已經被賦予為主鍵。

雖然我也可能選擇使用fname和lname列的組合作為主鍵(由兩個或更多列組成的主鍵稱為複合主鍵),但實際上很容易出現兩個或多個姓名都相同的人都在銀行擁有帳戶的情況。是以,選擇Customer表中的cust_id列作為主鍵是更合适的。

注意:在本例中,選擇fname/lname作為主鍵将被稱為自然主鍵,而選擇cust_id将被稱為代理鍵( surrogate key)。到底選哪一種鍵取決于資料庫設計者,但在這種本例中的選擇是顯而易見的,因為一個人的姓可能會更改(例如,當一個人使用配偶的姓時),但是主鍵一旦配置設定了值就不能更改。

有些表還包含用于導航到另一個表的資訊,這就是前面提到的“備援資料”。例如,Account表包括一個名為cust_id的列,該列包含使用該帳戶的客戶的唯一辨別,以及一個名為product_id的列,該列包含帳戶所關聯産品的唯一辨別,這些列稱為外鍵,它們的作用與賬戶資訊網絡結構中各實體之間的連線相同。如果你正在檢視一個特定的帳戶記錄,并且希望了解有關該客戶的更多資訊,你可以擷取cust_id列的值,并使用它在Customer表中查找相應的行(在關系資料庫術語中,這個過程稱為連接配接(join),會在第三章介紹,并在第五章和第十章進行深入探讨)。

多次存儲相同的資料似乎很浪費,但是某些情況下使用備援資料可以更清楚地展現關系模型。例如,Account表包含一列作為開戶客戶的唯一辨別符是合适的,但是如果在Account表中包含該客戶的名字和姓氏就不合适了。如果客戶要更改其姓名,你需要確定資料庫中隻有一個地方儲存客戶的姓名;否則,資料可能會在一個地方更改,而不會在另一個地方更改,進而導緻資料庫中資料的不可靠(資料的不一緻)。此資料的适當位置是Customer表,其他表中隻應包括cust_id值。一個列包含多條資訊也是不合适的,例如使用name列同時包含使用者的姓和名,或者使用address列包含街道、城市、州和郵政編碼資訊。優化資料庫設計以確定每個獨立資訊隻存放在一個位置(外鍵除外)的過程稱為規範化。

讓我們回到上圖(1-3)中的四個表,你可能想知道如何使用這些表來查找George Blake在checking上的賬戶交易。首先,在Customer表中找到George Blake的唯一辨別符(主鍵)。然後,在Account表中找到一行,該行的cust_id列包含George的唯一辨別符,其product_id列與Product表中name列等于“Checking”的行比對。最後,通過比對Account表的唯一辨別account_id列來定位Transaction表中對應的行。這聽起來可能很複雜,但其實在SQL語言中,用一個指令就可以完成這些任務了,稍後你将看到這一點。

3.一些術語

我在前面的章節中介紹了一些新的術語,是以也許是時候給出一些正式的定義了。下表(1-1)顯示了本書餘下部分使用術語的定義:

「SQL資料分析系列」1. SQL背景知識介紹

二、什麼是SQL

根據Codd對關系模型的定義,他提出了一種稱為DSL/Alpha的語言來處理關系表中的資料。Codd的論文發表後不久,IBM建立一個小組根據Codd的想法建構了一個原型。這個小組建立了一個簡化的DSL/Alpha版本,他們稱之為SQUARE。對SQUARE的改進産生了一種稱為SEQUEL的語言,它最終被縮短為SQL。雖然SQL最初是一種用于操控關系資料庫中的資料的語言,但在本書的最後你會知道,它已經發展成了一種跨各種資料庫技術操作資料的語言。

距今為止,SQL已經有40多年的曆史了,在這一期間它發生了巨大的變化。在20世紀80年代中期,美國國家标準協會(American National Standards Institute,ANSI)開始為SQL語言制定第一個标準,該标準于1986年釋出,随後對其不斷改進,在1989年、1992年、1999年、2003年、2006年、2008年、2011年和2016年釋出了一系列新的SQL标準。除了對核心語言的改進之外,SQL語言還添加了新的特性,以結合面向對象等其他功能。後來的标準側重于相關技術的內建,如可擴充标記語言( extensible markup language,XML)和JavaScript對象表示法(JavaScript object notation,JSON)。

SQL與關系模型密切相關,因為SQL查詢的結果是一個表(在本文中也稱為結果集)。是以,隻需存儲查詢的結果集,就可以在關系資料庫中建立一個新的固定表。同樣地,一個查詢可以使用固定表和來自其他查詢的結果集作為輸入(我們将在第九章對此進行詳細探讨)。

最後要注意一點:SQL不是任何短語的縮寫(盡管許多人堅持認為它代表“結構化查詢語言Structured Query Language”)。當提到該語言時,可以使用單獨的字母(即S.Q.L)或使用sequel一詞。

1.SQL語句的類别

SQL語言分為幾個不同的部分:我們在本書中探讨的部分包括SQL案例(schema)語句,用于定義存儲在資料庫中的資料結構;SQL資料(data)語句,用于操作SQL案例語句所定義的資料結構;以及SQL事務(transaction)語句,用于開始、結束和復原事務(将在第十二章介紹)。比如,要在資料庫中建立一個新表,可以使用SQL 案例語句create table,而要在新表中插入資料則需要SQL資料語句insert。

為了讓你了解這些句子是怎樣的,下面給出了一個SQL案例語句,它建立了一個名為corporation的表:

CREATE TABLE corporation 
(corp_id SMALLINT, 
 name VARCHAR(30), 
 CONSTRAINT pk_corporation PRIMARY KEY (corp_id) 
);
           

該語句建立一個包含兩列的表,列為corp_id和name,其中corp_id列被設定為表的主鍵。在第二章中,我們将探讨此語句更多的細節,比如MySQL提供的各種不同的資料類型。下面是一條SQL資料語句,它将向corporation表插入一行關于Acme Paper Corporation的資料:

INSERT INTO corporation (corp_id, name) 
VALUES (27, 'Acme Paper Corporation');
           

此語句向corporation表中添加一行資料,其中corp_id列的值為27,name列的值為Acme Paper Corporation。

最後,這裡給出一條簡單的select語句來檢索剛剛建立的資料:

mysql< SELECT name
 -> FROM corporation
 -> WHERE corp_id = 27;
+------------------------+
| name |
+------------------------+
| Acme Paper Corporation |
+------------------------+
           

通過SQL案例語句建立的所有資料庫元素都存儲在一組稱為資料字典的特殊表中。這種“關于資料庫的資料”統稱為中繼資料,我們将在第十五章中讨論它。與使用者自己建立的表一樣,資料字典表也可以通過select語句來查詢,進而允許在運作時檢視部署在資料庫中的目前資料結構。例如,如果你被要求寫一份報告來顯示上個月建立的新帳戶,那麼你可以寫死account表中的各個列名,也可以查詢資料字典以确定目前列集,并在每次運作的時候動态生成報表。

本書的大部分内容涉及SQL語言的資料部分,其中包括select、update、insert和delete指令。SQL案例語句在第二章中示範,它将引導你完成一些簡單表的設計和建立。一般來說,除了文法之外,SQL案例語句不需要太多的研究,而SQL資料語句雖然數量很少,卻是非常值得研究的。是以,盡管我試圖介紹很多SQL案例語句,但本書的大部分章節都會側重于SQL資料語句。

2.SQL:非過程化語句

如果你以前使用過程式設計語言,那麼你會習慣于定義變量和資料結構、使用條件邏輯(即if-then-else)和循環結構(即do-while-end),并将代碼分解為可重用的小片段(即對象、函數、過程)。你的代碼會被傳遞給編譯器,給出的可執行檔案能夠精确地(但其實不是總是精确的)符合你程式設計的預期。無論你使用的是Java、Python、Scala還是其他一些過程化語言,你都可以完全控制程式的功能。

注意:過程化語言定義了期望的結果和生成結果的機制或過程。非過程化語言也定義了期望的結果,但是生成結果的過程留給了外部代理來定義。

然而使用SQL需要放棄一些對過程的控制,因為SQL語句定義了必要的輸入和輸出,而語句的執行方式傳遞給資料庫引擎的一個元件——優化器(optimizer)。優化器的任務是檢視SQL語句,并考慮表的配置方式和有無索引可用等,以确定最有效的執行路徑(當然,并不總是最有效的)。大多數資料庫引擎都允許通過指定優化器選項來影響優化器的決策,例如建議使用特定的索引等。然而,大多數SQL使用者永遠都不需要考慮得這麼複雜,而是會将它留給資料庫管理者或性能調優專家來處理。

是以隻使用SQL是無法編寫完整的應用程式的,除非是一些操作某些資料的簡單腳本,否則一般都需要将SQL與你喜歡用的程式設計語言相內建。一些資料庫供應商已經做到了這一點,例如Oracle的PL/SQL語言、MySQL的存儲過程語言,還有Microsoft的Transact-SQL語言。使用這些語言的時候,SQL資料語句是該語言文法的一部分,能夠将資料庫查詢與過程化指令無縫內建。但是,如果你使用的是非特定于資料庫的語言(如Java或Python),則需要使用一些由資料庫供應商提供的工具包/API來執行SQL語句。有些工具包是由資料庫廠商提供的,而另一些則是由第三方廠商或開放源代碼提供者所建立。下表(1-2)顯示了将SQL內建到特定語言中的一些可用選項:

「SQL資料分析系列」1. SQL背景知識介紹

倘若你隻需要以互動方式執行SQL指令,那麼每個資料庫開發商至少都會提供一個簡單的指令行工具,用于将SQL指令送出到資料庫引擎并檢查結果。大多數開發商也提供了圖形化工具,包括一個顯示SQL指令的視窗和另一個顯示SQL指令結果的視窗。此外,還有第三方工具,如SQuirrel,它通過JDBC連接配接到許多不同的資料庫伺服器。由于本書中的示例是針對MySQL資料庫執行的,是以我使用MySQL安裝檔案中包含的mysql指令行工具來運作示例并格式化結果。

3.SQL示例

在本章前面,我承諾過會展示一個傳回George Blake的checking賬戶上所有交易的SQL語句,廢話少說,語句和查詢結果如下:

SELECT t.txn_id, t.txn_type_cd, t.txn_date, t.amount 
FROM individual i 
 INNER JOIN account a ON i.cust_id = a.cust_id 
 INNER JOIN product p ON p.product_cd = a.product_cd 
 INNER JOIN transaction t ON t.account_id = a.account_id 
WHERE i.fname = 'George' AND i.lname = 'Blake' 
 AND p.name = 'checking account';
+--------+-------------+---------------------+--------+
| txn_id | txn_type_cd | txn_date | amount |
+--------+-------------+---------------------+--------+
| 11 | DBT | 2008-01-05 00:00:00 | 100.00 |
+--------+-------------+---------------------+--------+
1 row in set (0.00 sec)
           

這裡就簡單地分析一下該語句:該查詢在individual表中查找姓名為George Blake的行,以及查詢在product表中賬戶名為checking account的行,并通過account表将它們關聯起來,然後傳回transaction表中所有送出到該賬戶上面的交易資訊内容,分為四列顯示。

如果你碰巧知道George Blake的客戶ID為8,并且checking賬戶的指定代碼為“CHK”,那麼你隻需在account表中根據客戶ID查找George Blake的checking賬戶,并使用賬戶ID查找相應的交易:

SELECT t.txn_id, t.txn_type_cd, t.txn_date, t.amount 
FROM account a 
 INNER JOIN transaction t ON t.account_id = a.account_id 
WHERE a.cust_id = 8 AND a.product_cd = 'CHK';
           

在接下來的章節中,我将介紹這些查詢中的所有概念(并且還會涉及更多概念),但我至少在這裡想展示一下它們的大緻結構。

前面的查詢包含三個不同的子句:select、from和where。幾乎你遇到的每個查詢都将至少包含這三個子句,當然還有幾個子句可以用于更加特定的查詢。三個字句的作用如下:

SELECT /* one or more things */ ...
FROM /* one or more places */ ...
WHERE /* one or more conditions apply */ ...
           

注意:大多數SQL實作都将标記之間的所有文本視為注釋。

構造查詢時,你首先要做的通常是确定需要哪些表,然後将它們添加到from子句中。接下來,你需要向where子句添加查詢條件,以便從這些表中篩選出你不感興趣的資料。最後,你要确定需要檢索不同表中的哪些列,并将它們添加到select子句中。下面是一個簡單的示例,該例子展示了如何找到所有姓為“Smith”的客戶:

SELECT cust_id, fname 
FROM individual 
WHERE lname = 'Smith';
           

此查詢在individual表中搜尋lname列與字元串“Smith”比對的所有行,并從這些行傳回cust_id和fname列。

除了查詢資料庫外,可能還需要插入和更新資料庫中的資料。下面是一個簡單的示例,說明如何在product表中插入新行:

INSERT INTO product (product_cd, name) 
VALUES ('CD', 'Certificate of Depysit')
           

啊咧,看來你把“Deposit”拼錯了,不過沒關系,你可以使用update語句:

UPDATE product 
SET name = 'Certificate of Deposit' 
WHERE product_cd = 'CD';
           

注意,update語句和select語句一樣包含where子句,這是因為update語句必須識别要修改的行。在本例中,指定隻修改product_cd列與字元串“CD”比對的行。由于product_cd列是product表的主鍵,是以應該期望update語句隻修改一行(如果表中不存在該值,則為零行)。每當你執行SQL data語句時,你都會收到來自資料庫引擎的回報,顯示其中有多少行受本次執行語句的影響。如果你使用的是互動式工具,如前面提到的mysql指令行工具,那麼你将收到以下關于操作影響行數的回報:

• 由select語句傳回

• 由insert語句建立

• 由update語句修改

• 由delete語句删除

如果你将過程化語言與前面提到的某個工具箱一起使用,那麼該工具箱将包含一個調用,以便在執行SQL資料語句後獲得此資訊。一般來說,最好是檢查此資訊以確定語句沒有執行意外操作(比如忘記在delete語句中編寫where子句,導緻删除表中的所有行)。

三、什麼是MySQL

關系資料庫已經商業化三十多年了,幾種最成熟和流行的商業産品包括:

• Oracle公司的Oracle Database

• Microsoft的SQL Server

• IBM提供的DB2 Universal Database

所有這些資料庫伺服器都執行大緻相同的操作,盡管其中的一些更适合運作大容量和高吞吐量的資料庫,而另一些更擅長處理對象、大檔案或XML文檔等。此外,所有這些伺服器都遵從最新的ANSI SQL标準。這是一件好事,我将重點向你展示如何編寫标準SQL語句,以便不進行或者隻進行少量修改就可以在這些平台上運作。

随着商業資料庫伺服器的出現,在過去的二十年中,開源社群正在為建立一個可行的替代案例而努力。最常用的兩個開源資料庫伺服器是PostgreSQL和MySQL。MySQL伺服器是免費的,并且非常便于下載下傳和安裝。基于這些原因,本書的所有示例都将在MySQL(8.0版)運作,并使用mysql指令行工具格式化查詢結果。即便你使用的是另外一種資料庫并且也沒打算使用MySQL,我還是建議你安裝最新的MySQL伺服器,加載示例模式和資料,并嘗試使用本書中的資料和示例。

但是,還請牢牢記住以下告誡:

本書并不是一本關于MySQL的SQL實作教程。

相反,本書的目的是教你如何編寫SQL語句,這些語句将在MySQL上運作而不做任何修改,并且在做很少或根本不做任何修改的情況下移植到Oracle Database、DB2和SQL Server上。

四、不隻是SQL

在本書出版第二版和第三版的十年間,資料庫領域内發生了很多事情。盡管關系型資料庫現在還在大量使用,并且也将持續一段時間,但是新的資料庫技術已經出現并滿足了亞馬遜和谷歌等公司的需求。這些技術包括Hadoop、Spark、NoSQL和NewSQL,它們是分布式、可擴充的系統,通常部署在商品伺服器叢集上。雖然詳細探讨這些技術并不在本書的範圍之内,但是它們卻和關系資料庫有一些共同點:SQL。

由于組織經常使用多種技術存儲資料,是以需要從特定的資料庫伺服器中“拔出”SQL,并提供一種可以跨多個資料庫的服務。例如,一份報表可能需要将存儲在Oracle、Hadoop、JSON檔案、CSV檔案和Unix日志檔案中的資料合并在一起。新一代的工具已經被用來應對這類挑戰,其中最有前途的是Apache Drill,它是一個開源的查詢引擎,允許使用者編寫查詢來通路存儲在大多數資料庫或檔案系統中的資料。我們将在第十八章中探讨Apache Drill。

五、内容提要

接下來四章的主要目标是介紹SQL資料語句,并且會特别強調select語句的三個主要子句。此外,你将看到許多使用Sakila模式的示例(将在下一章中介紹),本書的所有示例都圍繞它展開。

我希望通過熟悉單個資料庫,讓你可以了解示例的關鍵之處,而不必每次都停下來了解所使用的表。如果你覺得使用同一組表太無聊了,那也可以随意使用其他表來擴充示例資料庫,或者建立自己的資料庫來進行實驗。

在幫你牢固地掌握基礎了知識之後,剩下的章節将深入探讨其他概念,其中大多數概念是互相獨立的。是以,如果你在學習這些章節的過程中感到困惑,也可以繼續學習後面的内容,稍後再重溫這一章的内容。當你讀完這本書并學習了所有的示例之後,你很快就能成為一名經驗豐富的SQL實踐者了。

對于有興趣了解更多關系資料庫、計算機資料庫系統的曆史或SQL語言的讀者,下面給出了一些值得參考的資源

• Database in Depth: Relational Theory for Practitioners by C. J. Date (O’Reilly)

• An Introduction to Database Systems, Eighth Edition, by C. J. Date (Addison-Wesley)

• The Database Relational Model: A Retrospective Review and Analysis, by C. J. Date (Addison-Wesley)

• Wikipedia subarticle on definition of “Database Management System”

「SQL資料分析系列」1. SQL背景知識介紹
「SQL資料分析系列」1. SQL背景知識介紹