1. 程式人生 > 其它 >SQL資料庫和語法

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了還未提交的事務:

  1. low_trx_id表示該SQL啟動時,當前事務連結串列中最大的事務id編號,也就是最近建立的除自身以外最大事務編號;
  2. up_trx_id表示該SQL啟動時,當前事務連結串列中最小的事務id編號,也就是當前系統中建立最早但還未提交的事務;
  3. 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索引適用範圍

  1. 資料量少的不適合加索引
  2. 更新比較頻繁的也不適合加索引
  3. 區分度低的欄位不適合加索引(如性別)

SQL慢查詢優化

explain + 查詢SQL,根據參考資訊可以進行SQL優化;

加索引、避免返回不必要的資料、適當分批量進行、優化sql結構、分庫分表、讀寫分離

InnoDB與MyISAM的區別

  1. InnoDB支援事務,MyISAM不支援事務
  2. InnoDB支援外來鍵,MyISAM不支援外來鍵
  3. InnoDB 支援 MVCC(多版本併發控制),MyISAM 不支援
  4. select count(*) from table時,MyISAM更快,因為它有一個變數儲存了整個表的總行數,可以直接讀取,InnoDB就需要全表掃描。
  5. Innodb不支援全文索引,而MyISAM支援全文索引(5.7以後的InnoDB也支援全文索引)
  6. InnoDB支援表、行級鎖,而MyISAM支援表級鎖。
  7. InnoDB表必須有主鍵,而MyISAM可以沒有主鍵
  8. Innodb表需要更多的記憶體和儲存,而MyISAM可被壓縮,儲存空間較小,。
  9. Innodb按主鍵大小有序插入,MyISAM記錄插入順序是,按記錄插入順序儲存。
  10. InnoDB 儲存引擎提供了具有提交、回滾、崩潰恢復能力的事務安全,與 MyISAM 比 InnoDB 寫的效率差一些,並且會佔用更多的磁碟空間以保留資料和索引

資料庫的三大正規化

  1. 第一正規化:資料表中的每一列(每個欄位)都不可以再拆分。
  2. 第二正規化:在第一正規化的基礎上,分主鍵列完全依賴於主鍵,而不能是依賴於主鍵的一部分。
  3. 第三正規化:在滿足第二正規化的基礎上,表中的非主鍵只依賴於主鍵,而不依賴於其他非主鍵。

百萬級別或以上的資料刪除方式

  1. 我們想要刪除百萬資料的時候可以先刪除索引
  2. 然後批量刪除其中無用資料
  3. 刪除完成後重新建立索引

MYSQL死鎖

  1. 如果不同程式會併發存取多個表,儘量約定以相同的順序訪問表,可以大大降低死鎖機會。
  2. 在同一個事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖產生概率;
  3. 對於非常容易產生死鎖的業務部分,可以嘗試使用升級鎖定顆粒度,通過表級鎖定來減少死鎖產生的概率;
  4. 如果業務處理不好可以用分散式事務鎖或者使用樂觀鎖

建立索引的三種方式

在執行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(對儲存過程傳入和傳出)型別的引數。儲存過程的程式碼位於BEGINEND語句內,如前所見,它們是一系列SELECT語句,用來檢索值,然後儲存到相應的變數(通過指定INTO關鍵字)