[英]How does MySQL select columns ..?
當MySQL查詢的select語句中存在多個相同的表達式時,服務器是否再次計算表達式或重用它?
例:
從Table中選擇Column1, upper(Column1)作為name1,其中upper(Column1)類似於'ADAM%'
在上面的查詢中, 上(Column1)表達式計算一次或兩次..?
另外,你能指導我找到有關這方面的更多信息的文件嗎?
TIA
在此查詢中:
Select Column1, upper(Column1) as name1
from Table
where upper(Column1) like 'ADAM%';
我猜想upper(Column1)
被評估兩次。 有些數據庫具有復雜的優化功能,可以找到常見的子表達式; 我不認為MySQL會這樣做。
但是,它並沒有什么不同。 upper()
應該是相對便宜的,相對於讀取數據和比like
。 這尤其正確,因為查詢無法使用索引,因此必須執行全表掃描。
MySQL確實可以解決這個問題。 如果您使用having
,那么它應該做的計算一次:
Select Column1, upper(Column1) as name1
from Table
having upper(Column1) like 'ADAM%';
這是一個MySQL擴展。
我有幾點要回答這個問題。
回答你的問題:不,我從來沒有讀過MySQL“記得”已經評估過WHERE子句中的函數的任何報告。 它分別評估WHERE子句和選擇列表。
另一方面,MySQL將評估確定性常量函數(即,對每行計算相同常量值的表達式),這是一種更普遍有用的性能改進。 請參閱https://dev.mysql.com/doc/refman/5.7/en/function-optimization.html
UPPER()
並不昂貴。 即使運行此功能100萬次仍然只需要0.04秒,這比評估字符串文字要多,但它仍然幾乎不可察覺。
mysql> select benchmark(1000000, 'adam');
+----------------------------+
| benchmark(1000000, 'adam') |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (0.01 sec)
mysql> select benchmark(1000000, upper('adam'));
+-----------------------------------+
| benchmark(1000000, upper('adam')) |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set (0.04 sec)
如果使用不區分大小寫的排序規則(這是默認設置),則不必在顯示的查詢中使用UPPER()
)。
由於“ci”排序規則, LIKE
謂詞默認情況下會進行不區分大小寫的比較。
mysql> show variables like '%collation%';
+----------------------+--------------------+
| Variable_name | Value |
+----------------------+--------------------+
| collation_connection | utf8mb4_0900_ai_ci |
| collation_database | utf8_general_ci |
| collation_server | utf8_general_ci |
+----------------------+--------------------+
mysql> select 'abc' like 'ABC';
+------------------+
| 'abc' like 'ABC' |
+------------------+
| 1 |
+------------------+
在WHERE子句中的表達式比較中,評估UPPER()
性能要高得多。
假設您的表有100萬行,但只有1000行匹配您正在尋找的'ADAM%'模式。 使用像這樣的WHERE子句:
mysql> EXPLAIN SELECT UPPER(column1) AS name1 FROM MyTable
WHERE UPPER(column1) LIKE 'ADAM%'\G
id: 1
select_type: SIMPLE
table: MyTable
partitions: NULL
type: index
possible_keys: NULL
key: column1
key_len: 153
ref: NULL
rows: 735250 <-- this is an order-of-magnitude estimate
filtered: 100.00
Extra: Using where; Using index
這不能使用索引,因此強制執行表掃描,導致查詢讀取100萬行,並評估每行的WHERE子句表達式。
而使用裸柱:
mysql> EXPLAIN SELECT UPPER(column1) AS name1 FROM MyTable
WHERE column1 LIKE 'ADAM%'\G
id: 1
select_type: SIMPLE
table: MyTable
partitions: NULL
type: range
possible_keys: column1
key: column1
key_len: 153
ref: NULL
rows: 1000 <-- much better!
filtered: 100.00
Extra: Using where; Using index
使用Column1上的索引(如果索引存在),並僅檢查匹配的行。 你不僅可以避免100萬次評估UPPER()
,而且還可以避免檢查99.9%的行!
你必須記住的事情叫做sargability 。 這意味着在給定搜索表達式的情況下,MySQL可以使用索引來查找值。 在函數調用中放置一個列可以破壞sargability。
您在評論中詢問MySQL虛擬列是否有幫助。 正如我上面所寫,這不是必要的。 但是為了爭論,這是交易:
MySQL沒有像PostgreSQL中的表達式索引那樣的表達式索引 (在PostgreSQL中,你在索引定義中包含了表達式,當你在WHERE子句中使用相同的表達式時,它知道使用索引)。
MySQL有一些略有不同:您根據表達式定義虛擬列,然后您可以索引虛擬列。 但是您不要將表達式放在索引定義中。
mysql> ALTER TABLE MyTable
ADD COLUMN column1_upper VARCHAR(50) AS (UPPER(column1)),
ADD KEY (column1_upper);
然后,當您引用新列時,它可能會使用索引。 但是,您不使用已定義列的表達式,您只需使用列名裸,就像常規規則的sargability一樣。
邏輯上,FROM和WHERE子句在SELECT子句之前被評估,並且一些數據庫(我知道的Oracle,SQL Server)可以將謂詞別名分配給計算值(甚至是case表達式),這些可以在稍后的解釋中重用。 /執行計划。 MySQL解釋計划沒有透露我所知道的那種級別的信息,所以我無法確定MySQL是否能夠做到這一點。
另一個例子是“復雜”,例如UPPER或在GROUP BY和SELECT子句中重復的case表達式,SELECT子句受益於此,因為GROUP BY比SELECT早執行。 (這可能是之前提出的HAVING條款“技巧”有效的原因。)
所以,我認為理解條款的邏輯運作順序對此做出判斷是非常有用的。 例如,SQL操作的真正順序的初學者指南雖然我無法明確回答您的具體問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.