簡體   English   中英

MySQL如何選擇列..?

[英]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 |
+------------------+

Sargability

在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM