SQL資料庫和語法
增刪改查
SELECT prod_id, prod_name, prod_price
FROM Products;
SELECT *
FROM Products;
//增
INSERT INTO Customers
VALUES(' ',' ');
UPDATE Customers
SET cust_email = ' '
WHERE cust_id = ' ';
DELETE FROM Customers
WHERE cust_id = ' ';
建立表
CREATE TABLE Products ( prod_id CHAR(10) NOT NULL, vend_id CHAR(10) NOT NULL, prod_name CHAR(254) NOT NULL, prod_price DECIMAL(8,2) NOT NULL, prod_desc VARCHAR(1000) NULL );
限制查詢結果
這種方式只支援MySQL、 MariaDB、 PostgreSQL 或者 SQLite資料庫,SQL只是大致相同
SELECT prod_name
FROM Products
LIMIT 5 OFFSET 5;
IF表示式
IF( expr1 , expr2 , expr3 )
expr1 的值為 TRUE,則返回值為 expr2
expr1 的值為FALSE,則返回值為 expr3
排序資料
ORDER BY 位於WHERE之後;資料排序預設是升序排序,為了使用降序排序,必須指定DESC關鍵字
SELECT * FROM Products ORDER BY prod_name DESC;//降序排列 SELECT * FROM Products ORDER BY prod_price, prod_name//僅在多個行具有相同的 prod_price 值時才對產品按 prod_name 進行排序。如果 prod_price 列中所有的值都是唯一的,則不會按 prod_name 排序
過濾資料
資料過濾不應該在應用層完成,首先是會消耗應用效能,另外佔用額外的頻寬資源。
WHERE a <= 10【條件是a小於等於10】 WHERE a <> 10【例舉出a不為10的所有結果】或者 WHERE a!=10
單引號用來限定字串。如果將值與字串型別的列進行比較,就需要限定引號。用來與數值列進行比較的值不用引號
SELECT * FROM Products
WHERE prod_price = 3.49;
WHERE prod_price IS NULL//空值檢查
可以通過AND 子句或 OR 子句組合多個WHERE 條件
WHERE vend_id = 'DLL01' AND prod_price <= 4;//WHERE 子句可以包含任意數目的 AND 和 OR 操作符,AND操作符優先
CASE
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
WHEN conditionN THEN resultN
ELSE result
END;
CASE compare_value
WHEN condition1 THEN result1
WHEN condition2 THEN result2
WHEN conditionN THEN resultN
ELSE result
END;
IN操作符
IN 操作符用來指定條件範圍,範圍中的每個條件都可以進行匹配。 IN 取一組由逗號分隔、括在圓括號中的合法值
WHERE vend_id IN ( 'DLL01', 'BRS01' )
範圍值檢查BETWEEN AND
SELECT prod_name, prod_price
FROM Products
WHERE prod_price BETWEEN 5 AND 10;
萬用字元&LIKE
最常使用的萬用字元是百分號( %)。在搜尋串中, %表示任何字元出現任意次數,但是%無法匹配NULL
另一個有用的萬用字元是下劃線( _)。下劃線的用途與%一樣,但它只匹配單個字元
方括號( [])萬用字元用來指定一個字符集,它必須匹配指定位置(萬用字元的位置)的一個字元
WHERE cust_contact LIKE '[JM]%'
WHERE cust_contact LIKE '[^JM]%'//否定集合
MYSQL拼接
SELECT Concat(vend_name, ' (', vend_country, ')')
FROM Vendors
ORDER BY vend_name;
算術計算
SELECT prod_id,
quantity,
item_price,
quantity*item_price AS expanded_price
數值函式
聚集函式
對某些行執行的函式,計算並返回一個值;AVG()通過對錶中行數計數並計算其列值之和,求得該列的平均值;COUNT()函式進行計數;SUM()用來返回指定列值的和
SELECT COUNT(*) AS num_cust
FROM Customers;
如果只需要聚集不同值,需要指定指定 DISTINCT 引數
SELECT AVG(DISTINCT prod_price) AS avg_price
FROM Products
WHERE vend_id = 'DLL01';
分組GROUP BY&HAVING
HAVING允許過濾分組
SELECT cust_id, COUNT(*) AS orders
FROM Orders
GROUP BY cust_id
HAVING COUNT(*) >= 2;
等值聯結表
SELECT vend_name, prod_name, prod_price
FROM Vendors, Products
WHERE Vendors.vend_id = Products.vend_id;
由沒有聯結條件的表關係返回的結果稱為笛卡爾積(檢索出的行的數目將是第一個表中的行數乘以第二個表中的行數)
內聯結
內聯結是查找出同時存在於兩張表中的資料,
SELECT vend_name, prod_name, prod_price
FROM Vendors INNER JOIN Products
ON Vendors.vend_id = Products.vend_id;
左聯結&右聯結
左聯結,會將左側表中的資料全部取出來
SELECT Customers.cust_id, Orders.order_num
FROM Customers LEFT OUTER JOIN Orders
ON Customers.cust_id = Orders.cust_id;
子查詢
SQL 允許建立子查詢( subquery),即巢狀在其他查詢中的查詢;
另外,如果在SELECT 語句中操作多個表,就應使用完全限定列名來避免歧義。
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM Orders
WHERE cust_id = cust_id) AS orders
FROM Customers
ORDER BY cust_name;
事務
事務用於為SQL資料庫提供原子性操作
SET AUTOCOMMIT=0 禁止自動提交
SET AUTOCOMMIT=1 開啟自動提交
SHOW VARIABLES LIKE '%AUTOCOMMIT%' #檢視是否開啟自動提交
Set Autocommit=0;
開啟事務:SET TRANSACTION [ READ WRITE | READ ONLY ];
提交:COMMIT;
回滾:ROLLBACK
事務ACID
原子性、一致性、隔離性、永續性
MySQL 中執行事務以 begin 或者 start transaction 開始,然後執行一系列操作,最後要執行 commit 操作,事務才算結束。當然,如果進行回滾操作(rollback),事務也會結束
隔離級別
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交(read-uncommitted) | 是 | 是 | 是 |
讀已提交(read-committed) | 否 | 是 | 是 |
可重複讀(repeatable-read) | 否 | 否 | 是 |
序列化(serializable) | 否 | 否 | 否 |
髒讀指的是讀到了其他事務未提交的資料,資料可能回滾
不可重複讀指的在同一事務內,不同的時刻讀到的同一批資料可能是不一樣的
幻讀:幻讀指當用戶讀取某一範圍的資料行時,另一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的資料行時,會發現有新的“幻影” 行
序列化:讀的時候加共享鎖,也就是其他事務可以併發讀,但是不能寫。寫的時候加排它鎖,其他事務不能併發寫也不能併發讀
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
MVCC(多版本併發控制)
MySQL中,每一行資料會有兩個隱藏欄位:trx_id、rollback_pointer; 事務ID只有執行第一條更新語句後才會生成。rollback_pointer用於找到當前行的歷史資料。
在可重複讀的隔離級別下,Mysql只會在第一次執行查詢語句的時候生成快照read-view,在讀已提交的隔離級別下,每次查詢語句都會重新生成快照。
MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工作
Read-View中主要就是有個列表來儲存我們系統中當前活躍著的讀寫事務,也就是begin了還未提交的事務:
- low_trx_id表示該SQL啟動時,當前事務連結串列中最大的事務id編號,也就是最近建立的除自身以外最大事務編號;
- up_trx_id表示該SQL啟動時,當前事務連結串列中最小的事務id編號,也就是當前系統中建立最早但還未提交的事務;
- trx_ids表示所有事務連結串列中事務的id集合。
通過這個列表來判斷記錄的某個版本是否對當前事務可見。
MySQL(3306)與Redis一致性問題
強一致性:寫操作同時往資料庫和redis上寫
弱一致性:
清空快取
構建一個伺服器,實現一個定時任務,定時去查詢Mysql上變化的資料,更新到redis上
通過canal伺服器訪問Mysql的binlog檔案,將變化資料推送到MQ上,由MQ消費者同步到redis上
利用redis快取失效策略,redis熱鍵快速失效,保證一定延時下的資料最終一致性。當多個執行緒在redis上查詢不到資料時,加入執行緒鎖,只讓單個執行緒訪問Mysql,並把結果寫會redis,如此被寫回的執行緒直接訪問redis即可
索引語法
show index from table_name;//檢視索引
一句刪除mysql中重複語句:
DELETE FROM vitae WHERE peopleId NOT IN(SELECT dt.minno FROM (SELECT MIN(peopleId) AS minno FROM vitae GROUP BY seq) dt);
MySQL整合Spring
spring.datasource.url=jdbc:mysql://localhost:3306/lou_springboot?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
組合SQL語句
SELECT * FROM account
WHERE NAME IN(
SELECT NAME FROM account GROUP BY NAME HAVING COUNT(*)>1 這裡不能有封號,另外GROUP BY後面只能加 Having
AND
id NOT IN(12)
讀出所有不重複的資料
SELECT * FROM ACCOUNT WHERE ID NOT IN(
SELECT ID FROM ACCOUNT
GROUP BY NAME HAVING COUNT(*)>1
)
但是,在MySQL中,不能先select出同一表中的某些值,再update這個表(在同一語句中),否則會出現1093錯誤
InnoDB引擎
InnoDB支援事務、外來鍵,採用聚集索引結構(資料索引不分離),不儲存表的行數。
在InnoDB中,表資料檔案本身就是按照B+Tree組織的一個單索引結果檔案,在B+樹中,葉節點包含了完整的資料結構,InnoDB表必須要有主鍵,沒有MySql底層會有一套方法維護一套主鍵。同時主鍵最好是自增整型,減小比較的消耗和減小插入消耗。
B+樹索引可以很好支援範圍查詢,而hash索引和B樹對範圍查詢並不友好。
MySQL分散式儲存
讀寫分離
基於主從複製架構,由從庫分擔讀壓力,主庫將變更寫入 binlog 日誌,然後從庫連線到主庫之後,從庫有一個 IO 執行緒,將主庫的 binlog 日誌拷貝到自己本地,寫入一個 relay 中繼日誌中。接著從庫中有一個 SQL 執行緒會從中繼日誌讀取 binlog,然後執行 binlog 日誌中的內容,保證從庫和主庫資料的一致性。
叢集:
分庫分表,將資料存放在不同的叢集中,分擔資料庫寫壓力
資料儲存方案:
一些關係型欄位資訊的儲存方案: Oracle、MySQL
文件資料儲存方案:MongoDB
圖片資料儲存方案:FastDFS、TFS、GFS、HDFS、oss
搜尋引擎: solr、elasticsearch、Isearch
熱點資料、日誌系統:Redis、Tair
拓撲結構圖:Neo4j、InfoGrid
SQL索引失效
1.當全表掃描速度比索引速度快時,mysql會使用全表掃描,此時索引失效
2.在索引欄位上使用not,<>,!=。不等於操作符是永遠不會用到索引的,因此對它的處理只會產生全表掃描
3.在索引列上使用 IS NULL 或 IS NOT NULL操作可能導致索引失效
4.like 以%開頭,索引無效;當like字首沒有%,字尾有%時,索引有效
5.or語句前後沒有同時使用索引。
SQL索引適用範圍
- 資料量少的不適合加索引
- 更新比較頻繁的也不適合加索引
- 區分度低的欄位不適合加索引(如性別)
SQL慢查詢優化
explain + 查詢SQL,根據參考資訊可以進行SQL優化;
加索引、避免返回不必要的資料、適當分批量進行、優化sql結構、分庫分表、讀寫分離
InnoDB與MyISAM的區別
- InnoDB支援事務,MyISAM不支援事務
- InnoDB支援外來鍵,MyISAM不支援外來鍵
- InnoDB 支援 MVCC(多版本併發控制),MyISAM 不支援
- select count(*) from table時,MyISAM更快,因為它有一個變數儲存了整個表的總行數,可以直接讀取,InnoDB就需要全表掃描。
- Innodb不支援全文索引,而MyISAM支援全文索引(5.7以後的InnoDB也支援全文索引)
- InnoDB支援表、行級鎖,而MyISAM支援表級鎖。
- InnoDB表必須有主鍵,而MyISAM可以沒有主鍵
- Innodb表需要更多的記憶體和儲存,而MyISAM可被壓縮,儲存空間較小,。
- Innodb按主鍵大小有序插入,MyISAM記錄插入順序是,按記錄插入順序儲存。
- InnoDB 儲存引擎提供了具有提交、回滾、崩潰恢復能力的事務安全,與 MyISAM 比 InnoDB 寫的效率差一些,並且會佔用更多的磁碟空間以保留資料和索引
資料庫的三大正規化
- 第一正規化:資料表中的每一列(每個欄位)都不可以再拆分。
- 第二正規化:在第一正規化的基礎上,分主鍵列完全依賴於主鍵,而不能是依賴於主鍵的一部分。
- 第三正規化:在滿足第二正規化的基礎上,表中的非主鍵只依賴於主鍵,而不依賴於其他非主鍵。
百萬級別或以上的資料刪除方式
- 我們想要刪除百萬資料的時候可以先刪除索引
- 然後批量刪除其中無用資料
- 刪除完成後重新建立索引
MYSQL死鎖
- 如果不同程式會併發存取多個表,儘量約定以相同的順序訪問表,可以大大降低死鎖機會。
- 在同一個事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖產生概率;
- 對於非常容易產生死鎖的業務部分,可以嘗試使用升級鎖定顆粒度,通過表級鎖定來減少死鎖產生的概率;
- 如果業務處理不好可以用分散式事務鎖或者使用樂觀鎖
建立索引的三種方式
在執行CREATE TABLE時建立索引
CREATE TABLE `employee` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`date` datetime DEFAULT NULL,
`sex` int(1) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
使用CREATE INDEX命令建立
create (unique) index 索引名 on 表名(列名);
CREATE INDEX 索引名 ON 表名(列名,列名,。。。);//建立聯合索引,聯合索引底層也是B+樹,比較原則是先比較第一個欄位,然後再比較第二個欄位。所以只有使用最左字首法則才會走聯合索引
唯一索引
新增唯一性索引的資料列可以為空,但是隻要存在資料值,就必須是唯一的。主鍵索引是唯一索引的特殊型別。主鍵索引要求主鍵中的每個值是唯一的。當在查詢中使用主鍵索引時,它還允許快速訪問資料。資料不能為空
儲存過程
Mysql5 開始支援儲存過程
DROP PROCEDURE IF EXISTS produce_name(); -- 沒有括號()
//儲存過程語法
DELIMITER ;;
CREATE PROCEDURE `select_students_count`()
BEGIN
SELECT count(id) from students;
END;;
DELIMITER ;
//帶引數的儲存過程
DELIMITER ;;
CREATE PROCEDURE `select_students_by_city_count`(
in _city varchar(225)
)
BEGIN
SELECT count(id) from students where city = _city;
END;;
DELIMITER ;
//帶輸入輸出引數的儲存過程
DELIMITER ;;
CREATE PROCEDURE `select_students_by_name`(
in _name varchar(225), -- 輸入引數
out _city varchar(225), -- 輸出引數
inout _age int(11) -- 輸入輸出引數
)
BEGIN
SELECT city from students where name = _name and age = _age into _city;
END;;
DELIMITER ;
MySQL支援IN(傳遞給儲存過程),OUT(從儲存過程傳出)和INOUT(對儲存過程傳入和傳出)型別的引數。儲存過程的程式碼位於BEGIN和END語句內,如前所見,它們是一系列SELECT語句,用來檢索值,然後儲存到相應的變數(通過指定INTO關鍵字)