MySQL子查詢原理的深入分析
01前言
子查詢,通俗解釋就是查詢語句中嵌套著另一個查詢語句。相信日常工作中接觸到 MySQL 的同學(xué)都了解或使用過子查詢,但是具體它是怎樣實現(xiàn)的呢? 查詢效率如何? 這些恐怕好多人就不太清楚了,下面咱們就圍繞這兩個問題共同探索一下。
02準(zhǔn)備內(nèi)容
這里我們需要用到3個表,這3個表都有一個主鍵索引 id 和一個索引 a,字段 b 上無索引。存儲過程 idata() 往表 t1 里插入的是 100 行數(shù)據(jù),表 t2、t3 里插入了 1000 行數(shù)據(jù)。建表語句如下:
CREATE TABLE `t1` ( `id` INT ( 11 ) NOT NULL, `t1_a` INT ( 11 ) DEFAULT NULL, `t1_b` INT ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ), KEY `idx_a` ( `t1_a` )) ENGINE = INNODB; CREATE TABLE `t2` ( `id` INT ( 11 ) NOT NULL, `t2_a` INT ( 11 ) DEFAULT NULL, `t2_b` INT ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ), KEY `idx_a` ( `t2_a` )) ENGINE = INNODB; CREATE TABLE `t3` ( `id` INT ( 11 ) NOT NULL, `t3_a` INT ( 11 ) DEFAULT NULL, `t3_b` INT ( 11 ) DEFAULT NULL, PRIMARY KEY ( `id` ), KEY `idx_a` ( `t3_a` )) ENGINE = INNODB; -- 向t1添加100條數(shù)據(jù) -- drop procedure idata; delimiter ;; create procedure idata() begin declare i int; set i=1; while(i<=100)do insert into t1 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata(); -- 向t2添加1000條數(shù)據(jù) drop procedure idata; delimiter ;; create procedure idata() begin declare i int; set i=101; while(i<=1100)do insert into t2 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata(); -- 向t2添加1000條數(shù)據(jù),且t3_a列的值為倒敘 drop procedure idata; delimiter ;; create procedure idata() begin declare i int; set i=101; while(i<=1100)do insert into t3 values(i, 1101-i, i); set i=i+1; end while; end;; delimiter ; call idata();
03子查詢的語法形式和分類
3.1 語法形式
子查詢的語法規(guī)定,子查詢可以在一個外層查詢的各種位置出現(xiàn),這里我們只介紹常用的幾個:
3.1.1 FROM子句中
如 SELECT m, n FROM (SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t;
這個例子中的子查詢是:(SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2
),這個放在FROM子句中的子查詢相當(dāng)于一個表,但又和我們平常使用的表有點兒不一樣,這種由子查詢結(jié)果集組成的表稱之為派生表。
3.1.2 WHERE或IN子句中
如:SELECT * FROM t1 WHERE m1 = (SELECT MIN(m2) FROM t2);
SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);
其他的還有 SELECT 子句中,ORDER BY 子句中,GROUP BY 子句中,雖然語法支持,但沒啥意義,就不嘮叨這些情況了。
3.2 分類
3.2.1 按返回的結(jié)果集區(qū)分
標(biāo)量子查詢,只返回一個單一值的子查詢稱之為標(biāo)量子查詢,比如:
SELECT * FROM t1 WHERE m1 = (SELECT m1 FROM t1 LIMIT 1);
行子查詢,就是只返回一條記錄的子查詢,不過這條記錄需要包含多個列(只包含一個列就成了標(biāo)量子查詢了)。比如:SELECT * FROM t1 WHERE (m1, n1) = (SELECT m2, n2 FROM t2 LIMIT 1);
列子查詢,就是只返回一個列的數(shù)據(jù),不過這個列的數(shù)據(jù)需要包含多條記錄(只包含一條記錄就成了標(biāo)量子查詢了)。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);
表子查詢,就是子查詢的結(jié)果既包含很多條記錄,又包含很多個列,比如:
SELECT * FROM t1 WHERE (m1, n1) IN (SELECT m2, n2 FROM t2);
其中的 (SELECT m2, n2 FROM t2) 就是一個表子查詢,這里需要和行子查詢對比一下,行子查詢中我們用了 LIMIT 1 來保證子查詢的結(jié)果只有一條記錄。
3.2.2 按與外層查詢關(guān)系來區(qū)分
不相關(guān)子查詢,就是子查詢可以單獨運行出結(jié)果,而不依賴于外層查詢的值,我們就可以把這個子查詢稱之為不相關(guān)子查詢。
相關(guān)子查詢,就是需要依賴于外層查詢的值的子查詢稱之為相關(guān)子查詢。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2 WHERE n1 = n2);
04子查詢在MySQL中是怎么執(zhí)行的
4.1 標(biāo)量子查詢、行子查詢的執(zhí)行方式
4.1.1 不相關(guān)子查詢
如下邊這個查詢語句:
mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 limit 1); +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+ | 1 | PRIMARY | t1 | ref | idx_a | idx_a | 5 | const | 1 | Using where | | 2 | SUBQUERY | t2 | index | <null> | idx_a | 5 | <null> | 1000 | Using index | +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
它的執(zhí)行方式:
先單獨執(zhí)行 (select t2_a from t2 limit 1) 這個子查詢。
然后在將上一步子查詢得到的結(jié)果當(dāng)作外層查詢的參數(shù)再執(zhí)行外層查詢 select * from t1 where t1_a = ...。
也就是說,對于包含不相關(guān)的標(biāo)量子查詢或者行子查詢的查詢語句來說,MySQL 會分別獨立的執(zhí)行外層查詢和子查詢,就當(dāng)作兩個單表查詢就好了。
4.1.2 相關(guān)的子查詢
比如下邊這個查詢:
mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 where t1.t1_b=t2.t2_b limit 1); +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+ | 1 | PRIMARY | t1 | ALL | <null> | <null> | <null> | <null> | 100 | Using where | | 2 | DEPENDENT SUBQUERY | t2 | ALL | <null> | <null> | <null> | <null> | 1000 | Using where | +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+
它的執(zhí)行方式就是這樣的:
先從外層查詢中獲取一條記錄,本例中也就是先從 t1 表中獲取一條記錄。
然后從上一步驟中獲取的那條記錄中找出子查詢中涉及到的值,就是 t1 表中找出 t1.t1_b 列的值,然后執(zhí)行子查詢。
最后根據(jù)子查詢的查詢結(jié)果來檢測外層查詢 WHERE 子句的條件是否成立,如果成立,就把外層查詢的那條記錄加入到結(jié)果集,否則就丟棄。
然后重復(fù)以上步驟,直到 t1 中的記錄全部匹配完。
4.2 IN子查詢
4.2.1 物化
如果子查詢的結(jié)果集中的記錄條數(shù)很少,那么把子查詢和外層查詢分別看成兩個單獨的單表查詢效率還是蠻高的,但是如果單獨執(zhí)行子查詢后的結(jié)果集太多的話,就會導(dǎo)致這些問題:
結(jié)果集太多,可能內(nèi)存中都放不下~
對于外層查詢來說,如果子查詢的結(jié)果集太多,那就意味著 IN 子句中的參數(shù)特別多,這就導(dǎo)致:
1)無法有效的使用索引,只能對外層查詢進行全表掃描。
2)在對外層查詢執(zhí)行全表掃描時,由于 IN 子句中的參數(shù)太多,這會導(dǎo)致檢測一條記錄是否符合和 IN 子句中的參數(shù)匹配花費的時間太長。
于是就有:不直接將不相關(guān)子查詢的結(jié)果集當(dāng)作外層查詢的參數(shù),而是將該結(jié)果集寫入一個臨時表里。寫入臨時表的過程是這樣的:
該臨時表的列就是子查詢結(jié)果集中的列。
寫入臨時表的記錄會被去重,讓臨時表變得更小,更省地方。
一般情況下子查詢結(jié)果集不大時,就會為它建立基于內(nèi)存的使用 Memory 存儲引擎的臨時表,而且會為該表建立哈希索引。
如果子查詢的結(jié)果集非常大,超過了系統(tǒng)變量 tmp_table_size或者 max_heap_table_size,臨時表會轉(zhuǎn)而使用基于磁盤的存儲引擎來保存結(jié)果集中的記錄,索引類型也對應(yīng)轉(zhuǎn)變?yōu)?B+ 樹索引。
這個將子查詢結(jié)果集中的記錄保存到臨時表的過程稱之為物化(Materialize)。為了方便起見,我們就把那個存儲子查詢結(jié)果集的臨時表稱之為物化表。正因為物化表中的記錄都建立了索引(基于內(nèi)存的物化表有哈希索引,基于磁盤的有 B+ 樹索引),通過索引執(zhí)行IN語句判斷某個操作數(shù)在不在子查詢結(jié)果集中變得非???,從而提升了子查詢語句的性能。
mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2); +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+ | 1 | SIMPLE | t3 | ALL | idx_a | <null> | <null> | <null> | 1000 | Using where | | 1 | SIMPLE | <subquery2> | eq_ref | <auto_key> | <auto_key> | 5 | test.t3.t3_a | 1 | <null> | | 2 | MATERIALIZED | t2 | index | idx_a | idx_a | 5 | <null> | 1000 | Using index | +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+
其實上邊的查詢就相當(dāng)于表 t3 和子查詢物化表進行內(nèi)連接:
mysql root@localhost:test> explain select * from t3 left join t2 on t3.t3_a=t2.t2_a; +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+ | 1 | SIMPLE | t3 | ALL | <null> | <null> | <null> | <null> | 1000 | <null> | | 1 | SIMPLE | t2 | ref | idx_a | idx_a | 5 | test.t3.t3_a | 1 | <null> | +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+
此時 MySQL 查詢優(yōu)化器會通過運算來選擇成本更低的方案來執(zhí)行查詢。
雖然,上面通過物化表的方式,將IN子查詢轉(zhuǎn)換成了聯(lián)接查詢,但還是會有建立臨時表的成本,能不能不進行物化操作直接把子查詢轉(zhuǎn)換為連接呢?直接轉(zhuǎn)換肯定不行。
-- 這里我們先構(gòu)造了3條記錄,其實也是構(gòu)造不唯一的普通索引
+------+------+------+ | id | t2_a | t2_b | +------+------+------+ | 1100 | 1000 | 1000 | | 1101 | 1000 | 1000 | | 1102 | 1000 | 1000 | +------+------+------+ -- 加限制條件where t2.id>=1100是為了減少要顯示的數(shù)據(jù) mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id>=1100); +-----+------+------+ | id | t3_a | t3_b | +-----+------+------+ | 101 | 1000 | 101 | +-----+------+------+ 1 row in set Time: 0.016s mysql root@localhost:test> select * from t3 left join t2 on t3.t3_a=t2.t2_a where t2.id>=1100; +-----+------+------+------+------+------+ | id | t3_a | t3_b | id | t2_a | t2_b | +-----+------+------+------+------+------+ | 101 | 1000 | 101 | 1100 | 1000 | 1000 | | 101 | 1000 | 101 | 1101 | 1000 | 1000 | | 101 | 1000 | 101 | 1102 | 1000 | 1000 | +-----+------+------+------+------+------+ 3 rows in set Time: 0.018s
所以說 IN 子查詢和表聯(lián)接之間并不完全等價。而我們需要的是另一種叫做半聯(lián)接 (semi-join) 的聯(lián)接方式 :對于 t3 表的某條記錄來說,我們只關(guān)心在 t2 表中是否存在與之匹配的記錄,而不關(guān)心具體有多少條記錄與之匹配,最終的結(jié)果集中也只保留 t3 表的記錄。
注意:semi-join 只是在 MySQL 內(nèi)部采用的一種執(zhí)行子查詢的方式,MySQL 并沒有提供面向用戶的 semi-join 語法。
4.2.2 半聯(lián)接的實現(xiàn):
- Table pullout (子查詢中的表上拉)
當(dāng)子查詢的查詢列表處只有主鍵或者唯一索引列時,可以直接把子查詢中的表上拉到外層查詢的 FROM 子句中,并把子查詢中的搜索條件合并到外層查詢的搜索條件中,比如這個:
mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id=999) +-----+------+------+ | id | t3_a | t3_b | +-----+------+------+ | 102 | 999 | 102 | +-----+------+------+ 1 row in set Time: 0.024s mysql root@localhost:test> select * from t3 join t2 on t3.t3_a=t2.t2_a where t2.id=999; +-----+------+------+-----+------+------+ | id | t3_a | t3_b | id | t2_a | t2_b | +-----+------+------+-----+------+------+ | 102 | 999 | 102 | 999 | 999 | 999 | +-----+------+------+-----+------+------+ 1 row in set Time: 0.028s mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.id=999) +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+ | 1 | SIMPLE | t2 | const | PRIMARY,idx_a | PRIMARY | 4 | const | 1 | <null> | | 1 | SIMPLE | t3 | ref | idx_a | idx_a | 5 | const | 1 | <null> | +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
- FirstMatch execution strategy (首次匹配)
FirstMatch 是一種最原始的半連接執(zhí)行方式,跟相關(guān)子查詢的執(zhí)行方式是一樣的,就是說先取一條外層查詢的中的記錄,然后到子查詢的表中尋找符合匹配條件的記錄,如果能找到一條,則將該外層查詢的記錄放入最終的結(jié)果集并且停止查找更多匹配的記錄,如果找不到則把該外層查詢的記錄丟棄掉。然后再開始取下一條外層查詢中的記錄,重復(fù)上邊這個過程。
mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a=1000) +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+ | 1 | SIMPLE | t3 | ref | idx_a | idx_a | 5 | const | 1 | <null> | | 1 | SIMPLE | t2 | ref | idx_a | idx_a | 5 | const | 4 | Using index; FirstMatch(t3) | +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
- DuplicateWeedout execution strategy (重復(fù)值消除)
轉(zhuǎn)換為半連接查詢后,t3 表中的某條記錄可能在 t2 表中有多條匹配的記錄,所以該條記錄可能多次被添加到最后的結(jié)果集中,為了消除重復(fù),我們可以建立一個臨時表,并設(shè)置主鍵id,每當(dāng)某條 t3 表中的記錄要加入結(jié)果集時,就首先把這條記錄的id值加入到這個臨時表里,如果添加成功,說明之前這條 t2 表中的記錄并沒有加入最終的結(jié)果集,是一條需要的結(jié)果;如果添加失敗,說明之前這條 s1 表中的記錄已經(jīng)加入過最終的結(jié)果集,直接把它丟棄。
- LooseScan execution strategy (松散掃描)
這種雖然是掃描索引,但只取值相同的記錄的第一條去做匹配操作的方式稱之為松散掃描。
4.2.3 半聯(lián)接的適用條件
當(dāng)然,并不是所有包含IN子查詢的查詢語句都可以轉(zhuǎn)換為 semi-join,只有形如這樣的查詢才可以被轉(zhuǎn)換為 semi-join:
SELECT ... FROM outer_tables? ? ? WHERE expr IN (SELECT ... FROM inner_tables ...) AND ... -- 或者這樣的形式也可以: SELECT ... FROM outer_tables? ? ? WHERE (oe1, oe2, ...) IN (SELECT ie1, ie2, ... FROM inner_tables ...) AND ...
用文字總結(jié)一下,只有符合下邊這些條件的子查詢才可以被轉(zhuǎn)換為 semi-join:
- 該子查詢必須是和IN語句組成的布爾表達式,并且在外層查詢的 WHERE 或者 ON 子句中出現(xiàn)
- 外層查詢也可以有其他的搜索條件,只不過和 IN 子查詢的搜索條件必須使用AND 連接起來
- 該子查詢必須是一個單一的查詢,不能是由若干查詢由 UNION 連接起來的形式
- 該子查詢不能包含 GROUP BY 或者 HAVING 語句或者聚集函數(shù)
4.2.4 轉(zhuǎn)為 EXISTS 子查詢
不管子查詢是相關(guān)的還是不相關(guān)的,都可以把 IN 子查詢嘗試轉(zhuǎn)為 EXISTS子查詢。其實對于任意一個 IN 子查詢來說,都可以被轉(zhuǎn)為 EXISTS 子查詢,通用的例子如下:
outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where) -- 可以被轉(zhuǎn)換為: EXISTS (SELECT inner_expr FROM ... WHERE subquery_where AND outer_expr=inner_expr)
當(dāng)然這個過程中有一些特殊情況,比如在 outer_expr 或者 inner_expr 值為 NULL 的情況下就比較特殊。因為有 NULL 值作為操作數(shù)的表達式結(jié)果往往是 NULL,比方說:
mysql root@localhost:test> SELECT NULL IN (1, 2, 3); +-------------------+ | NULL IN (1, 2, 3) | +-------------------+ | <null> | +-------------------+ 1 row in set
而 EXISTS 子查詢的結(jié)果肯定是 TRUE 或者 FASLE 。但是現(xiàn)實中我們大部分使用 IN 子查詢的場景是把它放在 WHERE 或者 ON 子句中,而 WHERE 或者 ON 子句是不區(qū)分 NULL 和 FALSE 的,比方說:
mysql root@localhost:test> SELECT 1 FROM s1 WHERE NULL; +---+ | 1 | +---+ 0 rows in set Time: 0.016s mysql root@localhost:test> SELECT 1 FROM s1 WHERE FALSE; +---+ | 1 | +---+ 0 rows in set Time: 0.033s
所以只要我們的IN子查詢是放在 WHERE 或者 ON 子句中的,那么 IN -> EXISTS 的轉(zhuǎn)換就是沒問題的。說了這么多,為啥要轉(zhuǎn)換呢?這是因為不轉(zhuǎn)換的話可能用不到索引,比方說下邊這個查詢:
mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a>=999) or t3_b > 1000; +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+ | 1 | PRIMARY | t3 | ALL | <null> | <null> | <null> | <null> | 1000 | Using where | | 2 | SUBQUERY | t2 | range | idx_a | idx_a | 5 | <null> | 107 | Using where; Using index | +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+
但是將它轉(zhuǎn)為 EXISTS 子查詢后卻可以使用到索引:
mysql root@localhost:test> explain select * from t3 where exists (select 1 from t2 where t2.t2_a>=999 and t2.t2_a=t3.t3_a) or t3_b > 1000; +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+ | 1 | PRIMARY | t3 | ALL | <null> | <null> | <null> | <null> | 1000 | Using where | | 2 | DEPENDENT SUBQUERY | t2 | ref | idx_a | idx_a | 5 | test.t3.t3_a | 1 | Using where; Using index | +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+
需要注意的是,如果 IN 子查詢不滿足轉(zhuǎn)換為 semi-join 的條件,又不能轉(zhuǎn)換為物化表或者轉(zhuǎn)換為物化表的成本太大,那么它就會被轉(zhuǎn)換為 EXISTS 查詢?;蛘咿D(zhuǎn)換為物化表的成本太大,那么它就會被轉(zhuǎn)換為 EXISTS 查詢。
05總結(jié)
1. 如果IN子查詢符合轉(zhuǎn)換為 semi-join 的條件,查詢優(yōu)化器會優(yōu)先把該子查詢轉(zhuǎn)換為 semi-join,然后再考慮下邊執(zhí)行半連接的策略中哪個成本最低,
1)Table pullout
2)DuplicateWeedout
3)LooseScan
4)FirstMatch
選擇成本最低的那種執(zhí)行策略來執(zhí)行子查詢。
2. 如果IN子查詢不符合轉(zhuǎn)換為 semi-join 的條件,那么查詢優(yōu)化器會從下邊兩種策略中找出一種成本更低的方式執(zhí)行子查詢:
1)先將子查詢物化之后再執(zhí)行查詢
2)執(zhí)行 IN to EXISTS 轉(zhuǎn)換
到此這篇關(guān)于MySQL子查詢原理的文章就介紹到這了,更多相關(guān)MySQL子查詢原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Ubuntu20下MySQL?8.0.28?安裝卸載方法圖文教程
這篇文章主要為大家詳細(xì)介紹了Ubuntu20下MySQL?8.0.28?安裝卸載方法圖文教程,文中安裝步驟介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04MySQL表LEFT JOIN左連接與RIGHT JOIN右連接的實例教程
這篇文章主要介紹了MySQL表LEFT JOIN左連接與RIGHT JOIN右連接的實例教程,表連接操作是MySQL入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-12-12MySQL優(yōu)化案例系列-mysql分頁優(yōu)化
這篇文章主要介紹了MySQL優(yōu)化案例系列-mysql分頁優(yōu)化,需要的朋友可以參考下2016-08-08mysql簡單實現(xiàn)查詢結(jié)果添加序列號的方法
這篇文章主要介紹了mysql簡單實現(xiàn)查詢結(jié)果添加序列號的方法,結(jié)合實例形式演示了2種查詢結(jié)果添加序列號的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2016-06-06分享CentOS下MySQL最新版本5.6.13源碼安裝過程
這篇文章主要介紹了CentOS下安裝MySQL最新版本5.6.13過程分享,需要的朋友可以參考下2014-02-02