MySQL 簡介

戴均民 @ 中興大學資訊社

2014 Winter Olympic Games

country gold silver bronze total
Russia 13 11 9 33
United States 9 7 12 28
Norway 11 5 10 26
Canada 10 10 5 25
Netherlands 8 7 9 24

儲存資料

  • 以 table 為單位
  • 由 row 和 column 所組成
  • column 設定好後就不會輕易更改
  • row 可以無限增加

CREATE TABLE

建立表格

表格或欄位名稱

Table 和 Cloumn 的名稱允許以下格式

  • 大小寫英文字母
  • 底線 _ 和 $
  • Unicode
    U+0080 ~ U+FFFF
  • 建議只用 「小寫字母」 「底線」
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

主鍵 (Primary Key)

  • 每一筆資料必須是表格中的唯一值
  • 用來獨一無二地確認表格中某行資料
  • 可以普通欄位或是一個公式
  • 可以包含一或多個欄位
  • 不能為 NULL
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

AUTO_INCREMENT

  • 新增時沒有指定就自動遞增
  • 只能和主鍵一起使用
  • 只能用在正整數
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

NULL / NOT NULL

  • 設定欄位是否允許 NULL
  • NULL 值是 MySQL 的一個特殊的值,代表什麼都沒有
  • NULL 不好使用,所以通常建議把欄位設定為 NOT NULL
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

INDEX

  • 建立索引可加速搜尋
  • 如果有需要對某個欄位搜尋務必記得加上索引
  • 不要過於濫用
    建過多索引也會浪費效能
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

UNIQUE

  • 和 INDEX 差別在多了資料不能重複的限制 (NULL 不會算重複)
  • 主鍵和 UNIQUE 差別在 UNIQUE 可以存 NULL 值
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

FOREIGN KEY

  • 代表和另外一個表格的欄位有關聯
  • 兩個欄位的各項屬性必須一樣 (名字可以不同)
  • 只能有在另外一個表格中存在的資料
CREATE TABLE employee (
    employee_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
    last_name VARCHAR(30) NOT NULL,
    first_name VARCHAR(30) NOT NULL,
    email VARCHAR(100) NOT NULL,
    hire_date DATE NOT NULL,
    notes MEDIUMTEXT,
    PRIMARY KEY (employee_id),
    INDEX (last_name),
    UNIQUE (email)
)
ENGINE=InnoDB;

CREATE TABLE address (
    employee_id INTEGER UNSIGNED NOT NULL,
    address VARCHAR(50) NOT NULL,
    city VARCHAR(30) NOT NULL,
    state CHAR(2) NOT NULL,
    postcode CHAR(5) NOT NULL,
    FOREIGN KEY (employee_id)
    REFERENCES employee (employee_id)
)
ENGINE=InnoDB;

DATA TYPES

資料型態

整數

型態 Byte(s) 有號最小 有號最大 無號最小 無號最大
TINYINT 1 -128 127 0 255
SMALLINT 2 -32768 32767 0 65535
MEDIUMINT 3 -8388608 8388607 0 6777215
INTEGER 4 -2147483648 2147483647 0 4294967295
BIGINT 8 -9223372036854775808 9223372036854775807 0 18446744073709551615

浮點數

型態 Byte(s) 說明
FLOAT 4 能存 -3.402823466e+38 與 3.402823466e+38 之間的近似值
DOUBLE 8 能存 -1.7976931348623157e+308 與 1.7976931348623157e+308 之間的近似值
DECIMAL 不定 能存完全精準的數值,儲存在這個型態中的數值,不論是查詢或是運算,都不會有任何誤差。最大長度 65,最大小數長度 30。

CHAR

型態 Byte(s) 最大長度
CHAR(m) m × 字元集單一字元最大 byte(s) 255 chars
VARCHAR(m) 最大值 m × 字元集單一字元最大 byte(s) + 2 65535 chars
  • 適合建立索引
  • 適合儲存可見字元
  • 可以設定字元集
  • 建議設定字元集為 utf8mb4

BINARY

型態 Byte(s) 最大長度
BINARY(m) m 255 bytes
VARBINARY(m) 最大值 m + 2 65535 bytes
  • 適合建立索引
  • 適合儲存二進位資料
  • 不能設定字元集

TEXT

型態 Byte(s) 最大長度
TEXT 資料的 byte(s) 大小 + 2 65535 chars
TINYTEXT 資料的 byte(s) 大小 + 1 255 chars
MEDIUMTEXT 資料的 byte(s) 大小 + 3 16777215 chars
LONGTEXT 資料的 byte(s) 大小 + 4 4294967295 chars
  • 不適合建立索引
  • 適合儲存可見字元
  • 可以設定字元集
  • 建議設定字元集為 utf8mb4

