1. 什么是數據庫?
數據庫的作用就是為了保存數據。
程序運行時的數據會放在內存之中,但是只要程序結束或關機就會消失了。需要永久保存數據的話,就會存放在硬盤上。我們在編程基礎課程曾經學過,可以利用檔案來將數據放在硬盤上。不過如果數據又多又雜,存成檔案要怎么命名、檔案要用什么格式才方便之后查詢,這些都是問題。
數據庫就是為了解決這些問題而發明了,除了可以保存大量數據,數據庫還提供了方便的查詢(Query)機制,以及提供 CRUD(Create, Read, Update, Delete) 操作.
關系型數據庫
SQLite3 ; MySQL ; PostgreSQL ; Oracle ; MS SQL Server
2. 關系型數據庫的特性一: Schema
在關系型數據庫中,有一些共通的特性,第一個就是 Schema 綱要:
例如在這張 Table,有 id、name、capacity、user_id 等字段,其中每一行(row)就一筆數據(data)。
在excel表中,每一格都可以隨便你填什么,但是 Schema 綱要不一樣。我們需要定義數據型別(Data Type)。每個字段(Column)都要指定格式,只有符合格式才能存進數據庫。不同數據庫的數據類型大同小異,大體上都有:
字符串:varchar 或 text。varchar 默認是 255 字符、text 默認是 65535 字符。如果你要開一個字段來存長篇文章,text 可能會不夠存,需要在額外指定長度。
數字: Integer, Decimal, Float
Blob 二進制: 可以存放檔案。但是通常不建議把檔案直接塞數據庫,一來數據庫塞太大不容易備份和管理、二來沒有什么好處,因為你也沒辦法針對二進制檔案進行條件搜尋和過濾。人們對于讀檔案也有心理準備會比較慢。所以通常只會在數據庫里面紀錄檔案的 metadata 例如檔名、大小、MimeType 等等,而實際的檔案則放在檔案系統上,或是上傳到七?;駻WS S3等空間。
Boolean 布林
Date 日期
Time 時間
Datetime 日期時間
3. 關系型數據庫的特性二: SQL 標準語法
關系型數據庫都支援使用一種叫做 SQL (Structured Query Language) 的結構化查詢語言。我們會用這種語法來操作數據庫,例如:
INSERT INTO events VALUES ("RubyConf", 100);
# 這個 SQL 句會插入一筆數據到 events 表。
SELECT * FROM events;
# 這個 SQL 句告訴數據庫拿出 events 表的所有數據。
4. 關系型數據庫的特性三: ACID
關系型數據庫的另一個重要的特性是 ACID,也就是Atomicity, Consistency, Isolation, Durability。在解釋個別的意義前,我們先介紹一個關系型數據庫的功能,叫做 Transaction 事務。
請想像這樣的場景:當你再做銀行轉帳時,A 的馀額會減少、B 的馀額會增加,如果這是兩個 SQL 操作的話,我們如何能保證這兩個 SQL 操作必須是一起成功的?不能發生 A 錢變少了,但是 B 沒有收到錢的情況?;蚴强紤]一個更極端的場景,如果你和別人同時同一秒鐘互相轉帳,數據庫會不會算錯馀額?
要達成這種跨 Tables 多個 SQL 操作必須同時完成(或失敗)的需求,就必須用上 Transaction 事務。語法是用BEGIN; 和 COMMIT; 把 SQL 句包裹在一起,例如:
BEGIN;
INSERT INTO histories (user_id, amount) VALUES (1, -100);
INSERT INTO histories (user_id, amount) VALUES (2, 100);
UPDATE accounts SET balance=200 WHERE id=1;
UPDATE accounts SET balance=300 WHERE id=2;
COMMIT;
每個 SQL 句必須用分號 ; 代表結束
這樣 BEGIN; 和 COMMIT; 中間的所有 SQL 句,就會一起遞交給數據庫,要麻一起成功、要麻一起失敗。
ACID
ACID 其實就是在說明 Transactin 的能耐,以下取自 wikipedia:
Atomicity 原子性:一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
Consistency 一致性:在事務開始之前和事務結束以后,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。
Isolation 隔離性:數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(Serializable)。
Durability 持久性:事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
透過 Transactions 事務功能,關系型數據庫可以在多人連線同時執行多個 SQL 句的情況下,也可以保證數據最后的正確性。
5. SQL 語言: DDL
關系型數據庫使用 SQL(Structured Query Language) 語言,每個 SQL 句子叫做 SQL Query 或 SQL Statement。我們可以用 CLI 指令或 GUI 軟件,用 SQL 語言對數據庫進行查詢和操作。
SQL 分成 DDL 和 DML 兩種,都是用分號 ; 結尾。
Data Definition Language, DDL
如何告訴數據庫去定義 Schema 綱要? 也是使用 SQL 語法,這類型的 SQL 就做 DDL(Data Definition Language)
建立、刪除和更名數據庫
每家方法不太一樣,建議可以用 GUI 進行即可。PostgreSQL 和 MySQL 都是數據庫服務器,可以管理很多不同數據庫,例如你可以架很多 Rails 網站,但是只需要一個數據庫服務器,里面建立不同數據庫即可。
建立數據庫時,請注意選擇編碼(Encoding)。PostgrSQL 可用 utf-8、MySQL 可用 utf8mb4 編碼。
在 SQLite3 的話,直接在 Terminal 用 CLI 指令 sqlite3 your_db_name.db
就會打開(或產生)一個數據庫檔案。直接砍掉檔案就是刪除數據庫。
以下用 SQLite3 示范。
建立 Table
以下 SQL 會建立events
表,并新增三個字段 name, capacity 和 date。默認是字段允許 NULL,除非加上 NOT NULL。
CREATE TABLE events (name VARCHAR(50) NOT NULL, capacity INTEGER, date DATE);
ALTER TABLE persons RENAME TO people; # 改名 Table
ALTER TABLE people ADD COLUMN status VARCHAR(50); # 新增字串
DROP TABLE IF EXISTS people; # 刪除table
**Migration 機制**
數據庫 Schema 不是一成不變的,會隨著軟件變更升級也會有修改的需要。因此,在一些軟件中會實作一種叫做 Migration 的功能,透過 Schema Migration 紀錄目前的 schema 版本。開機的時候檢查目前程式的版本和數據庫里面的版本是否相同,不同的話,執行 Migration 更新 schema。這些 Migration 代碼也會放進版本控制系統 Git 里面,這樣整個團隊的開發者和不同服務器上,都可以利用 Migration 來一致管理 Schame。這個功能就是大家熟習的 Rails Migration。
<hr>
### 6. SQL 語言: DML
操作數據的 SQL 就是 DML(Data Manipulation Language),也就是做 CRUD 的操作。
INSERT INTO events (capacity, name) VALUES (200, "JSConf"); # SQL 會新增數據
INSERT INTO events (capacity, name) VALUES (300, "COSCUP"), (300, "OSDC.TW"); # 插入多筆
SELECT * FROM events; # 查詢全部events資料
SELECT name, capacity FROM events; # 查詢指定資料
UPDATE events SET capacity=10; # 修改SQL 數據
DELETE FROM events; # 刪除全部
DELETE FROM events WHERE name="RubyConf"; # 只刪除指定的
查有哪些 tables 和 columns
各家語法不一樣:
SQLite3: .tables
和.schema tablename
MySQL: show tables
和 describe tablename
PostgreSQL: \dt
和\d tablename
7. 數據庫規范化 Normalization
本質上就是 一對一,一對多,多對一(拆解成一對一與一對多)
8. 數據庫設計實務
Primary Key 主鍵
所謂的 Primary Key 主鍵就是可以唯一識別的字段,在 Rails 中會默認產生一個字段是 id。
如何選擇你的 Primary Key ?
最常見是自動遞增的整數(Auto incrementing Primary Key),這是 Rails 的默認方式,也是大家熟悉的 ID
UUID 通用唯一識別碼: 1. 分布式系統喜歡用 2. 或是當作 token URL 功能
Natural key (例如身分證號碼, ISBN, 國碼 ISO ALPHA-2) 等等,不過你需要真的確認不會重復,例如 ISBN 其實會重復的
Foreign Key (Reference Key) 外鍵
所謂的 Foreign Key 是指用來關聯一對多的字段字段,例如上述 registrations 表中的 user_id 和 event_id。外鍵的命名沒有特別規定,通常是 _id 結尾。
你不需要特別告訴數據庫這個是 foreign key,就可以當他是 foreign key 來使用。
9. Joining
SQL 查詢厲害的地方,就是可以同時關聯(Joining)多張表來進行復雜的查詢。讓我們先準備示范用的數據。
以下是 user 一對多 events 的情境,請執行 sqlite3 demo2.db
,并輸入以下 SQL 建立 tables 和數據:
CREATE TABLE events (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, capacity INTEGER, user_id INTEGER);
CREATE TABLE users (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT);
INSERT INTO users (name) VALUES ('ihower');
INSERT INTO users (name) VALUES ('john');
INSERT INTO users (name) VALUES ('roy');
INSERT INTO events (name, capacity, user_id) VALUES ('rubyconf',100, 1);
INSERT INTO events (name, capacity, user_id) VALUES ('jsconf', 200, 1);
INSERT INTO events (name, capacity, user_id) VALUES ('cssconf', 150, 2);
INSERT INTO events (name, capacity, user_id) VALUES ('htmlconf', 300, NULL);
跨 Tables 進行 Joining 查詢,常用的有 Inner Joining 和 Left Outer Joining 兩種:
Inner joining 合并兩張 tables,接不起來就不要:
撈出所有活動,以及該活動的主辦人資料:
SELECT * FROM events INNER JOIN users ON events.user_id = users.id;
或 SELECT * FROM events, users WHERE events.user_id = users.id;
Outer joining 合并兩張 tables,接不起就填 NULL:
撈出所有活動,以及該活動的主辦人資料(包括沒有主辦人的活動):
SELECT * FROM events LEFT OUTER JOIN users ON events.user_id = users.id;
AS 語法
因為有多張 tables 在 SQL 時,column 最好必須加上 table name 當作 prefix (特別是有重復的 column name 時,在 WHERE 條件里可能會無法判斷),而且可以加上別名 AS。
例如
SELECT events.id AS event_id, events.capacity AS ec, events.name FROM events INNER JOIN users ON events.user_id = users.id WHERE ec=100;
Joining 圖表
10. Functions
計算 Aggregations
數量
SELECT COUNT(*) AS event_count FROM events;
為何要 Joining
回頭想想看為什么需要 Joining 語法。SQL 的 Joining 語法是對新手比較困難的部分,沒辦法完全掌握是正常的。很多時候其實我們在 Rails 先將需要的數據通通撈出來,然后用 Ruby 進行過濾跟組合似乎也可以達成目標,為什么需要用到這些看起來很復雜的 SQL Joining 語法呢?
主要的原因還是查詢速度和需要的內存空間,數據庫是一套針對 SQL 優化非常快速的軟件,因此可以用遠比 Ruby 高效的方式來取出數據。更何況如果全部的數據都拿出來用 Ruby 處理,很可能內存也不夠。例如以下的問題:請回答去年第三季所有商品的銷售額,并根據分類計算總額。去年一整年的銷售可能多達上百萬筆,如果要逐筆撈出用 Ruby 處理,效能會非常低下。這時候就必須用 SQL 精準地撈出想要的數據才是可行的方式。
關系型數據庫原理-最詳講解
涉及到基礎的數據結構,算法,二叉樹等,篇幅過長,難度較高,后期再補
Mac平臺重新設置MySQL的root密碼
您是否忘記了Mac OS 的MySQL的root密碼? 通過以下4步就可重新設置新密碼:
- 停止
mysql server
. 通常是在 '系統偏好設置' > MySQL > 'Stop MySQL Server'
- 打開終端,輸入:
sudo /usr/local/mysql/bin/mysqld_safe --skip-grant-tables
- 打開另一個新終端,輸入:
sudo /usr/local/mysql/bin/mysql -u root # 分四次輸入
UPDATE mysql.user SET authentication_string=PASSWORD('新密碼') WHERE User='root';
FLUSH PRIVILEGES;
\q
- 重啟MySQL.
*以上方法針對 MySQL V5.7.9, 舊版的mysql請使用:UPDATE mysql.user SET Password=PASSWORD('新密碼') WHERE User='root';
有時候上述操作還會報錯:ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
這就蠻奇怪的,因為剛剛我就是輸入密碼進去的,原因未知,好的,那我只好再設置:
mysql> SET PASSWORD = PASSWORD('123456'); # 123456 是重置的新密碼