sql count(1) count(id) 的區別

之前一直沒有去深入瞭解 count(1) count(id) 的區別,借這篇文章順便深入挖掘一下

  • 本文章圍繞mysql explain (執行計劃)
  • mysql 8.0.16 for windows
  • 工具 Navicat Premium 12
  • 存儲引擎 InnoDB
  • 以下結果不看具體執行時間,只看執行計劃。具體的執行時間受數據量、機器性能等等因素影響就不多說了。

explain

首先需要了解一下 EXPLAIN ,他可以幫助我們優化sql

當EXPLAIN與可解釋語句一起使用時,MySQL會顯示優化程序中有關語句執行計劃的信息。也就是說,MySQL解釋了它將如何處理語句,包括有關如何連接表以及以何種順序連接的信息。

示例

新建一張表,之後的測試均是基於此表

CREATE TABLE `temp_test`  (
  `id` int(18) NOT NULL,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  `age` int(3) NULL DEFAULT NULL,
  `class` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `pri_name`(`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

id   int類型 爲主鍵
name 爲普通索引

再插入點數據

INSERT INTO temp_test VALUES
(1,'張1',1,'班級1'),
(2,'張大',2,'班級2'),
(3,'張3',3,'班級3'),
(4,'張4',4,'班級4'),
(5,'張5',5,'班級5'),
(6,'張6',6,'班級6'),
(7,'張7',1,'班級7'),
(8,'張8',8,'班級8'),
(9,'張9',9,'班級9'),
(10,'張10',10,'班級10'),
(11,'張11',11,NULL);

select * from table

讓我們看看當全表掃描時會發生什麼,執行以下sql,查看返回結果

EXPLAIN SELECT * FROM temp_test;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test ALL 10 100.00

返回瞭如上表格內的結果,返回的每個字段的詳解請看下文:

  • id 爲 1 意味着:這是查詢序號爲 1 的sql
  • select_type 爲 SIMPLE 意味着:這是一個簡單的單表查詢沒有使用到子查詢或UNION
  • table 爲 temp_test 意味着: 目標的表是剛新建的表temp_test,
  • type 爲 ALL意味着: 類型是一個全表的掃描,雖然測試的數據只有10行,但是在數據量很大的情況下查詢速度會更慢,這條sql沒有使用到索引。

SELECT count(1/i)/*)

EXPLAIN SELECT count(1) FROM temp_test; 執行結果如下
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test index pri_name 303 10 100.00 Using index
EXPLAIN SELECT count(id) FROM temp_test; 執行結果如下
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test index pri_name 303 10 100.00 Using index
EXPLAIN SELECT count(*) FROM temp_test; 執行結果如下
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test index pri_name 303 10 100.00 Using index

這三結果看起來差不多,與select * 相比不過是

  • type變成了 index

  • Extra 變成了 Using index

    index聯接類型是一樣的 ALL,只是索引樹被掃描。這有兩種方式:

    • 如果索引是查詢的覆蓋索引,並且可用於滿足表中所需的所有數據,則僅掃描索引樹。在這種情況下,Extra專欄說 Using index。僅索引掃描通常比ALL索引的大小通常小於表數據更快 。
    • 使用索引中的讀取執行全表掃描,以按索引順序查找數據行。 Uses index沒有出現在 Extra列中。

    僅使用索引樹中的信息從表中檢索列信息,而不必執行額外的搜索以讀取實際行。當查詢僅使用屬於單個索引的列時,可以使用此策略。

    對於InnoDB具有用戶定義的聚簇索引的表,即使列中Using index不存在 該索引,也可以使用該索引Extra。這樣的話,如果 typeindexkeyPRIMARY

簡單的來說 type變成index幾乎與全表掃描一樣。當然,這裏掃描的是索引樹,速度肯定要比ALL快點。

總結來說,這count(1/id/*/2/3) 查詢結果都差不多,分析sql執行計劃最重要的字段就是 type能達到ref 索引訪問最好。

1並不是表示第一個字段,而是表示一個固定值。 其實就可以想成表中有這麼一個字段,這個字段就是固定值1,count(1),就是計算一共有多少個1.

select 主鍵= 索引(普通) =

EXPLAIN SELECT * FROM temp_test WHERE id = 2; 執行結果如下
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test const PRIMARY PRIMARY 4 const 1 100.00

根據主鍵查詢,最理想的查詢

EXPLAIN SELECT * FROM temp_test WHERE NAME = '張3';執行結果如下 
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE temp_test ref pri_name pri_name 303 const 10 1

根據索引查詢

explain輸出參數解釋

列明 說明
id 執行編號,標識select所屬的行。如果在語句中沒子查詢或關聯查詢,只有唯一的select,每行都將顯示1。否則,內層的select語句一般會順序編號,對應於其在原始語句中的位置
select_type 顯示本行是簡單或複雜select。如果查詢有任何複雜的子查詢,則最外層標記爲PRIMARY(DERIVED、UNION、UNION RESUlT)
table 訪問引用哪個表
partitions 匹配的分區
type 數據訪問/讀取操作類型(ALL、index、range、ref、eq_ref、const/system、NULL)
possible_keys 揭示哪一些索引可能有利於高效的查找
key mysql決定採用哪個索引來優化查詢
key_len mysql在索引裏使用的字節數
ref 顯示了之前的表在key列記錄的索引中查找值所用的列或常量
rows 爲了找到所需的行而需要讀取的行數,估算值,不精確。通過把所有rows列值相乘,可粗略估算整個查詢會檢查的行數
filtered 按表條件過濾的行的百分比
Extra 附加信息 ,如using index、filesort等

id

id是用來順序標識整個查詢中SELELCT 語句的,在嵌套查詢中id越大的語句越先執行。該值可能爲NULL,如果這一行用來說明的是其他行的聯合結果。

select_type

select_type JSON名稱 含義
SIMPLE 沒有 簡單SELECT(不使用 UNION或子查詢)
PRIMARY 沒有 SELECT
UNION 沒有 第二次或以後的SELECT陳述 UNION
DEPENDENT UNION dependenttrue a中的第二個或更晚的SELECT語句 UNION,取決於外部查詢
UNION RESULT union_result 的結果UNION
SUBQUERY 沒有 首先SELECT是子查詢
DEPENDENT SUBQUERY dependenttrue 首先SELECT在子查詢中,依賴於外部查詢
DERIVED 沒有 派生表
DEPENDENT DERIVED dependenttrue 派生表依賴於另一個表
MATERIALIZED materialized_from_subquery 物化子查詢
UNCACHEABLE SUBQUERY cacheablefalse 無法緩存結果的子查詢,必須爲外部查詢的每一行重新計算
UNCACHEABLE UNION cacheablefalse UNION 屬於不可緩存的子查詢的第二個或後一個選擇(請參閱參考資料 UNCACHEABLE SUBQUERY

type

type顯示的是訪問類型,是較爲重要的一個指標,結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到range級別,最好能達到ref。

類型 說明
All 最壞的情況,全表掃描
index 和全表掃描一樣。只是掃描表的時候按照索引次序進行而不是行。主要優點就是避免了排序, 但是開銷仍然非常大。如在Extra列看到Using index,說明正在使用覆蓋索引,只掃描索引的數據,它比按索引次序全表掃描的開銷要小很多
range 範圍掃描,一個有限制的索引掃描。key 列顯示使用了哪個索引。當使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比較關鍵字列時,可以使用 range
ref 一種索引訪問,它返回所有匹配某個單個值的行。此類索引訪問只有當使用非唯一性索引或唯一性索引非唯一性前綴時纔會發生。這個類型跟eq_ref不同的是,它用在關聯操作只使用了索引的最左前綴,或者索引不是UNIQUE和PRIMARY KEY。ref可以用於使用=或<=>操作符的帶索引的列。
eq_ref 最多隻返回一條符合條件的記錄。使用唯一性索引或主鍵查找時會發生 (高效)
const 當確定最多隻會有一行匹配的時候,MySQL優化器會在查詢前讀取它而且只讀取一次,因此非常快。當主鍵放入where子句時,mysql把這個查詢轉爲一個常量(高效)
system 這是const連接類型的一種特例,表僅有一行滿足條件。
Null 意味說mysql能在優化階段分解查詢語句,在執行階段甚至用不到訪問表或索引(高效)

possible_keys

possible_keys列指示MySQL可以從中選擇查找此表中的行的索引。請注意,此列完全獨立於輸出中顯示的表的順序 EXPLAIN。這意味着某些鍵possible_keys可能無法在生成中使用生成的表順序。

如果此列是NULL(或在JSON格式的輸出中未定義),則沒有相關索引。在這種情況下,您可以通過檢查WHERE 子句以檢查它是否引用適合索引的某些列或列來提高查詢性能。如果是,請創建適當的索引並EXPLAIN再次檢查查詢 。請參見 第13.1.9節“ALTER TABLE語法”

要查看錶有哪些索引,請使用。 SHOW INDEX FROM *tbl_name*

key

key列顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key列指示MySQL實際決定使用的密鑰(索引)。如果MySQL決定使用其中一個possible_keys 索引來查找行,那麼該索引將被列爲鍵值。

可能key會命名值中不存在的索引 possible_keys。如果沒有possible_keys索引適合查找行,則會發生這種情況,但查詢選擇的所有列都是其他索引的列。也就是說,命名索引覆蓋了所選列,因此雖然它不用於確定要檢索的行,但索引掃描比數據行掃描更有效。

因爲InnoDB,即使查詢還選擇主鍵,輔助索引也可能覆蓋所選列,因爲InnoDB主鍵值與每個輔助索引一起存儲。如果 keyNULL,MySQL沒有找到用於更有效地執行查詢的索引。

要強制MySQL使用或忽略列出的索引 possible_keys列,使用 FORCE INDEXUSE INDEXIGNORE INDEX在您的查詢。請參見第8.9.4節“索引提示”

對於MyISAM表,運行 ANALYZE TABLE有助於優化器選擇更好的索引。對於 MyISAM表格,myisamchk --analyze也是如此。請參見 第13.7.3.1節“ANALYZE TABLE語法”第7.6節“MyISAM表維護和崩潰恢復”

key_len

key_len列顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度爲NULL。使用的索引的長度。在不損失精確性的情況下,長度越短越好 。

key_len列指示MySQL決定使用的密鑰的長度。該值 key_len使您可以確定MySQL實際使用的多部分密鑰的多少部分。如果key列說 NULL,該len_len 列也說NULL

由於密鑰存儲格式,對於可能NULL 比列的列,密鑰長度更大NOT NULL

ref

ref列顯示使用哪個列或常數與key一起從表中選擇行。

ref列顯示將哪些列或常量與列中指定的索引進行比較,以 key從表中選擇行。

如果值爲func,則使用的值是某個函數的結果。要查看哪個函數,請使用 SHOW WARNINGS以下內容 EXPLAIN查看擴展 EXPLAIN輸出。該函數實際上可能是算術運算符等運算符。

rows

rows列指示MySQL認爲必須檢查以執行查詢的行數。

對於InnoDB表格,此數字是估算值,可能並不總是準確的

filtered

filtered列指示將按表條件過濾的錶行的估計百分比。最大值爲100,這意味着不會對行進行過濾。值從100開始減少表示過濾量增加。 rows顯示檢查的估計行數,rows×filtered表示將與下表連接的行數。例如,如果 rows是1000並且 filtered是50.00(50%),則使用下表連接的行數是1000×50%= 500。

Extra

此列包含有關MySQL如何解析查詢的其他信息。有關不同值的說明,請參閱 EXPLAIN 附加信息

沒有與該Extra列對應的單個JSON屬性 ; 但是,此列中可能出現的值將作爲JSON屬性公開,或作爲屬性的文本公開message

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章