BLOB

型態 Byte(s) 最大長度
BLOB 資料的 byte(s) 大小 + 2 65535 bytes
TINYBLOB 資料的 byte(s) 大小 + 1 255 bytes
MEDIUMBLOB 資料的 byte(s) 大小 + 3 16777215 bytes
LONGBLOB 資料的 byte(s) 大小 + 4 4294967295 bytes
  • 不適合建立索引
  • 適合儲存二進位資料
  • 不能設定字元集

其他字串型態

型態 Byte(s) 最大長度
ENUM 最大值 2 65535 values
SET 最大值 8 64 members

時間

型態 Byte(s) 最小值 最大值
DATETIME 8 1000-01-01 00:00:00 9999-12-31 23:59:59
TIMESTAMP 4 1970-01-01 00:00:01 UTC 2038-01-19 03:14:07 UTC
DATE 3 1000-01-01 9999-12-31
TIME 3 -838:59:59 838:59:59
YEAR 1 1901 2155

STORAGE ENGINES

儲存引擎

InnoDB 和 MyISAM 比較

InnoDB MyISAM
儲存上限 64TB 256TB
ACID
強制性 Foreign Key
索引
同時新增
強制性 UNIQUE
全文搜尋
資料鎖定 Row Table
Transactions
幾何地理資料類型
加密
資料庫同步
叢集

INSERT DATA

新增資料

INSERT

INSERT INTO employee
    (first_name, last_name, email, hire_date)
VALUES
    ('Nischal', 'Bhatia', 'nbhatia@example.com', '2014-12-15');
  • 省略 employee_id (AUTO_INCREMENT)
  • 省略 notes (允許 NULL)
  • 字串型態的資料要用單引號或雙引號均可
    • 建議使用單引號
    • 使用反斜線 \ 作為跳脫字元
SELECT * FROM employee;

INSERT (省略欄位名稱)

INSERT INTO employee VALUES
    (NULL, 'Bhatia', 'Nischal', 'nbhatia@example.com', '2014-12-15', NULL);
  • 不可省略欄位
  • 欄位順序必須和 CREATE 時一樣

RETRIEVING DATA

取得資料

sakila database

  • 這是 MySQL 提供的範例資料庫之一
  • 下載網址安裝教學
  • Unix-like 系統建議下載 tgz
  • Windows 系統建議下載 zip
  • 先匯入 sakila-schema.sql
  • 再匯入 sakila-data.sql
USE sakila;

SOURCE C:/temp/sakila-db/sakila-schema.sql;
SOURCE C:/temp/sakila-db/sakila-data.sql;

SELECT 語法

SELECT last_name, first_name
FROM actor;

ORDER

SELECT last_name, first_name
FROM actor
ORDER BY last_name;

多個 ORDER

SELECT last_name, first_name
FROM actor
ORDER BY last_name ASC, first_name DESC;

LIMIT

SELECT last_name, first_name
FROM actor
ORDER BY last_name ASC, first_name DESC
LIMIT 5;

SELECT 語法順序

以上的 SELECT 語法順序是很重要的,如果不確定該怎麼下指令,可以參考官方文件

WHERE

過濾資料

WHERE 語法

SELECT title, length, rating
FROM film
WHERE length < 60
ORDER BY title;

AND

SELECT title, length, rating
FROM film
WHERE length >= 60 AND length <= 120
ORDER BY title;

OR

SELECT title, length, rating
FROM film
WHERE length = 60 OR length = 120 OR length = 180
ORDER BY title;

LIKE

SELECT title, length, rating
FROM film
WHERE title LIKE '%GAME%'
ORDER BY title;

WHERE 可用的條件式

運算子 說明
A = B 當兩者相等時成立
A < B 當 A 小於 B 時成立
A <= B 當 A 不大於 B 時成立
A > B 當 A 大於 B 時成立
A >= B 當 A 不小於 B 時成立
A != B 或 A <> B 當兩者不等時成立
A IS NULL 當 A 是 NULL 時成立
A LIKE 'PATTERN%' 當 A 匹配這個 Pattern 時成立(萬用字元是 % 和 _)
A REGEXP '/EXPRESSION/' 當 A 匹配這個正規表示法時成立
A BETWEEN B AND C 當 A >= B 且 A <= C 時成立
A IN (B,C,D) 當 A 是 B, C, D 其中之一時成立

AGGREGATE FUNCTIONS AND GROUPING

彙總函式與集合

MAX

