這一份筆記以 《MySQL必知必會》爲基礎,按照我的需求持續補充。php
完善中...不只限於該入門書上的知識html
因爲原文是在Typora寫的, 所以部分圖片可能會沒有上傳上來, 有空後續會補上.mysql
最後修改時間: 2019年10月23日15:38:40git
MySQL 的兩種發音:正則表達式
sequel
算法
['siːkw(ə)l]
數據庫中的 schema : 關於數據庫和表的佈局及特性的信息sql
有時,schema 用做數據庫的同義詞。遺憾的是,schema 的含義一般在上下文中並非很清晰。
主鍵(primary key): 一列(或一組列), 其值能惟一區分表中每一行。shell
子句(clause) SQL語句由子句構成,有些子句是必需的,而有的是可選的。數據庫
子句的例子有 SELECT 語句的 FROM 子句,
MariaDB 與 MySQL 版本替代:ubuntu
下載對應的yum倉庫: https://dev.mysql.com/downloa...
# RHEL6 wget https://dev.mysql.com/get/mysql80-community-release-el6-3.noarch.rpm # RHEL7 wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm #### 如下以 RHEL6(CentOS) 爲例 #### #### CentOS 7 一些命令不大同樣, eg. service, chkconfig #### # 安裝yum源 yum localinstall mysql80-community-release-el6-3.noarch.rpm # 確認是否已成功安裝 yum repolist enabled | grep "mysql.*-community.*" # 因爲默認指定最新的版本(MySQL 8.0), 所以須要手動指定咱們想安裝的版本 (MySQL 5.7) # 查看MySQL Yum存儲庫中的全部子存儲庫及其狀態 yum repolist all | grep mysql # 禁止8.0系列的子存儲庫 yum-config-manager --disable mysql80-community # 啓用5.7系列的子存儲庫 yum-config-manager --enable mysql57-community # 或在安裝時指定倉庫 --disablerepo="*" --enablerepo="mysql57-community" # 安裝MySQL # 會自動安裝依賴: mysql-community-client, mysql-community-common, mysql-community-libs, mysql-community-libs-compat yum install mysql-community-server -y # 啓動mysql server service mysqld start # 查看mysql server狀態 service mysqld status # 設置開機自動啓動 chkconfig mysqld on # 查看初始密碼 # mysql server初始化時會建立帳號 'root'@'localhost', 默認密碼存放在錯誤日誌中 grep 'temporary password' /var/log/mysqld.log # 修改帳號密碼 mysql -uroot -p mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '新密碼需包含大小寫,數字,字符'; # 建立新帳號 mysql> GRANT ALL ON *.* TO yjx@'%' IDENTIFIED BY '複雜密碼'; # 查看當前mysql編碼 show variables like 'character%'; # 修改mysql server字符集 # 修改 /etc/my.cnf # 設置 # # [mysqld] # character_set_server=utf8 # # 設置完後重啓mysqld # 不使用service來關閉mysqld的方法 mysqladmin -uroot -p shutdown
mysql_secure_installation
是用於設置root密碼, 移除匿名用戶等MySQL 5.7 請不要運行
mysql_secure_installation
, 由於安裝時已經默認執行過了.
關閉默認實例
# 取消開機啓動 chkconfig mysqld off # 關閉默認mysqld service mysqld stop
!!! mysql 5.7 如下沒有 --initialize-insecure
和 --initialize
, 所以初始化數據庫必須使用 mysql_install_db --datadir=【數據目錄】
分別配置實例配置
mkdir -p /data/mysql_3307 mkdir -p /data/mysql_3308 chown -R mysql:mysql /data/mysql_33* # 一份簡單的配置文件 # basedir 指的是mysqld的安裝目錄 cat > /data/mysql_3307/my.cnf <<\EOF [mysqld] user = mysql port = 3307 socket = /data/mysql_3307/mysqld.sock pid-file = /data/mysql_3307/mysqld.pid datadir = /data/mysql_3307/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3307/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3307/slow.log long_query_time = 2 EOF # 複製配置文件 sed "s/3307/3308/g" /data/mysql_3307/my.cnf > /data/mysql_3308/my.cnf # 初始化數據庫 # --initialize-insecure 生成無密碼的root帳號 # --initialize 生成帶隨機密碼的root帳號 # 注意參數順序, 必須先指定 --defaults-file=配置文件, 不然會報錯 mysqld --defaults-file=/data/mysql_3307/my.cnf --initialize-insecure mysqld --defaults-file=/data/mysql_3308/my.cnf --initialize-insecure # 啓動實例 mysqld_safe --defaults-file=/data/mysql_3307/my.cnf & mysqld_safe --defaults-file=/data/mysql_3308/my.cnf & # 關閉實例 mysqladmin -S /data/mysql_3307/mysqld.sock shutdown mysqladmin -S /data/mysql_3308/mysqld.sock shutdown # 鏈接實例 mysql -uroot -p -S /data/mysql_3307/mysqld.sock # # 注意, 默認只有 root@localhost, 需自行建立 root@'%' 帳號才能遠程登陸 mysql -uroot -h192.168.190.100 -P 3308
使用mysqld_multi管理多實例
# 建立目錄 mkdir -p /data/mysql_3307 mkdir -p /data/mysql_3308 chown -R mysql:mysql /data/mysql_33* # 初始化數據庫 # 建議初始化時仍是讀取配置文件來初始化好, 初始化完成後再將配置集中到 multi.cnf mysqld --defaults-file=/data/mysql_3309/my.cnf --initialize-insecure mysqld --defaults-file=/data/mysql_3310/my.cnf --initialize-insecure #mysqld -u mysql --basedir=/usr --datadir=/data/mysql_3309/data --initialize-insecure #mysqld -u mysql --basedir=/usr --datadir=/data/mysql_3310/data --initialize-insecure # 直接修改 /etc/my.cnf 或新建立一份multi配置 /data/multi.cnf # 如果新建立multi配置, 則執行mysql_multi時需指定該配置文件 cat > /etc/multi.cnf <<EOF [mysqld_multi] mysqld = /usr/bin/mysqld_safe mysqladmin = /usr/bin/mysqladmin [mysqld3309] user = mysql port = 3309 socket = /data/mysql_3309/mysqld.sock pid-file = /data/mysql_3309/mysqld.pid datadir = /data/mysql_3309/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3309/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3309/slow.log long_query_time = 2 EOF [mysqld3310] user = mysql port = 3310 socket = /data/mysql_3310/mysqld.sock pid-file = /data/mysql_3310/mysqld.pid datadir = /data/mysql_3310/data basedir = /usr bind-address = 0.0.0.0 character_set_server = utf8 symbolic-links = 0 log_error = /data/mysql_3310/error.log slow_query_log = 1 slow_query_log_file = /data/mysql_3310/slow.log long_query_time = 2 EOF EOF # 啓動實例 3309 和 3310, 支持使用連字符語法或用逗號分隔 mysqld_multi --defaults-file=/data/multi.cnf start 3309,3310 # 關閉實例 mysqld_multi --defaults-file=/data/multi.cnf stop 3309-3310 # 查看實例狀態 mysqld_multi --defaults-file=/data/multi.cnf report 3309-3310
!!!
mysqld_multi
不支持 !include 或 !includedir
eg. 採用 heartbeat + drbd + mysql 實現mysql高可用雙機熱備方案
未實踐
目的:
eg. MySQL 主從複製
Master 配置文件
log_bin = /data/3306/mysql-bin server-id = 1 #expire_logs_days = 10 #max_binlog_size = 100M
必須打開 master 端的 Binary Log
Slave 配置文件
read-only = 1 log_bin = /data/3307/mysql-bin server-id = 2 # 可選, 指定中繼日誌的位置和命名 relay_log = /data/3307/relay.log # 容許備庫將其重放的事件也記錄到自身的二進制日誌中 log_slave_updates = 1
1. 確認Master 開啓了二進制日誌
mysql> SHOW MASTER STATUS;
Tip. 鎖表
FLUSH TABLE WITH READ LOCK; UNLOCK TABLES;
2. Master 建立專門用於主從複製的帳號
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'rep'@'192.168.100.%' IDENTIFIED BY '123456';
複製帳號在主庫上只須要REPLICATION SLAVE
權限,1.用來監控和管理複製的帳號須要REPLICATION CLIENT 權限,而且針對這兩種目的使用同一個帳號更加容易(而不是爲了兩個目的各建立一個帳號)。
2.若是在主庫上創建了帳號,而後從主庫將數據克隆到備庫上時,備庫也就設置好了-變成主庫所須要的配置。這樣後續有須要能夠方便的交換主備庫角色。
3. 主-從 數據保持一致
# 主庫導出數據快照 # 若表全都使用InnoDB引擎, 則可以使用 --single-transaction 來代替 --lock-all-tables # --master-data=1 導出sql中會包含CHANGE MASTER語句 # eg. CHANGE MASTER TO MASTER_LOG_FILE='bin.000003', MASTER_LOG_POS=25239; # --master-data=2 導出CHANGE MASTER語句,可是會被註釋(僅在平時備份時導出用) mysqldump -uroot -p -S /data/3306/mysql.sock -A --master-data=1 --lock-all-tables > master.sql > master.sql # 從庫導入 mysql -uroot -p -S /data/3307/mysql.sock < master.sql
此處假設主數據庫已經在使用, 而從數據庫是新的, 所以須要先保持兩邊數據一致
4. Slave 更改從庫的鏈接參數
# 嘗試在此處不設置 MASTER_LOG_FILE,MASTER_LOG_POS, 結果後面 START SLAVE 後一直出錯 # 此處的 MASTER_LOG_FILE,MASTER_LOG_POS 能夠在日誌中查看 mysql> CHANGE MASTER TO MASTER_HOST='192.168.190.100',MASTER_PORT=3309,MASTER_USER='rep',MASTER_PASSWORD='123456',MASTER_LOG_FILE='bin.000003', MASTER_LOG_POS=25239; # 確認一下配置文件正確 cat /data/3307/data/master.info # 從庫鏈接主庫 mysql> START SLAVE; # 確認鏈接正常 mysql> SHOW SLAVE STATUS\G;
-- 查看master的狀態, 尤爲是當前的日誌及位置 show master status; -- 查看slave的狀態. show slave status; -- 重置slave狀態,用於刪除SLAVE數據庫的relaylog日誌文件,並從新啓用新的relaylog文件.會忘記 主從關係,它刪除master.info文件和relay-log.info 文件 reset slave; -- 啓動slave 狀態(開始監聽msater的變化) start slave; -- 暫停slave狀態; stop slave; -- 跳過致使複製終止的n個事件,僅在slave線程沒運行的情況下使用 set global sql_slave_skip_counter = n;
如下參數中, 部分參數只對InnoDB引擎有效
參數名 | 含義 | 建議 |
---|---|---|
max_connections |
最大客戶端鏈接數 | |
innodb_buffer_pool_size |
Innodb存儲引擎緩存池大小 | 建議設置爲物理內存的 80% 左右 |
innodb_file_per_table |
Innodb中每一個表存放使用獨立的表空間 不然是放在共享空間 |
設爲 1, 表示每一個表使用獨立表空間, 方便回收空間. |
innodb_log_file_size |
事務日誌(Redo Log)單個大小(文件 ib_logfile* |
總的日誌大小足以容納1個小時的量 |
innodb_log_files_in_group |
事務日誌數量 | 默認是 2 |
innodb_flush_logs_at_trx_commit |
事務提交時寫日誌的方式 0: 每秒將日誌持久化到磁盤. 數據庫崩潰時丟失最多1秒數據 1: 默認, 每次事務提交都將日誌持久化到磁盤, 最安全, 性能最通常. 2: 每次事務提交都寫入磁盤(指磁盤緩衝區), 具體什麼時候持久化到磁盤則由操做系統控制. 系統崩潰時丟失數據 |
不推薦設爲 0. 對性能要求較高能夠設置爲 2. |
sync_binlog |
MySQL 控制寫入 BinLog 的方式 0 : 每次事務提交寫入磁盤緩衝區, 由操做系統控制什麼時候持久化 N: 每進行N個事務提交後持久化到磁盤, 當N=1時最安全但性能最差 5.7.7及之後默認是1, 以前默認是0 |
|
mysql 自帶 mysqlslap 壓測工具, 能夠自行測試, 我的未使用過.
調整Redo Log大小通常是經過修改 innodb_log_file_size
配置.
在業務高峯期, 計算出1分鐘寫入的redo log量
# 只顯示結果中 Log 相關 > pager grep Log; # 查看日誌位置, sleep > show engine innodb status\G; select sleep(60); show engine innodb status\G; # 取消結果過濾 > nopager;
經過查看兩次的 Log sequence number
值, 計算出差值, 單位是字節.
將上述值乘以 60 獲得合理的 Redo Log 大小. 所以 innodb_log_file_size
推薦設置成 估算的Redo Log 大小 / 日誌文件數(innodb_log_files_in_group
)
innodb_log_file_size
值, 並將數據目錄下的全部 ib_logfile*
文件move走(先不刪, 防止出問題沒法啓動)ib_logfile*
備註: 有看到說 mysql5.6 版本及之後無需手動刪除
ib_logfile*
文件.
若是Redo Log過小, 會致使頻繁刷髒頁??
太大會致使故障恢復時恢復時間太長(甚至長到不可接受的程度)
USE 數據庫名; -- 選擇數據庫 SHOW DATABASES; -- 查看數據庫列表 SHOW TABLES; -- 查看當前數據庫內的表的列表 SHOW COLUMNS FROM `表名`; -- 查看錶結構 SHOW STATUS; -- 用於顯示普遍的服務器狀態信息 SHOW CREATE DATABASE `數據庫名`; -- 顯示建立特定數據庫的MySQL語句 SHOW CREATE TABLE `表名`; -- 顯示建立表的MySQL語句; SHOW GRANTS; -- 顯示受權指定用戶的安全權限, 默認是當前用戶 SHOW GRANTS FOR 用戶@"..." -- eg. root@'%' 或 root@localhost 或 root@0.0.0.0 SHOW ERRORS -- 用來顯示服務器錯誤消息 SHOW WARNINGS -- 用來顯示服務器警告消息
DESCRIBE 表名
等同於 SHOW COLUMNS FROM 表名
, MySQL獨有
STATUS
快速查看當前實例狀態, eg.
-------------- mysql Ver 14.14 Distrib 5.7.22, for Linux (x86_64) using EditLine wrapper Connection id: 28 Current database: mysql_learn Current user: root@localhost SSL: Not in use Current pager: stdout Using outfile: '' Using delimiter: ; Server version: 5.7.22-0ubuntu18.04.1 (Ubuntu) Protocol version: 10 Connection: 127.0.0.1 via TCP/IP Insert id: 114 Server characterset: latin1 Db characterset: latin1 Client characterset: utf8 Conn. characterset: utf8 TCP port: 3306 Uptime: 7 days 23 hours 29 min 13 sec Threads: 6 Questions: 817 Slow queries: 0 Opens: 205 Flush tables: 1 Open tables: 150 Queries per second avg: 0.001 --------------
數值數據類型
<u> 有符號或無符號</u>全部數值數據類型(除 BIT 和 BOOLEAN 外)均可以有符號或無符號。有符號數值列能夠存儲正或負的數值,無符號數值列只能存儲正數。默認狀況爲有符號,但若是你知道本身不須要存儲負值,可使用 UNSIGNED 關鍵字,這樣作將容許你存儲兩倍大小的值。
類型 | 大小 | 範圍(有符號) | 範圍(無符號) | 用途 |
---|---|---|---|---|
TINYINT | 1 字節 | (-128,127) | (0,255) | 小整數值 |
SMALLINT | 2 字節 | (-32 768,32 767) | (0,65 535) | 大整數值 |
MEDIUMINT | 3 字節 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整數值 |
INT或INTEGER | 4 字節 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整數值 |
BIGINT | 8 字節 | (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 極大整數值 |
FLOAT | 4 字節 | (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 單精度 浮點數值 |
DOUBLE | 8 字節 | (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 雙精度 浮點數值 |
DECIMAL | DECIMAL(M,D) ,若是M>D,爲M+2不然爲D+2 | 依賴於M和D的值 | 依賴於M和D的值 | 小數值 |
關鍵字INT是INTEGER的同義詞,關鍵字DEC是DECIMAL的同義詞。
int(m) 裏的m是表示 SELECT 查詢結果集中的顯示寬度,並不影響實際的取值範圍.
FLOAT 類型
float(m,d)
DOUBLE 類型
double(m,d)
DECIMAL 類型
- 精確值
decimal(m,d)
m<=65, d<=30, m是總位數, d是小數位數
字符串數據類型
類型 | 大小 | 用途 |
---|---|---|
CHAR | 0-255字節 | 定長字符串 |
VARCHAR | 0-65535 字節 | 變長字符串 |
TINYBLOB | 0-255字節 | 不超過 255 個字符的二進制字符串 |
TINYTEXT | 0-255字節 | 短文本字符串 |
BLOB | 0-65 535字節 | 二進制形式的長文本數據 |
TEXT | 0-65 535字節 | 長文本數據 |
MEDIUMBLOB | 0-16 777 215字節 | 二進制形式的中等長度文本數據 |
MEDIUMTEXT | 0-16 777 215字節 | 中等長度文本數據 |
LONGBLOB | 0-4 294 967 295字節 | 二進制形式的極大文本數據 |
LONGTEXT | 0-4 294 967 295字節 | 極大文本數據 |
CHAR 類型
char(n) 若存入字符數小於n,則以空格補於其後,查詢之時再將空格去掉。因此 char 類型存儲的字符串末尾不能有空格
- 效率比 VARCHAR 高點
VARCHAR 類型
- 長度設置, 在MySQL 5以後是按<u>字符數</u>, 而不是字節數.
varchar(20)
能夠存儲20個<u>字符</u>- varchar 頭部會佔用 1個(n<=255)或2個字節(n>255)保存字符串長度, 若值可設爲 null, 則還須要一個1字節記錄null, 所以保存utf8編碼的字符串最多可存 (65535 - 3)/3 = 21844
- 如果utf8編碼
- 效率比 TEXT 高
TEXT 類型
- 建立索引時要指定前多少個字符
- 不能有默認值
- text 頭部會佔用 2個字節來保存長度
日期和時間類型
類型 | 大小 (字節) | 範圍 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01/9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | '-838:59:59'/'838:59:59' | HH:MM:SS | 時間值或持續時間 |
YEAR | 1 | 1901/2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和時間值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00/2038結束時間是第 2147483647 秒,北京時間 2038-1-19 11:14:07,格林尼治時間 2038年1月19日 凌晨 03:14:07 | YYYYMMDD HHMMSS | 混合日期和時間值,時間戳 |
每一個時間類型有一個有效值範圍和一個"零"值,當指定不合法的MySQL不能表示的值時使用"零"值。
TIMESTAMP類型有專有的自動更新特性
update_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
- 保存毫秒數(相似php 的
microtime(true)
)- timestamp列默認not null。沒有顯式指定nullable,那麼default null不合法
- mysql不會給timestamp設置默認值,除非顯式設置default約束或者可空null。特例:mysql會給表第一個timestamp類型的字段同時添加
default current_timestamp
和on update timestamp
- 其餘狀況均會引發不合法報錯
- ↑ 總結: 最好手動設置 NULL 以避免出錯
日期函數:
CURRENT_TIMESTAMP
, CURRENT_TIMESTAMP()
是 NOW()
的同義詞SYSDATE()
獲取的是函數執行時的時間, NOW()
獲取的是執行語句時的<u>開始時間</u>.二進制數據類型
子句的書寫順序很重要:
SELECT ... FROM ... JOIN ... ON ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT ...
執行順序:
FROM > WHERE(包含JOIN) > GROUP BY > 彙集函數字段計算 > HAVING > SELECT 的字段 > ORDER BY > LIMIT
舉例:
SELECT player.team_id, count(*) as num -- 順序5 FROM player JOIN team ON player.team_id = team.team_id -- 順序1 WHERE height > 1.80 -- 順序2 GROUP BY player.team_id -- 順序3 HAVING num > 2 -- 順序4 ORDER BY num DESC -- 順序6 LIMIT 2; -- 順序7
基本檢索
SELECT `列名1`,`列名2` FROM `表名`; -- 檢索指定列 SELECT * FROM `表名`; -- 檢索全部列
DISTINCT關鍵字: 只返回不一樣的值
SELECT DISTINCT `列名1` FROM `表名`; -- 只返回惟一的 `列名1`行 SELECT DISTINCT `列名1`,`列名2` FROM `表名`; -- DISTINCT應用於全部的列, 返回 (`列名1`, `列名2`) 不一樣的行
LIMIT關鍵字: 限制結果
SELECT * FROM `表名` LIMIT <limit>; -- 限制返回最多 <limit> 條, 等同 0,<limit> SELECT * FROM `表名` LIMIT <offset>,<limit>; -- 略過前 <offset> 條記錄, 返回最多 <limit> 條 SELECT * FROM `表名` LIMIT <limit> OFFSET <offset>; -- MySQL 5 語法糖
徹底限定名
SELECT `表名`.`列名` FROM `數據庫名`.`表名`; -- 有一些情形須要徹底限定名
ORDER 子句
SELECT * FROM `表名` ORDER BY `列名` <dir>; -- <dir> 默認是 ASC, 可指定 DESC. SELECT * FROM `表名` ORDER BY `列名1` <dir1>,`列名2` <dir2>; -- 僅在`列名1`等值時才按`列名2`排序
排序方式可選:
- ORDER BY 字句使用的排序列不必定是顯示的列, 這是容許的.
- ORDER BY
- 對文本數據排序時, 大小寫的排序取決於數據庫如何設置
COLLATE
操做符 | 說明 |
---|---|
= |
等於 |
<> |
不等於 |
!= |
不等於 |
< |
小於 |
<= |
小於等於 |
> |
大於 |
>= |
大於等於 |
BETWEEN ... AND ... |
在指定的兩個值之間 |
... IS NULL |
沒有值 |
... IS NOT NULL |
非空, 有值 |
IN (…) | 在元組 |
NOT IN (...) | 不在元組 |
AND, OR 操做符
計算次序: AND 運算符優先級更高, 在處理OR操做符前會先處理AND操做符, 舉例:
SELECT prod_name,prod_price,vend_id FROM products WHERE vend_id=1002 OR vend_id=1003 AND prod_price>=10; -- 等價於 SELECT prod_name,prod_price,vend_id FROM products WHERE (vend_id=1002) OR (vend_id=1003 AND prod_price>=10);
IN操做符
SELECT * FROM `表名` WHERE `列名` IN (值1, 值2, ..., 值N);
IN
功能等同於 OR
, 那麼爲何用 IN
:
NOT操做符
否認它以後所跟的任何條件
... WHERE `列名` NOT IN (值1, ..., 值N); ... WHERE `列名` IS NOT NULL; ... WHERE `列名` NOT BETWEEN 1 AND 10; ... WHERE `列名` NOT EXISTS (...);
NOT 在複雜的WHERE字句中頗有用.
例如,在與 IN 操做符聯合使用時, NOT 使找出與條件列表不匹配的行很是簡單。MySQL支持使用 NOT 對 IN 、 BETWEEN 和
EXISTS子句取反,這與多數其餘 DBMS容許使用 NOT 對各類條件
取反有很大的差異。
LIKE操做符
通配符(wildcard)
通配符 | 含義 |
---|---|
% |
任何字符出現任意次數(0,1,N), 注意不匹配 NULL |
_ |
匹配單個字符(1) |
like 匹配完整的列.
通配符置於搜索模式開始處, 不會使用索引.
注意NULL 雖然彷佛 % 通配符能夠匹配任何東西,但有一個例外,即 NULL 。即便是 WHERE prod_name LIKE '%' 也不能匹配用值 NULL 做爲產品名的行。
SELECT * FROM `表名` WHRER `列名` LIKE `a%d`; SELECT * FROM `表名` WHRER `列名` LIKE `a_cd`;
MySQL僅支持多數正則表達式實現的一個很小的子集。
eg. 不支持\d
,\s
等
SELECT * FROM `表名` WHERE `列名` REGEXP '^[0-9]'; -- 匹配數字開頭的, 默認不區分大小寫 SELECT * FROM `表名` WHERE `列名` REGEXP BINARY "正則表達式"; -- 區分大小寫
多數正則表達式實現使用單個反斜槓轉義特殊字符,以便能使用這些字符自己。但MySQL要求兩個反斜槓(MySQL本身解釋一個,正則表達式庫解釋另外一個)。
轉義.
時, 需使用\\.
而非\.
計算字段並不實際存在於數據庫表中。計算字段是運行時在 SELECT 語句內建立的。
字段(field) 基本上與列(column)的意思相同,常常互換使用,不過數據庫列通常稱爲列,而術語字段一般用在計算字段的鏈接上。
AS 別名
別名(alias)是一個字段或值的替換名。
SELECT `列名` AS `別名` FROM `表名`;
別名的其餘常見用途:
算數運算
圓括號可用來區分優先順序。
函數沒有SQL的可移植性強, 幾乎每種主要的DBMS的實現都支持其餘實現不支持的函數,並且有時差別還很大。
concat()
字符串拼接
SELECT concat(`列名1`, '(', `列名2`, ')') FROM `表名`; -- 組成: `列名1`(`列名2`)
MySQL的不一樣之處 多數DBMS使用 + 或 || 來實現拼接,MySQL則使用 Concat() 函數來實現。當把SQL語句轉換成MySQL語句時必定要把這個區別銘記在心
LOCATE(substr,str)
返回子串 substr 在字符串 str 中第一次出現的位置。若是子串 substr 在 str 中不存在,返回值爲 0:
LOCATE(substr,str,pos)
多字節安全
返回子串 substr 在字符串 str 中的第 pos 位置後第一次出現的位置。若是 substr 不在 str 中返回 0
substr(str,pos)
pos
爲1時表示截取所有
substr(str,pos,len)
concat_ws(separator, str1, str2, ...)
使用指定分隔符拼接參數, 忽略NULL
group_concat(...)
函數返回一個字符串結果,該結果由分組中的值鏈接組合而成。
!!! 注意 MySQL字符串位置是從1開始
MySQL 日期格式固定爲: yyyy-mm-dd
, 不管是插入,更新,或WHERE字句過濾.
補充
函數 | 說明 | 示例 |
---|---|---|
from_unixtime() |
將時間戳轉爲日期 | from_unixtime(1559001600) |
unix_timestamp() |
將指定日期或日期字符串轉換爲時間戳 | unix_timestamp(Now()) |
標準誤差 MySQL還支持一系列的標準誤差彙集函數
AVG()
, MAX()
, MIN()
, SUM()
函數忽略列值爲 NULL 的行 COUNT(*)
對錶中行的數目進行計數,無論表列中包含的是空值( NULL )仍是非空值 COUNT(column)
對特定列中具備值的行進行計數,忽略NULL 值上述彙集函數可配合 DISTINCT 來使用
SELECT COUNT(DISTINCT `列名`) FROM `表名`; SELECT AVG(DISTINCT `列名`) FROM `表名`; SELECT SUM(DISTINCT `列名`) FROM `表名`;
CHAR_LENGTH(str) 返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算做一個單字符。 對於一個包含五個二字節字符集, LENGTH()返回值爲 10, 而CHAR_LENGTH()的返回值爲5。 CONCAT(str1,str2,...) 字符串拼接 若有任何一個參數爲NULL ,則返回值爲 NULL。 CONCAT_WS(separator,str1,str2,...) 字符串拼接(自定義鏈接符) CONCAT_WS()不會忽略任何空字符串。 (然而會忽略全部的 NULL)。 CONV(N,from_base,to_base) 進制轉換 例如: SELECT CONV('a',16,2); 表示將 a 由16進制轉換爲2進制字符串表示 FORMAT(X,D) 將數字X 的格式寫爲'#,###,###.##',以四捨五入的方式保留小數點後 D 位, 並將結果以字符串的形式返回。若 D 爲 0, 則返回結果不帶有小數點,或不含小數部分。 例如: SELECT FORMAT(12332.1,4); 結果爲: '12,332.1000' INSERT(str,pos,len,newstr) 在str的指定位置插入字符串 pos:要替換位置其實位置 len:替換的長度 newstr:新字符串 特別的: 若是pos超過原字符串長度,則返回原字符串 若是len超過原字符串長度,則由新字符串徹底替換 INSTR(str,substr) 返回字符串 str 中子字符串的第一個出現位置。 LEFT(str,len) 返回字符串str 從開始的len位置的子序列字符。 LOWER(str) 變小寫 UPPER(str) 變大寫 LTRIM(str) 返回字符串 str ,其引導空格字符被刪除。 RTRIM(str) 返回字符串 str ,結尾空格字符被刪去。 SUBSTRING(str,pos,len) 獲取字符串子序列 LOCATE(substr,str,pos) 獲取子序列索引位置 REPEAT(str,count) 返回一個由重複的字符串str 組成的字符串,字符串str的數目等於count 。 若 count <= 0,則返回一個空字符串。 若str 或 count 爲 NULL,則返回 NULL 。 REPLACE(str,from_str,to_str) 返回字符串str 以及全部被字符串to_str替代的字符串from_str 。 REVERSE(str) 返回字符串 str ,順序和字符順序相反。 RIGHT(str,len) 從字符串str 開始,返回從後邊開始len個字符組成的子序列 SPACE(N) 返回一個由N空格組成的字符串。 SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len) 不帶有len 參數的格式從字符串str返回一個子字符串,起始於位置 pos。帶有len參數的格式從字符串str返回一個長度同len字符相同的子字符串,起始於位置 pos。 使用 FROM的格式爲標準 SQL 語法。也可能對pos使用一個負值。倘若這樣,則子字符串的位置起始於字符串結尾的pos 字符,而不是字符串的開頭位置。在如下格式的函數中能夠對pos 使用一個負值。 mysql> SELECT SUBSTRING('Quadratically',5); -> 'ratically' mysql> SELECT SUBSTRING('foobarbar' FROM 4); -> 'barbar' mysql> SELECT SUBSTRING('Quadratically',5,6); -> 'ratica' mysql> SELECT SUBSTRING('Sakila', -3); -> 'ila' mysql> SELECT SUBSTRING('Sakila', -5, 3); -> 'aki' mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2); -> 'ki' TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str) 返回字符串 str , 其中全部remstr 前綴和/或後綴都已被刪除。若分類符BOTH、LEADIN或TRAILING中沒有一個是給定的,則假設爲BOTH 。 remstr 爲可選項,在未指定狀況下,可刪除空格。 mysql> SELECT TRIM(' bar '); -> 'bar' mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx'); -> 'barxxx' mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx'); -> 'bar' mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz'); -> 'barx'
GROUP BY ... HAVING ...
彙集 配合 分組 GROUP BY 子句指示MySQL分組數據,而後對每一個組而不是整個結果集進行彙集。
使用 GROUP BY
的重要規定
??
HAVING
過濾的是分組
WHERE
過濾的是行
HAVING
可使用別名HAVING 和 WHERE 的差異
這裏有另外一種理解方法, WHERE 在數據分組前進行過濾, HAVING 在數據分組後進行過濾。這是一個重要的區別, WHERE 排除的行不包括在分組中。這可能會改變計算值,從而影響 HAVING 子句中基於這些值過濾掉的分組。
根據子查詢執行的次數, 對子查詢進行分類:
查詢不依賴外層(即與主查詢無關), 該子查詢只需執行一次, 獲得的數據結果能夠做爲外層查詢的條件直接使用.
查詢時依賴外層(用到外層表數據, 與主查詢相關), 所以每次外層查詢都要根據外層查詢結果再進行子查詢, 該子查詢須要執行屢次.
在實際使用時因爲性能的限制,不能嵌套太多的子查詢。
eg. 返回訂購產品 TNT2 的客戶列表
select * from customers where cust_id in (select distinct cust_id from orders where order_num in (select order_num from orderitems where prod_id="TNT2") );
存在性子查詢
判斷條件是否知足, 知足爲True, 不然爲False
集合比較子查詢
判斷是否在集合中
ANY
必須與比較操做符一塊兒使用, 與子查詢中<u>任意值</u>比較
SELECT * FROM A WHERE A.cc > ANY (SELECT cc FROM B);
是ANY的別名, 等價於 ANY, 通常經常使用 ANY
ALL
必須與比較操做符一塊兒使用, 與子查詢中<u>全部值</u>比較
SELECT * FROM A WHERE A.cc > ALL (SELECT cc FROM B);
Q. 對於 表 A, B 的子查詢, EXISTS 和 IN 要選哪一個?
A. 須要根據表A 和 表B 的表大小及索引狀況而定.
一般使用 IN 及 EXISTS 狀況能夠歸納爲如下 SQL 語句:
-- 語句1 SELECT * FROM A WHERE cc IN (SELECT cc FROM B); -- 語句2 SELECT * FROM A WHERE EXISTS (SELECT cc FROM B WHERE A.cc = B.cc);
原則: 小表驅動大表.
結論:
這裏的表大小指的是根據where篩選後的大小, 而非表的原始大小
聯結是一種機制,用來在一條 SELECT語句中關聯表,所以稱之爲聯結。
使用特殊的語法,能夠聯結多個表返回一組輸出,聯結在運行時關聯表中正確的行。
使用聯結的要點:
<u>表別名</u>只在查詢執行中使用。與列別名不同,表別名不返回到客戶機。
<u>徹底限定列名</u> 在引用的列可能出現二義性時,必須使用徹底限定列名(用一個點分隔的表名和列名)。若是引用一個沒有用表名限制的具備二義性的列名,MySQL將返回錯誤。
在聯結兩個表時,你實際上作的是將第一個表中的每一行與第二個表中的每一行配對。 WHERE 子句做爲過濾條件,它只包含那些匹配給定條件(這裏是聯結條件)的行。沒有WHERE 子句,第一個表中的每一個行將與第二個表中的每一個行配對,而無論它們邏輯上是否能夠配在一塊兒。
示例代碼↓
-- 將表 vendors 與表 products 聯結, 聯結條件是: vendors.vend_id = products.vend_id SELECT prod_id,vend_name,prod_name,prod_price FROM vendors,products WHERE vendors.vend_id = products.vend_id; -- 表 vendors 每一行都將於表 products 每一行配對 -- 此時返回的結果爲 笛卡爾積, 返回行數目是: count(表A) * count(表B) SELECT prod_id,vend_name,prod_name,prod_price FROM vendors,products;
根據獲取到的結果集的範圍將鏈接進行分類:
隱式的內鏈接: 不寫 INNER JOIN
顯式的內鏈接: 寫 INNER JOIN
左外鏈接 LEFT OUTER JOIN
右外鏈接 RIGHT OUTER JOIN
全外鏈接 FULL OUTER JOIN
書寫時 OUTER
能夠忽略
CROSS JOIN
返回記錄的條數是每一個表的行數的乘積.
根據鏈接時的測試條件, 將鏈接進行分類:
當自身與自身進行鏈接時, 稱爲<u>自鏈接</u>.
<u>內部聯結</u>即上面的<u>等值聯結</u>, 它基於兩個表之間的相等測試。
SELECT vendors.vend_id,vend_name,prod_name,prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
<u>使用哪一種語法?</u> ANSI SQL規範首選 INNER JOIN 語法。此外,儘管使用 WHERE 子句定義聯結的確比較簡單,可是使用明確的聯結語法可以確保不會忘記聯結條件,有時候這樣作也能影響性能。
<u>性能考慮</u> MySQL在運行時關聯指定的每一個表以處理聯結。這種處理多是很是耗費資源的,所以應該仔細,不要聯結沒必要要的表。聯結的表越多,性能降低越厲害。
eg. 返回訂購產品 TNT2 的客戶列表
-- 子查詢方式 select * from customers where cust_id in (select distinct cust_id from orders where order_num in (select order_num from orderitems where prod_id="TNT2") ); -- 表聯結方式1 SELECT cust_name,cust_contact FROM customers,orders,orderitems WHERE customers.cust_id = orders.cust_id AND orders.order_num = orderitems.order_num AND orderitems.prod_id = 'TNT2'; -- 表聯結方式2 SELECT cust_name,cust_contact FROM customers INNER JOIN orders ON customers.cust_id = orders.cust_id INNER JOIN orderitems ON orders.order_num = orderitems.order_num WHERE orderitems.prod_id = "TNT2";
Eg. 假如你發現某物品(其ID爲 DTNTR )存在問題,所以想知道生產該物品的供應商生產的其餘物品是否也存在這些問題。
-- 子查詢方式 SELECT prod_id,prod_name FROM products WHERE vend_id = ( SELECT vend_id FROM products WHERE prod_id = 'DTNTR' ); -- 自聯結方式 SELECT p1.prod_id,p1.prod_name FROM products as p1,products as p2 WHERE p1.vend_id = p2.vend_id AND p2.prod_id = 'DTNTR';
<u>用自聯結而不用子查詢</u> 自聯結一般做爲外部語句用來替代從相同表中檢索數據時使用的子查詢語句。雖然最終的結果是相同的,但有時候處理聯結遠比處理子查詢快得多。應該試一下兩種方法,以肯定哪種的性能更好
<u>外部聯結</u>: 聯結包含了那些在相關表中沒有關聯行的行。
許多聯結將一個表中的行與另外一個表中的行相關聯。但有時候會需
要包含沒有關聯行的那些行。例如,可能須要使用聯結來完成如下工做:
- 對每一個客戶下了多少訂單進行計數,包括那些至今還沒有下訂單的
客戶;- 列出全部產品以及訂購數量,包括沒有人訂購的產品;
- 計算平均銷售規模,包括那些至今還沒有下訂單的客戶。
-- 使用 LEFT OUTER JOIN 從 FROM子句的左邊表( customers 表)中選擇全部行 select c.cust_id,o.order_num from customers as c left outer join orders as o on c.cust_id = o.cust_id; -- 查看全部客戶的訂單數量(彙集函數), 包含從沒下過單的客戶 SELECT c.cust_id,cust_name,count(distinct o.order_num) FROM customers as c LEFT OUTER JOIN orders as o ON c.cust_id=o.cust_id GROUP BY c.cust_id;
與內部聯結關聯兩個表中的行不一樣的是,外部聯結還包括沒有關聯行的行。在使用 OUTER JOIN 語法時,必須使用 RIGHT 或 LEFT 關鍵字指定包括其全部行的表( RIGHT 指出的是 OUTER JOIN 右邊的表,而 LEFT指出的是 OUTER JOIN 左邊的表)。
OUTER 關鍵字能夠省略不寫.
<u>外部聯結的類型</u> 存在兩種基本的外部聯結形式:左外部聯結和右外部聯結。它們之間的惟一差異是所關聯的表的順序不一樣。換句話說,左外部聯結可經過顛倒 FROM 或 WHERE 子句中表的順序轉換爲右外部聯結。所以,兩種類型的外部聯結可互換使用,而究竟使用哪種純粹是根據方便而定。
MySQL也容許執行多個查詢(多條 SELECT 語句),並將結果做爲單個查詢結果集返回。這些組合查詢一般稱爲並(union)或複合查詢(compound query)。
<u>組合查詢和多個 WHERE 條件</u> 多數狀況下,組合相同表的兩個查詢完成的工做與具備多個 WHERE 子句條件的單條查詢完成的工做相同。
-- 返回查詢(過濾重複行) SELECT ... FROM ... UNION SELECT ... FROM ... ORDER BY ... -- 返回查詢(保留全部行) SELECT ... FROM ... UNION ALL SELECT ... FROM ... ORDER BY ...
對於更復雜的過濾條件,或者從多個表(而不是單個表)中檢索數據的情形,使用 UNION 可能會使處理更簡單。
UNION規則
特色
重要說明
MyISAM 引擎支持, InnoDB 引擎不支持.
爲了進行全文本搜索,必須索引被搜索的列
CREATE TABLE table_name( note_id int NOT NULL AUTO_INCREMENT, note_text text NULL, PRIMARY KEY (note_id), FULLTEXT(note_text), -- 建立全文本索引 ) ENGINE=MyISAM;
<u> !!不要在導入數據時使用 FULLTEXT</u>更新索引要花時間,雖然不是不少,但畢竟要花時間。若是正在導入數據到一個新表,此時不該該啓用 FULLTEXT 索引。應該首先導入全部數據,而後再修改表,定義 FULLTEXT 。這樣有助於更快地導入數據(並且使索引數據的總時間小於在導入每行時分別進行索引所需的總時間)。
全文本搜索返回的結果默認排序是按照關聯程度最高的排在最前面
-- 針對指定的列進行搜索 SELECT * FROM `表名` WHERE Match(`列名`) Against('搜索詞');
Match(
列名) Against('搜索詞')
其實是計算出一個表明關聯程度的數值, 該數值能夠在 SELECT 中直接查看.
?? <u>使用完整的 Match() 說明</u> 傳遞給 Match() 的值必須與FULLTEXT() 定義中的相同。若是指定多個列,則必須列出它們(並且次序正確)。
<u>!!搜索不區分大小寫</u> 除非使用 BINARY 方式(本章中沒有介紹),不然全文本搜索不區分大小寫。
-- WITH QUERY EXPANSION 使用查詢擴展 SELECT note_id,note_text FROM productnotes WHERE match(note_text) against('anvils' WITH QUERY EXPANSION);
MySQL對數據和索引進行兩遍掃描來完成搜索:
利用查詢擴展,能找出可能相關的結果,即便它們並不精確包含所查找的詞。
<u>行越多越好</u> 表中的行越多(這些行中的文本就越多),使用查詢擴展返回的結果越好。
布爾方式(booleanmode)
<u>即便沒有 FULLTEXT 索引也可使用</u> 布爾方式不一樣於迄今爲止使用的全文本搜索語法的地方在於,即便沒有定義
FULLTEXT 索引,也可使用它。但這是一種很是緩慢的操做(其性能將隨着數據量的增長而下降)。
SELECT note_id,note_text FROM productnotes WHERE match(note_text) against('heavy -rope*' IN BOOLEAN MODE);
說明:
- 匹配
heavy
- 排除
rope
開頭的詞
幾種使用方式
插入時必須對每一個列必須提供一個值.
-- 簡單但不安全, 依賴表中列的定義次序 INSERT INTO customer VALUES(NULL,'pep', '100 main', 'los angles', 'CA', '90046', 'USA', NULL, NULL); -- 指定插入的列, 推薦(但很繁瑣) INSERT INTO customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email) VALUES('pep', '100 main', 'los angles', 'CA', '90046', 'USA', NULL, NULL); -- 插入多行 INSERT INTO `表名`(`列名1`, `列名2`) VALUES("值1", "值2"),("值3", "值4"),("值5", "值6"); -- 插入檢索出的數據, 注意避免主鍵的衝突 INSERT INTO `表1`(`列名1`, `列名2`) SELECT `列名1`, `列名2` FROM `表2`;
<u>插入時省略列需知足如下任一條件</u>:
<u>下降插入優先級</u> INSERT LOW PRIORITY INTO
↑ LOW PRIORITY
一樣適用於 UPDATE
和 DELETE
語句
提升 INSERT 的性能 一次插入多條記錄能夠提升數據庫處理的性能,由於MySQL用單條 INSERT 語句處理多個插入比使用多條 INSERT語句快。
<u>INSERT SELECT 中的列名</u> MySQL不關心 SELECT 返回的列名。它使用的是列的位置,所以 SELECT 中的第一列(無論其列名)將用來填充表列中指定的第一個列,第二列將用來填充表列中指定的第二個列,如此等等。這對於從使用不一樣列名的表中導入數據是很是有用的。
好習慣:
UPDATE `表名` SET `列1`="值1", `列2`="值2" WHERE ...; -- IGNORE, 更新多行時, 忽略錯誤 UPDATE IGNORE `表名` SET ... WHERE ...;
<u>IGNORE 關鍵字</u> 若是用 UPDATE 語句更新多行,而且在更新這些行中的一行或多行時出一個現錯誤,則整個 UPDATE 操做被取消(錯誤發生前更新的全部行被恢復到它們原來的值)。爲即便是發生錯誤,也繼續進行更新,可以使用 IGNORE 關鍵字
DELETE FROM `表名` WHERE ...;
<u>刪除表的內容而不是表</u> DELETE 語句從表中刪除行,甚至是刪除表中全部行。可是, DELETE 不刪除表自己。
<u>更快的刪除</u> 若是想從表中刪除全部行,不要使用 DELETE 。可以使用 TRUNCATE TABLE 語句,它完成相同的工做,但速度更快( TRUNCATE 實際是刪除原來的表並從新建立一個表,而不是逐行刪除表中的數據)。
-- 示例 CREATE TABLE IF NOT EXISTS `user_accounts`( `id` int(100) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, `password` varchar(64) NOT NULL COMMENT '用戶密碼', `reset_password` tinyint(2) NOT NULL DEFAULT 0 COMMENT '用戶類型:0-不須要重置密碼;1-須要重置密碼', `mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手機', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- 建立惟一索引, 不容許重複 UNIQUE KEY idx_user_mobile(`mobile`) -- 索引名可忽略: UNIQUE INDEX (`mobile`) -- 建立外鍵 -- FOREIGN KEY (`dept_id`) REFERENCES `depts`(`id`) ON DELETE cascade -- PRIMARY KEY (`id`) -- PRIMARY KEY (`key1`,`key2`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
主鍵值 必須惟一。表中的每一個行必須具備惟一的主鍵值。若是主鍵使用單個列,則它的值必須惟一。若是使用多個列,則這些列的組合值必須惟一。
eg. 多個列的組合做爲主鍵
CREATE TABLE IF NOT EXISTS orderitems ( order_num int NOT NULL, order_item int NOT NULL, prod_id char(10) NOT NULL, quantity int NOT NULL, item_price decimal(8,2) NOT NULL, PRIMARY KEY(order_num, order_item) ) ENGINE=InnoDB;orderitems 表包含orders表中每一個訂單的細節。每一個訂單有多項物品,但每一個訂單任什麼時候候都只有1個第一項物品,1個第二項物品,如此等等。所以,訂單號( order_num 列)和訂單物品( order_item 列)的組
合是惟一的,從而適合做爲主鍵
NULL值就是沒有值或缺值。容許 NULL 值的列也容許在插入行時不給出該列的值。不容許 NULL 值的列不接受該列沒有值的行,
<u>理解 NULL</u> 不要把 NULL 值與空串相混淆。 NULL 值是沒有值,它不是空串。若是指定 '' (兩個單引號,其間沒有字符),這在 NOT NULL 列中是容許的。空串是一個有效的值,它不是無值。 NULL 值用關鍵字 NULL 而不是空串指定。
主鍵和 NULL 值 主鍵爲其值惟一標識表中每一個行的列。<u>主鍵中只能使用不容許 NULL 值的列</u>。容許 NULL 值的
列不能做爲惟一標識。
AUTO_INCREMENT
last_insert_id()
函數返回最後一個 AUTO_INCREMENT 值.
eg. 增長一個新訂單
- orders 表中建立一行
- 使用
last_insert_id()
獲取自動生成的order_num
值- 在 orderitms 表中對訂購的每項物品建立一行。 order_num 在 orderitems 表中與訂單細節一塊兒存儲。
DEFAULT
使用當前時間做爲默認值
CREATE TABLE `表名`( ..., `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP );
ENGINE
<u>外鍵不能跨引擎</u> 混用引擎類型有一個大缺陷。外鍵(用於強制實施引用完整性,如第1章所述)不能跨引擎,即便用一個引擎的表不能引用具備使用不一樣引擎的表的外鍵。
列和外鍵的操做
-- 新增列 ALTER TABLE `表名` ADD COLUMN `列名` 列屬性; -- 刪除列 ALTER TABLE `表名` DROP COLUMN `列名`; -- 修改列(屬性替換) -- CHANGE 能夠重命名列名, MODIFY 不能 ALTER TABLE `表名` CHANGE COLUMN `舊列名` `新列名` 列屬性; ALTER TABLE `表名` MODIFY `列名` 列屬性; -- 刪除表 DROP TABLE `表名`; -- 重命名錶 RENAME TABLE `表名1` TO `表名2`;
複雜的表結構更改通常須要手動刪除過程
用新的列布局建立一個新表;
<u>當心使用 ALTER TABLE</u> 使用 ALTER TABLE 要極爲當心,應該在進行改動前作一個完整的備份(模式和數據的備份)。數據庫表的更改不能撤銷,若是增長了不須要的列,可能不能刪除它們。相似地,若是刪除了不該該刪除的列,可能會丟失該列中的全部數據。
-- 刪除外鍵 -- 約束名能夠用 show create table `表名` 語句來查看 ALTER TABLE `表名` DROP FOREIGN KEY `約束名`; -- 查看索引 SHOW INDEX FROM `表名`; SHOW KEY FROM `表名`; -- 建立普通索引(省略索引名) ALTER TABLE `表名` ADD INDEX (`列名`); ALTER TABLE `表名` ADD UNIQUE KEY(`列名`); ALTER TABLE `表名` ADD PRIMARY KEY(`列名`); ALTER TABLE `表名` ADD FOREIGN KEY(`列名`) REFERENCES `關聯表名`(`關聯列名`); ALTER TABLE `表1` ADD CONSTRAINT `約束名` FOREIGN KEY (`外鍵`) REFERENCES `表2` (`表2的鍵`); -- CREATE INDEX 只可對錶增長普通索引或UNIQUE索引 CREATE INDEX `索引名` ON `表名` (`列名`); CREATE UNIQUE INDEX `索引名` ON `表名` (`列名`); -- 刪除索引 ALTER TABLE `表名` DROP PRIMARY KEY; ALTER TABLE `表名` DROP INDEX `索引名`; ALTER TABLE `表名` DROP FOREIGN KEY `約束名`; DROP INDEX `索引名` ON `表名`;
2019年5月29日17:18:22 開始補充
種類:
INDEX
普通索引:僅加速查詢UNIQUE KEY
惟一索引:加速查詢 + 列值惟一(能夠有null)PRIMARY KEY
主鍵索引:加速查詢 + 列值惟一 + 表中只有一個(不能夠有null)INDEX
組合索引:多列值組成一個索引,專門用於組合搜索,其效率大於索引合併FULLTEXT
全文索引:對文本的內容進行分詞,進行搜索<u>術語</u>
組合索引
備註: 此處將僅做爲存檔, 後續更新將在 這裏 中處理. 2019年10月18日15:53:53
EXPLAIN 結果字段分析
select_type
查詢類型 | 說明 |
---|---|
SIMPLE | 簡單查詢 不包含UNION查詢或子查詢 |
PRIMARY | 最外層查詢 |
SUBQUERY | 子查詢中的第一個 SELECT |
DEPENDENT SUBQUERY | !!! 子查詢, 但依賴於外層查詢的結果 注意確認, 避免大表驅動小表 |
DERIVED | 子查詢 |
UNION | 聯合 |
UNION RESULT | 使用聯合的結果 |
訪問的表名
匹配的分區
type
查詢時的訪問方式, 性能:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
訪問方式 | 說明 |
---|---|
ALL | 全表掃描,對於數據表從頭至尾找一遍select * from tb1; 特別的:若是有limit限制,則找到以後就不在繼續向下掃描 select * from tb1 where email = 'seven@live.com' select * from tb1 where email = 'seven@live.com' limit 1; 雖然上述兩個語句都會進行全表掃描,第二句使用了limit,則找到一個後就再也不繼續掃描。 |
INDEX | 全索引掃描,對索引從頭至尾找一遍select nid from tb1; |
RANGE | 對索引列進行範圍查找 |
INDEX_MERGE | 合併索引,使用多個單列索引搜索 |
REF | 使用索引快速定位(根據索引查找一個或多個值) |
EQ_REF | 一般出如今多表的join查詢, 鏈接時使用primary key 或 unique 索引(都只能匹配到一行記錄) |
CONST | 經過主鍵或惟一索引精確查找到一行 常量 表最多有一個匹配行(主鍵或惟一索引),由於僅有一行,在這行的列值可被優化器剩餘部分認爲是常數,const表很快,由於它們只讀取一次 |
SYSTEM | 系統 表僅有一行(=系統表)。這是const聯接類型的一個特例。 |
表示查詢時,可能使用的索引
實際使用的索引
key_len
使用索引字段長度, 該字段能夠評估組合索引是否徹底被使用或僅僅是最左前綴被用到.
計算規則
字符串
- char(n): n 字節長度
- varchar(n): 若是是 utf8 編碼, 則是 3 n + 2字節; 若是是 utf8mb4 編碼, 則是 4 n + 2 字節.
數值類型:
- TINYINT: 1字節
- SMALLINT: 2字節
- MEDIUMINT: 3字節
- INT: 4字節
- BIGINT: 8字節
時間類型
- DATE: 3字節
- TIMESTAMP: 4字節
- DATETIME: 8字節
- 字段屬性: NULL 屬性 佔用一個字節. 若是一個字段是 NOT NULL 的, 則沒有此屬性.
列與索引的比較,表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
估算的須要掃描的行數
Extra
該列包含MySQL解決查詢的詳細信息
值 | 說明 |
---|---|
Using filesort | mysql對結果排序進行額外排序. mysql有兩種文件排序算法,這兩種排序方式均可以在內存或者磁盤上完成 explain不會告訴你mysql將使用哪種文件排序 也不會告訴你排序會在內存裏仍是磁盤上完成。 |
Using index | 使用覆蓋索引,以免訪問表。不要把覆蓋索引和index訪問類型弄混了。 |
Using index condition | 索引下推優化, 5.6新增特性 |
Using temporary | 意味着mysql在對查詢結果排序時會使用一個臨時表 |
Using where | 這意味着mysql服務器將在存儲引擎檢索行後再進行過濾 許多where條件裏涉及索引中的列,當(而且若是)它讀取索引時,就能被存儲引擎檢驗 所以不是全部帶where子句的查詢都會顯示「Using where」。 有時「Using where」的出現就是一個暗示:查詢可受益於不一樣的索引。 |
Range checked for each record(index map: N) | 這個意味着沒有好用的索引,新的索引將在聯接的每一行上從新估算,N是顯示在possible_keys列中索引的位圖,而且是冗餘的。 |
備註: 此處將僅做爲存檔, 後續更新將在 這裏 中處理. 2019年10月18日15:53:53
show profile 分析SQL性能工具(檢測數據存在臨時表中)
SET profiling=1;
show profiles
, show profile
, show profile for query <id>
視圖是虛擬的表。與包含數據的表不同,視圖只包含使用時動態檢索數據的查詢。
視圖包含的是一個SQL查詢, 它不包含數據!!我的理解: 視圖即別名~
-- 建立視圖 CREATE VIEW `視圖名` AS SELECT ... -- 查看建立指定視圖的語句 SHOW CREATE VIEW `視圖名`; -- 刪除視圖 DROP VIEW `視圖名`; -- 更新視圖 CREATE OR REPLACE VIEW AS SELECT ...
視圖的做用:
爲何使用視圖:
- 重用SQL語句。
- 簡化複雜的SQL操做。在編寫查詢後,能夠方便地重用它而沒必要知道它的基本查詢細節。
- 使用表的組成部分而不是整個表。
- 保護數據。能夠給用戶授予表的特定部分的訪問權限而不是整個表的訪問權限。
- 更改數據格式和表示。視圖可返回與底層表的表示和格式不一樣的數據。
視圖的規則和限制
<u>建立可重用的視圖</u>
建立不受特定數據限制的視圖是一種好辦法。例如,上面建立的視圖返回生產全部產品的客戶而不只僅是 生產TNT2 的客戶。擴展視圖的範圍不只使得它能被重用,並且甚至更有用。這樣作不須要建立和維護多個相似視圖。
<u>將視圖用於檢索</u>
通常,應該將視圖用於檢索( SELECT 語句)而不用於更新( INSERT 、 UPDATE 和 DELETE )。
簡單來講: 存儲過程是爲之後的使用而保存的一條或多條MySQL語句的集合.
使用存儲過程的緣由:
提升性能
使用存儲過程比使用單獨的SQL語句要快
存儲過程通常並不顯示結果, 而是把結果返回給你指定的變量.
-- 調用存儲過程 CALL `過程名`() -- 更改mysql命令行客戶端語句分隔符, 除了 \ 符號外,其餘字符均可以做爲語句分隔符. DELIMITER // -- 建立存儲過程 CREATE PROCEDURE `過程名`() BEGIN END// -- 還原mysql命令行客戶端語句分隔符 DELIMITER ; -- 刪除存儲過程 DROP PROCEDURE IF EXISTS `過程名`; -- 檢查(查看)存儲過程 SHOW CREATE PROCEDURE `過程名`; -- 查看存儲過程的元數據(建立時間, 建立人..) SHOW PROCEDURE STATUS LIKE '過程名';
MySQL支持存儲過程的參數:
<u>參數的數據類型</u>
存儲過程的參數容許的數據類型與表中使用的數據類型相同。!!! 記錄集不是容許的類型,所以,不能經過一個參數返回多個行和列。
一個簡單的示例: 計算商品的最低、最高、平均價格
DELIMITER // CREATE PROCEDURE productpricing( OUT pl DECIMAL(8,2), OUT ph DECIMAL(8,2), OUT pm DECIMAL(8,2) ) BEGIN SELECT Min(prod_price) INTO pl FROM products; SELECT Max(prod_price) INTO ph FROM products; SELECT Avg(prod_price) INTO pm FROM products; END// DELIMITER ; CALL productpricing(@pricelow, @pricehigh, @priceaverage); SELECT @pricelow, @pricehigh, @priceaverage;
另外一個簡單示例: 接受訂單號並返回該訂單的統計
DELIMITER // CREATE PROCEDURE ordertotal(IN onumber INT, OUT ototal DECIMAL(6,2)) BEGIN SELECT Sum(item_price*quantity) FROM orderitems WHERE order_num = onumber INTO ototal; END// DELIMITER ; CALL ordertotal(20005, @total); SELECT @total;
<u>變量(variable)</u>
內存中一個特定的位置,用來臨時存儲數據。變量名不區分大小寫
@變量名
, 僅對當前鏈接有效
可使用 SET @變量=值
或 SELECT @變量:=值;
來賦值給變量
-- SET @變量=值; -- 在SELECT中, = 是比較符, 所以須要用 := SELECT @變量:=值;
全局變量
對當前mysqld實例有效
SET GLOBAL 變量名=值; SET @@變量名=值;
須要 SUPER 權限, 需從新鏈接後才生效.
會話變量
只對當前鏈接有效
-- 設置變量值 SET SESSION 變量名=值; -- LOCAL 是SESSION的同義詞 SET LOCAL 變量名=值; -- 不指定 GLOBAL,SESSION 時, 默認就是 SESSION -- 此處沒有 @ SET 變量名=值; -- 若存在會話變量則返回, 不然返回全局變量. SELECT @@變量名; SHOW VARIABLES LIKE '變量名';
declare
定義, 僅在當前塊有效(begin...end)
條件語句
IF ... THEN ELSEIF ... THEN ELSE END IF;
循環語句
-- WHILE 循環 WHILE ... DO END WHILE; -- --------------------------------------------------- -- REPEAT 循環 REPEAT UNTIL ... END REPEAT; -- --------------------------------------------------- -- LOOP 循環 loop標記: LOOP IF ... THEN LEAVE loop標記; END IF; END LOOP; -- LOOP 示例 CREATE PROCEDURE proc_loop () BEGIN declare i int default 0; loop_label: loop select i; set i=i+1; if i>=5 then leave loop_label; end if; end loop; END
動態執行SQL語句
PREPARE 變量 FROM "..."; EXECUTE 變量 USING @p1; DEALLOCATE PREPARE 變量; -- 示例 SET @num = 20005; PREPARE stmt FROM 'SELECT * FROM orders WHERE order_num = ?'; EXECUTE stmt USING @num; DEALLOCATE PREPARE stmt;
參數只能使用 <u>用戶變量</u>來傳遞
在存儲過程當中包含:
-- Name: ordertotal -- Parameters: onumber = order number -- taxable = 0 if not taxable, 1 if taxable -- ototal = order total VARIABLES CREATE PROCEDURE ordertotal( IN onumber INT, IN taxable BOOLEAN, OUT ototal DECIMAL(8,2) ) COMMENT '獲取訂單總額, 可選增長營業稅' BEGIN -- Declare variable for total DECLARE total DECIMAL(8,2); -- Declare tax percentage DECLARE taxrate INT DEFAULT 6; -- Get the order total SELECT sum(item_price*quantity) FROM orderitems WHERE order_num = onumber INTO total; -- Is this taxable? IF taxable THEN -- yes, so add taxrate to the total SELECT total+(total/100*taxrate) INTO total; END IF; -- And finally, save to out vaiable SELECT total INTO ototal; END;
遊標(cursor)是一個存儲在MySQL服務器上的數據庫查詢,它不是一條 SELECT 語句,而是被該語句檢索出來的結果集。在存儲了遊標以後,應用程序能夠根據須要滾動或瀏覽其中的數據。
<u>只能用於存儲過程</u> 不像多數DBMS,MySQL遊標只能用於存儲過程(和函數)。
使用遊標的步驟:
關閉遊標
- 遊標關閉後必須從新打開才能再次使用
- 存儲過程結束時會自動將已打開的遊標關閉
CREATE PROCEDURE `processorders`() BEGIN -- create cursor DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- open cursor OPEN ordernumbers; -- close cursor CLOSE ordernumbers; END
書上示例
DROP PROCEDURE processorders; CREATE PROCEDURE processorders() BEGIN DECLARE done BOOLEAN DEFAULT 0; DECLARE o INT; DECLARE t DECIMAL(8,2); -- Declare CURSOR DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- Declare continue handler -- FOR NOT FOUND DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1; -- Create a table to store the results CREATE TABLE IF NOT EXISTS ordertotals( order_num INT, total DECIMAL(8,2) ); OPEN ordernumbers; REPEAT -- Get order number FETCH ordernumbers INTO o; CALL ordertotal(o,1,t); INSERT INTO ordertotals(order_num,total) VALUES(o,t); UNTIL done END REPEAT; -- Close the cursor CLOSE ordernumbers; END
改進示例
DROP PROCEDURE processorders; CREATE PROCEDURE processorders() BEGIN DECLARE done BOOLEAN DEFAULT 0; DECLARE o INT; DECLARE t DECIMAL(8,2); -- Declare CURSOR DECLARE ordernumbers CURSOR FOR SELECT order_num FROM orders; -- Declare continue handler -- FOR NOT FOUND DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; -- Create a table to store the results CREATE TABLE IF NOT EXISTS ordertotals( order_num INT, total DECIMAL(8,2) ); OPEN ordernumbers; FETCH ordernumbers INTO o; -- 避免插入 (NULL,NULL) 到 ordertotals 表 WHILE NOT done DO CALL ordertotal(o,1,t); SELECT o,t; INSERT INTO ordertotals(order_num,total) VALUES(o,t); FETCH ordernumbers INTO o; END WHILE; -- Close the cursor CLOSE ordernumbers; END
觸發器是MySQL響應如下任意語句而自動執行的一條MySQL語句(或位於 BEGIN 和 END 語句之間的一組語
句):
建立觸發器可能須要特殊的安全訪問權限,可是,觸發器的執行是自動的。若是 INSERT 、 UPDATE 或 DELETE 語句可以執行,則相關的觸發器也能執行。
在建立觸發器時,須要給出4條信息:
觸發器按每一個表每一個事件每次地定義,每一個表每一個事件每次只容許一個觸發器。所以,每一個表最多支持6個觸發器(每條 INSERT 、 UPDATE和 DELETE 的以前和以後)。單一觸發器不能與多個事件或多個表關聯,因此,若是你須要一個對 INSERT 和 UPDATE 操做執行的觸發器,則應該定義兩個觸發器。
<u>僅支持表</u> 只有表才支持觸發器,視圖不支持(臨時表也不支持)
-- 建立觸發器 CREATE TRIGGER `觸發器名` AFTER|BEFORE INSERT|UPDATE|DELETE ON `表名` FOR EACH ROW ... -- 刪除觸發器 DROP TRIGGER `觸發器名`;
<u>BEFORE 或 AFTER ?</u> 一般,將 BEFORE 用於數據驗證和淨化(目的是保證插入表中的數據確實是須要的數據)。本提示也適用於 UPDATE 觸發器。
-- mysql中沒法執行: 禁止觸發器返回結果集 -- ERROR 1415 (0A000): Not allowed to return a result set from a trigger CREATE TRIGGER neworder AFTER INSERT ON orders FOR EACH ROW SELECT NEW.order_num;
在任意訂單被刪除前將執行此觸發器。它使用一條 INSERT 語句將 OLD 中的值(要被刪除的訂單)保存到一個名爲 archive_orders 的存檔表中
CREATE TRIGGER deleteorders BEFORE DELETE ON orders FOR EACH ROW BEGIN INSERT INTO archive_orders(order_num,order_date,cust_id) VALUES(OLD.order_num,OLD.order_date,OLD.cust_id); END
使用 BEFORE DELETE 觸發器的優勢(相對於 AFTER DELETE 觸發器來講)爲,若是因爲某種緣由,訂單不能存檔, DELETE 自己將被放棄。
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors FOR EACH ROW SET NEW.vend_state=Upper(NEW.vend_state);
事務存在的問題:
參考: https://www.cnblogs.com/balfi...
數據庫事務隔離級別:
存在全部問題, 最低的隔離級別。一個事務能夠讀取另外一個事務並未提交的更新結果。
解決"髒讀", 大部分數據庫採用的默認隔離級別。一個事務的更新操做結果只有在該事務提交以後,另外一個事務才能夠的讀取到同一筆數據更新後的結果。
解決"不可重複讀", 整個事務過程當中,對同一筆數據的讀取結果是相同的,無論其餘事務是否在對共享數據進行更新,也無論更新提交與否。
解決"幻讀", 最高隔離級別。全部事務操做依次順序執行。注意這會致使併發度降低,性能最差。一般會用其餘併發級別加上相應的併發鎖機制來取代它。
MySQL 默認級別是 repeatable-read
術語
-- 標識事務開始 START TRANSACTION; -- ROLLBACK; -- COMMIT;
<u>哪些語句能夠回退?</u> 事務處理用來管理 INSERT 、 UPDATE 和DELETE 語句。你不能回退 SELECT 語句。(這樣作也沒有什麼意義。)你不能回退 CREATE 或 DROP 操做。事務處理塊中可使用這兩條語句,但若是你執行回退,它們不會被撤銷。
-- 關閉本次鏈接的mysql自動提交 SET autocommit=0;
爲了支持回退部分事務處理,必須能在事務處理塊中合適的位置放置佔位符。這樣,若是須要回退,能夠回退到某個佔位符。
-- 建立保留點 SAVEPOINT `保留點名`; -- 回退到指定保留點 ROLLBACK TO `保留點名`;
術語
-- 查看可用的字符集 SHOW CHARACTER SET; SHOW CHARSET; -- 查看可用的校對 SHOW COLLATION; -- 查看當前使用的字符集 SHOW VARIABLES LIKE 'character%'; SHOW VARIABLES LIKE 'collation%';
一般系統管理在安裝時定義一個默認的字符集和校對。此外,也能夠在建立數據庫時,指定默認的字符集和校對。
實際上,字符集不多是服務器範圍(甚至數據庫範圍)的設置。不一樣的表,甚至不一樣的列均可能須要不一樣的字符集,並且二者均可以在建立表時指定。
CREATE TABLE mytable( column1 INT, column2 VARCHAR(10), -- 指定特定列使用特定的字符集及校對 column3 VARCHAR(10) CHARSET latin1 COLLATE latin1_general_ci ) Engine=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- 臨時區分大小寫排序 SELECT * FROM mytable ORDER BY column3 COLLATE latin1_general_cs;
<u>SELECT 的其餘 COLLATE 子句</u> 除了這裏看到的在 ORDER BY子句 中使用之外, COLLATE 還能夠用於 GROUP BY 、 HAVING 、彙集函數、別名等。
串能夠在字符集之間進行轉換。爲此,使用 Cast() 或 Convert ()函數
用戶應該對他們須要的數據具備適當的訪問權,既不能多也不能少
<u>不要使用 root</u> 應該嚴肅對待 root 登陸的使用。僅在絕對須要時使用它(或許在你不能登陸其餘管理帳號時使用)。不該該在平常的MySQL操做中使用 root 。
-- 查看當前全部用戶 SELECT host,user FROM mysql.user; -- 建立用戶 CREATE USER 用戶名 IDENTIFIED BY '密碼'; -- 重命名用戶帳號 RENAME USER 舊用戶名 TO 新用戶名; -- 刪除用戶帳號 DROP USER 用戶名; -- 修改用戶名 RENAME USER '舊用戶名@..' TO '新用戶名@..'; -- 更改本身口令 SET PASSWORD = Password('密碼'); -- 更改指定用戶口令 SET PASSWORD FOR 用戶名 = Password('密碼'); ALTER USER 用戶名 IDENTIFIED BY '密碼';
mysqladmin
-- 設置密碼(若修改密碼, 則需輸入原密碼) mysqladmin -u root password
設置訪問權限
-- 查看賦予當前用戶帳號的權限 SHOW GRANTS; -- 查看賦予某個用戶帳號的權限 -- 完整的用戶定義: user@host -- 不指定主機名時使用默認的主機名 % -- 默認查詢: 用戶名@'%' SHOW GRANTS FOR 用戶名; -- 建立帳號並賦予最高權限 GRANT ALL ON *.* TO '用戶名'@'%' IDENTIFIED BY '密碼' WITH GRANT OPTION; -- 賦予 SELECT 權限 GRANT SELECT ON `數據庫名`.* TO 用戶名; -- 撤銷 SELECT 權限 REVOKE SELECT ON `數據庫名`.* FROM 用戶名;
新用戶的權限爲: GRANT USAGE ON *.* TO 'yjx'@'%'
, 即沒有任何權限.
經常使用權限
權限 | 說明 |
---|---|
ALL | 除GRANT OPTION外的全部權限 |
SELECT | 僅查詢 |
SELECT,INSERT | 查詢和插入 |
USAGE | 無權限 |
目標
目標 | 說明 |
---|---|
數據庫名.* | 指定數據庫全部 |
數據庫名.表 | 指定數據庫的指定表 |
數據庫名.存儲過程 | 指定數據庫的指定存儲過程 |
. | 全部數據庫 |
用戶
用戶 | 說明 |
---|---|
用戶名@ip | 指定ip登錄的用戶 |
用戶名@'192.168.1.%' | 指定ip段登錄的用戶 |
用戶名@'%' | 任意ip下登錄的用戶 |
用戶名@localhost | 本地登錄的用戶 |
用戶名@‘192.168.200.0/255.255.255.0’ |
(子網掩碼配置) |
GRANT 和 REVOKE 可在幾個層次上控制訪問權限:
BACKUP TABLE
或 SELECT INTO OUTFILE
語句轉儲數據到外部文件, 使用 RESTORE TABLE
還原mysqldump 備份
# 導出爲文本文件 mysqldump -uroot -p -B '數據庫名1' '數據庫名2' > /tmp/mysql.bak # 直接導出爲壓縮文件 mysqldump -uroot -p -B '數據庫名1' '數據庫名2' | gzip > /tmp/mysql.bak.gz # -A, --all-databases 備份全部庫 # -B, --database 備份指定庫 # -F 刷新binlog日誌 # -x,--lock-all-tables # -l,--locktables # --single-transaction 適合innodb事務數據庫備份 # --default-character-set=utf8 字符集 # --triggers 備份觸發器 # -d, --no-data 只備份表結構 # -t, --no-create-info 只備份數據, 無 create table 語句 # --master-data 增長binlog日誌文件名及對應的位置點 # 生產環境全備份 # 進行數據庫全備,(生產環境還經過定時任務每日凌晨執行) mysqldump -uroot -p123456 -S /data/3306/mysql.sock --single-transaction -F -B "數據庫名" | gzip > /server/backup/mysql_$(date +%F).sql.gz # innodb引擎備份 mysqldump -u$MYUSER -p$MYPASS -S $MYSOCK -F --single-transaction -A | gzip > $DATA_FILE # myisam引擎備份 mysqldump -u$MYUSER -p$MYPASS -S $MYSOCK -F -A -B --lock-all-tables |gzip >$DATA_FILE
恢復
# 直接從sql文件恢復 mysql -uroot -p < /tmp/mysql.sql # 從壓縮文件中恢復 gunzip < /tmp/mysql.bak.gz | mysql -uroot -p
SHOW PROCESSLIST
顯示全部活動進程(以及它們的線程ID和執行時間)。你還能夠用 KILL
命令終結某個特定的進程(使用這個命令須要做爲管理員登陸)。在恢復數據時,可能會導入大量的數據。
此時有一些技巧能夠提升導入速度:
導入時禁用索引, 導入結束後再開啓索引
ALTER TABLE 表名 disable keys; ALTER TABLE 表名 enable keys;
對於InnoDB, 因爲默認 autocommit=1
, 即每條語句做爲單獨的事務, 所以能夠將多條合併成少數幾條事務以加快速度.
-- 關閉自動提交, 也能夠用 begin 或 start transaction set autocommit = 0; -- 插入若干條提交 insert into ...; ... insert into ...; commit; -- 插入若干條提交 insert into ...; ... insert into ...; commit; set autocommit = 1;
mysql> show open tables where in_use > 0; +----------+-------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------+--------+-------------+ | test_yjx | user | 1 | 0 | +----------+-------+--------+-------------+
備註: 此處將僅做爲存檔, 後續更新將在 這裏 中處理. 2019年10月18日15:53:53
[mysqld] # ...此處省略了其餘與慢查詢日誌不相關的配置 ############### 慢查詢日誌 ################ slow_query_log=1 # 打開慢查詢日誌 log_output=file # 日誌記錄位置 slow_query_log_file=/var/run/mysqld/mysqld-slow.log # 慢查詢日誌記錄文件 long_query_time=3 # 慢查詢時間閥值
也能夠在mysql shell中修改相關 global 變量來開啓(重啓後失效)
當前會話中臨時啓用慢日誌分析
set global slow_query_log = 1; set long_query_time = 0;
測試慢查詢是否有效
mysql> select sleep(4);
此時日誌或多出一條:
# Time: 190215 15:16:14 # User@Host: root[root] @ localhost [] # Query_time: 11.000205 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 SET timestamp=1550214974; select sleep(11);
# 分析慢日誌 mysqldumpslow -a -n 50 -s c /var/run/mysqld/mysqld-slow.log # 參數說明 --verbose 版本 --debug 調試 --help 幫助 -v 版本 -d 調試模式 -s ORDER 排序方式 what to sort by (al, at, ar, c, l, r, t), 'at' is default al: average lock time ar: average rows sent at: average query time c: count l: lock time r: rows sent t: query time -r 反轉順序,默認文件倒序拍。reverse the sort order (largest last instead of first) -t NUM 顯示前N條just show the top n queries -a 不要將SQL中數字轉換成N,字符串轉換成S。don't abstract all numbers to N and strings to 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN 正則匹配;grep: only consider stmts that include this string -h HOSTNAME mysql機器名或者IP;hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l 總時間中不減去鎖定時間;don't subtract lock time from total time
超鍵 惟一標識元組(數據行)的屬性集叫作超鍵.
好比普通表中主鍵 id 是超鍵, (id, name) 也是超鍵, (id, age) 也是超鍵, 由於均可以惟一標識元組(數據行).
候選鍵 最小超鍵, 不包含無用字段, 也稱爲 碼.
以 超鍵 中的例子來說, (id, name) 由於包含無用的 name 字段, 因此顯然它不是候選鍵. 而單獨的 id 是候選鍵
主鍵 從候選鍵中選擇一個, 也稱爲 主碼.
外鍵 數據表中的字段是別的數據表的主鍵, 則稱它爲外鍵.
主屬性 包含在任意候選鍵中的屬性稱爲主屬性.
全部範式(按照嚴格順序排列):
關鍵: 表中任何屬性都是原子性的, 不可再分.
解釋: 字段不要是能夠由其餘字段組合/計算的.
須要保證表中的非主屬性與候選鍵(碼)徹底依賴 (即消除了部分依賴)
須要保證表中的非主屬性與候選鍵(碼)不存在傳遞依賴
一般只要求到這個級別.
消除主屬性之間的部分依賴和傳遞依賴
這裏的
- 部分依賴 也稱爲 部分函數依賴
- 傳遞依賴 也稱爲 傳遞函數依賴
一般要求: <u>3NF</u>
根據實際狀況, 必須時能夠新增冗餘字段來提升查詢效率, 須要權衡.
範式的嚴格程度是依次遞增, 且高級別範式確定是知足低級別範式的.
該部分主要來自: https://juejin.im/post/5c2c53...
show engines
Engine | Support | Comment |
---|---|---|
InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys |
MyISAM | YES | MyISAM storage engine |
MyISAM | Innodb | |
---|---|---|
文件格式 | 數據和索引是分別存儲的,數據.MYD ,索引.MYI |
數據和索引是集中存儲的,.ibd |
文件可否移動 | 能,一張表就對應.frm 、MYD 、MYI 3個文件 |
否,由於關聯的還有data 下的其它文件 |
記錄存儲順序 | 按記錄插入順序保存 | 按主鍵大小有序插入 |
空間碎片(刪除記錄並flush table 表名 以後,表文件大小不變) |
產生。定時整理:使用命令optimize table 表名 實現 |
不產生 |
事務 | 不支持 | 支持 |
外鍵 | 不支持 | 支持 |
鎖支持(鎖是避免資源爭用的一個機制,MySQL鎖對用戶幾乎是透明的) | 表級鎖定 | 行級鎖定、表級鎖定,鎖定力度小併發能力高 |
鎖擴展表級鎖(
table-level lock
):lock tables <table_name1>,<table_name2>... read/write
,unlock tables <table_name1>,<table_name2>...
。其中read
是共享鎖,一旦鎖定任何客戶端都不可讀;write
是獨佔/寫鎖,只有加鎖的客戶端可讀可寫,其餘客戶端既不可讀也不可寫。鎖定的是一張表或幾張表。行級鎖(
row-level lock
):鎖定的是一行或幾行記錄。共享鎖:select * from <table_name> where <條件> LOCK IN SHARE MODE;
,對查詢的記錄增長共享鎖;select * from <table_name> where <條件> FOR UPDATE;
,對查詢的記錄增長排他鎖。這裏值得注意的是:innodb
的行鎖,實際上是一個子範圍鎖,依據條件鎖定部分範圍,而不是就映射到具體的行上,所以還有一個學名:間隙鎖。好比select * from stu where id < 20 LOCK IN SHARE MODE
會鎖定id
在20
左右如下的範圍,你可能沒法插入id
爲18
或22
的一條新紀錄。
create.sql
######################################## # MySQL Crash Course # http://www.forta.com/books/0672327120/ # Example table creation scripts ######################################## ######################## # Create customers table ######################## CREATE TABLE customers ( cust_id int NOT NULL AUTO_INCREMENT, cust_name char(50) NOT NULL , cust_address char(50) NULL , cust_city char(50) NULL , cust_state char(5) NULL , cust_zip char(10) NULL , cust_country char(50) NULL , cust_contact char(50) NULL , cust_email char(255) NULL , PRIMARY KEY (cust_id) ) ENGINE=InnoDB; ######################### # Create orderitems table ######################### CREATE TABLE orderitems ( order_num int NOT NULL , order_item int NOT NULL , prod_id char(10) NOT NULL , quantity int NOT NULL , item_price decimal(8,2) NOT NULL , PRIMARY KEY (order_num, order_item) ) ENGINE=InnoDB; ##################### # Create orders table ##################### CREATE TABLE orders ( order_num int NOT NULL AUTO_INCREMENT, order_date datetime NOT NULL , cust_id int NOT NULL , PRIMARY KEY (order_num) ) ENGINE=InnoDB; ####################### # Create products table ####################### CREATE TABLE products ( prod_id char(10) NOT NULL, vend_id int NOT NULL , prod_name char(255) NOT NULL , prod_price decimal(8,2) NOT NULL , prod_desc text NULL , PRIMARY KEY(prod_id) ) ENGINE=InnoDB; ###################### # Create vendors table ###################### CREATE TABLE vendors ( vend_id int NOT NULL AUTO_INCREMENT, vend_name char(50) NOT NULL , vend_address char(50) NULL , vend_city char(50) NULL , vend_state char(5) NULL , vend_zip char(10) NULL , vend_country char(50) NULL , PRIMARY KEY (vend_id) ) ENGINE=InnoDB; ########################### # Create productnotes table ########################### CREATE TABLE productnotes ( note_id int NOT NULL AUTO_INCREMENT, prod_id char(10) NOT NULL, note_date datetime NOT NULL, note_text text NULL , PRIMARY KEY(note_id), FULLTEXT(note_text) ) ENGINE=MyISAM; ##################### # Define foreign keys ##################### ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num); ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id); ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id); ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);
populate.sql
######################################## # MySQL Crash Course # http://www.forta.com/books/0672327120/ # Example table population scripts ######################################## ########################## # Populate customers table ########################## INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10001, 'Coyote Inc.', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'Y Lee', 'ylee@coyote.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact) VALUES(10002, 'Mouse House', '333 Fromage Lane', 'Columbus', 'OH', '43333', 'USA', 'Jerry Mouse'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10003, 'Wascals', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'rabbit@wascally.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email) VALUES(10004, 'Yosemite Place', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Y Sam', 'sam@yosemite.com'); INSERT INTO customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact) VALUES(10005, 'E Fudd', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'E Fudd'); ######################## # Populate vendors table ######################## INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1001,'Anvils R Us','123 Main Street','Southfield','MI','48075', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1002,'LT Supplies','500 Park Street','Anytown','OH','44333', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1003,'ACME','555 High Street','Los Angeles','CA','90046', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1004,'Furball Inc.','1000 5th Avenue','New York','NY','11111', 'USA'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1005,'Jet Set','42 Galaxy Road','London', NULL,'N16 6PS', 'England'); INSERT INTO vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country) VALUES(1006,'Jouets Et Ours','1 Rue Amusement','Paris', NULL,'45678', 'France'); ######################### # Populate products table ######################### INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV01', 1001, '.5 ton anvil', 5.99, '.5 ton anvil, black, complete with handy hook'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV02', 1001, '1 ton anvil', 9.99, '1 ton anvil, black, complete with handy hook and carrying case'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('ANV03', 1001, '2 ton anvil', 14.99, '2 ton anvil, black, complete with handy hook and carrying case'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('OL1', 1002, 'Oil can', 8.99, 'Oil can, red'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FU1', 1002, 'Fuses', 3.42, '1 dozen, extra long'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('SLING', 1003, 'Sling', 4.49, 'Sling, one size fits all'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('TNT1', 1003, 'TNT (1 stick)', 2.50, 'TNT, red, single stick'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('TNT2', 1003, 'TNT (5 sticks)', 10, 'TNT, red, pack of 10 sticks'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FB', 1003, 'Bird seed', 10, 'Large bag (suitable for road runners)'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('FC', 1003, 'Carrots', 2.50, 'Carrots (rabbit hunting season only)'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('SAFE', 1003, 'Safe', 50, 'Safe with combination lock'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('DTNTR', 1003, 'Detonator', 13, 'Detonator (plunger powered), fuses not included'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('JP1000', 1005, 'JetPack 1000', 35, 'JetPack 1000, intended for single use'); INSERT INTO products(prod_id, vend_id, prod_name, prod_price, prod_desc) VALUES('JP2000', 1005, 'JetPack 2000', 55, 'JetPack 2000, multi-use'); ####################### # Populate orders table ####################### INSERT INTO orders(order_num, order_date, cust_id) VALUES(20005, '2005-09-01', 10001); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20006, '2005-09-12', 10003); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20007, '2005-09-30', 10004); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20008, '2005-10-03', 10005); INSERT INTO orders(order_num, order_date, cust_id) VALUES(20009, '2005-10-08', 10001); ########################### # Populate orderitems table ########################### INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 1, 'ANV01', 10, 5.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 2, 'ANV02', 3, 9.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 3, 'TNT2', 5, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20005, 4, 'FB', 1, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20006, 1, 'JP2000', 1, 55); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20007, 1, 'TNT2', 100, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20008, 1, 'FC', 50, 2.50); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 1, 'FB', 1, 10); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 2, 'OL1', 1, 8.99); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 3, 'SLING', 1, 4.49); INSERT INTO orderitems(order_num, order_item, prod_id, quantity, item_price) VALUES(20009, 4, 'ANV03', 1, 14.99); ############################# # Populate productnotes table ############################# INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(101, 'TNT2', '2005-08-17', 'Customer complaint: Sticks not individually wrapped, too easy to mistakenly detonate all at once. Recommend individual wrapping.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(102, 'OL1', '2005-08-18', 'Can shipped full, refills not available. Need to order new can if refill needed.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(103, 'SAFE', '2005-08-18', 'Safe is combination locked, combination not provided with safe. This is rarely a problem as safes are typically blown up or dropped by customers.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(104, 'FC', '2005-08-19', 'Quantity varies, sold by the sack load. All guaranteed to be bright and orange, and suitable for use as rabbit bait.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(105, 'TNT2', '2005-08-20', 'Included fuses are short and have been known to detonate too quickly for some customers. Longer fuses are available (item FU1) and should be recommended.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(106, 'TNT2', '2005-08-22', 'Matches not included, recommend purchase of matches or detonator (item DTNTR).' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(107, 'SAFE', '2005-08-23', 'Please note that no returns will be accepted if safe opened using explosives.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(108, 'ANV01', '2005-08-25', 'Multiple customer returns, anvils failing to drop fast enough or falling backwards on purchaser. Recommend that customer considers using heavier anvils.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(109, 'ANV03', '2005-09-01', 'Item is extremely heavy. Designed for dropping, not recommended for use with slings, ropes, pulleys, or tightropes.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(110, 'FC', '2005-09-01', 'Customer complaint: rabbit has been able to detect trap, food apparently less effective now.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(111, 'SLING', '2005-09-02', 'Shipped unassembled, requires common tools (including oversized hammer).' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(112, 'SAFE', '2005-09-02', 'Customer complaint: Circular hole in safe floor can apparently be easily cut with handsaw.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(113, 'ANV01', '2005-09-05', 'Customer complaint: Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead.' ); INSERT INTO productnotes(note_id, prod_id, note_date, note_text) VALUES(114, 'SAFE', '2005-09-07', 'Call from individual trapped in safe plummeting to the ground, suggests an escape hatch be added. Comment forwarded to vendor.' );