SELECT MAX(amount)
FROM payment;

COUNT

SELECT COUNT(payment_id)
FROM payment;

SUM AND GROUP

SELECT customer_id, SUM(amount) AS amt
FROM payment
GROUP BY customer_id
ORDER BY amt DESC
LIMIT 10;

UPDATING DATA

更新資料

UPDATE

SELECT customer_id, first_name, last_name
FROM customer
WHERE first_name = 'Courtney' AND last_name = 'Day';
UPDATE customer
SET last_name = 'DAY-WEBB'
WHERE customer_id = 245;
SELECT customer_id, first_name, last_name
FROM customer
WHERE customer_id = 245;

DELETING DATA

刪除資料

SOFT DELETE

  • 多一個欄位紀錄刪除狀態
  • 刪除時使用UPDATE更新該欄位
  • 避免真的刪除資料
  • 使用索引及WHERE找出未刪除資料
  • 使用temporal triggers定期刪除

DELETE

  • 使用刪除語法務必慎重
  • 建議先用SELECT確認WHERE沒設定錯
    • 忘記加WHERE可是會災情慘重的
DELETE FROM customer WHERE customer_id = 245;

FORRIGN KEY AND DELETE

ERROR 1451 (23000): Cannot delete or update a parent
row: a foreign key constraint fails (`sakila`.`payment`,
CONSTRAINT `fk_payment_customer` FOREIGN KEY (`customer_id`)
REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE)
  • FOREIGN KEY 預設在有相依資料時無法刪除
  • 使用 ON DELETE CASCADE (請自行 Google)
  • 先將相依資料刪除
DELETE FROM payment WHERE customer_id = 245;
DELETE FROM rental WHERE customer_id = 245;
DELETE FROM customer WHERE customer_id = 245;

TRANSACTIONS

事務

今天 A 要轉帳給 B

  1. 檢查 A 的餘額
  2. 從 A 的帳戶扣錢
  3. 從 B 的帳戶加錢

今天 A 要轉帳給 B

  1. 檢查 A 的餘額
  2. 從 A 的帳戶扣錢
  3. 從 B 的帳戶加錢

A 同時收到了 X 和 Y 轉帳

X1. 取得餘額 A

X2. 將 A+X 寫回去

Y1. 取得餘額 A + X

Y2. 將 A+X+Y 寫回去

A 同時收到了 X 和 Y 轉帳

X1. 取得餘額 A

Y1. 取得餘額 A

X2. 將 A+X 寫回去

Y2. 將 A+Y 寫回去

今天 A 要轉帳給 B

START TRANSACTION;

SELECT * FROM account WHERE name IN ('A', 'B') FOR UPDATE;

-- 如果餘額不足就需要透過程式執行 ROLLBACK
ROLLBACK;
START TRANSACTION;

SELECT * FROM account WHERE name IN ('A', 'B') FOR UPDATE;

-- 如果餘額足夠就開始進行轉帳
UPDATE account SET balance = balance - 10 WHERE name = 'A';
UPDATE account SET balance = balance + 10 WHERE name = 'B';

COMMIT;

資料庫管理系統DBMS)在寫入/更新資料的過程中,為保證事務transaction)是正確可靠的,所必須具備的四個特性

原子性 一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
一致性 在事務開始之前和事務結束以後,資料庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及後續資料庫可以自發性地完成預定的工作。
隔離性 當兩個或者多個事務並發訪問(此處訪問指查詢和修改的操作)資料庫的同一數據時所表現出的相互關係。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。
持久性 在事務完成以後,該事務對資料庫所作的更改便持久地保存在資料庫之中,並且是完全的。

JOINING TABLE

連接資料表

沒有 JOIN 的時候

如何找出前五名演出最多電影的演員?

SELECT actor_id, COUNT(actor_id) AS appearances
FROM film_actor
GROUP BY actor_id
ORDER BY appearances DESC
LIMIT 5;
SELECT actor_id, first_name, last_name
FROM actor
WHERE actor_id IN (107, 102, 198, 181, 23);

有 JOIN 的時候

如何找出前五名演出最多電影的演員?

SELECT
    a.first_name, a.last_name,
    COUNT(fa.film_id) AS appearance_count
FROM
    film_actor AS fa
    JOIN actor AS a ON a.actor_id = fa.actor_id
GROUP BY
    fa.actor_id
ORDER BY
    appearance_count DESC
LIMIT 5;

TYPES OF JOINS

JOIN 的種類

也想要計算沒有演出電影的演員時

與 LEFT JOIN 功能相同

發問時間

MySQL

By 戴均民

MySQL

  • 877