2016-11-28 84 views
0

背景:服務器應用程序(C#)使用下面的查詢(簡化)從數據庫中提取訂單數據。我記錄了這需要的平均時間,現在執行時間突然增加(平均從5ms到50ms)。所以我開始檢查這個查詢。使用變量時不使用索引

String ordernr = "123456789"; 
String sql = "SELECT * FROM MYDB.`MYTABLE` WHERE id = @id"; 
using (MySqlCommand cmd = new MySqlCommand(sql, conn)) 
{ 
    cmd.Parameters.Add("@id", MySqlDbType.VarChar, 16).Value = ordernr; 
    using (MySqlDataReader reader = cmd.ExecuteReader()) 
    { 
     //reading data 
    } 
} 

當我檢查在MySQL查詢時,我得到如下結果:

MariaDB [MYDB]> SET @id = '123456789'; 
Query OK, 0 rows affected (0.00 sec) 

MariaDB [MYDB]> explain SELECT SQL_NO_CACHE * FROM MYDB.`MYTABLE` WHERE id = @id ; 
+------+-------------+-----------+------+---------------+------+---------+------+---------+-------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra  | 
+------+-------------+-----------+------+---------------+------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | MYTABLE | ALL | NULL   | NULL | NULL | NULL | 1448219 | Using where | 
+------+-------------+-----------+------+---------------+------+---------+------+---------+-------------+ 
1 row in set (0.00 sec) 

MariaDB [MYDB]> explain SELECT SQL_NO_CACHE * FROM MYDB.`MYTABLE` WHERE id = '123456789'; 
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+ 
| id | select_type | table  | type | possible_keys | key  | key_len | ref | rows | Extra | 
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+ 
| 1 | SIMPLE  | MYTABLE | const | PRIMARY  | PRIMARY | 18  | const | 1 |  | 
+------+-------------+-----------+-------+---------------+---------+---------+-------+------+-------+ 
1 row in set (0.00 sec) 

MariaDB [MYDB]> 

現在我的問題是,爲什麼

查詢從服務器應用程序如下執行當我使用變量時,PRIMARY索引被忽略了嗎?

我不知道這是性能下降的原因(因爲在這個例子中,兩個查詢都返回0.00秒),但這讓我皺眉爲什麼會有差異。我也在服務器應用程序中使用了一個準備好的查詢和一個變量。所以我想檢查一下這是否相關。

任何人都可以解釋這一點嗎?

+0

這是一個MySQL的問題。無論客戶端工具如何,您都會得到相同的行爲。 「ID」列的類型和索引定義是什麼?你是否傳遞一個整數ID的文本參數值? MySQL可能會將* table *數據轉換爲參數類型而不是相反,從而強制進行全表掃描 –

+0

這確實是一個varchar字段(varchar(16)。在數據庫中,以及在C#代碼中設置的時候添加參數)。我可以補充說。 – Johan

+0

爲什麼使用'varchar(6)'字段來存儲數字鍵?除了轉換和空間問題,排序規則是不同的,'2'在'123456'後出現。 –

回答

1

索引長度表示id實際上是一個char/varchar列(VARCHAR(16)將是我的猜測)。

然後,問題是表或列的字符集與連接之一不匹配。考慮以下幾點:

MariaDB [test]> create table t1 (id varchar(16) primary key, i int) charset latin1; 
Query OK, 0 rows affected (0.72 sec) 

MariaDB [test]> insert into t1 values ('123456789',1),('987654321',2); 
Query OK, 2 rows affected (0.08 sec) 
Records: 2 Duplicates: 0 Warnings: 0 

MariaDB [test]> set names latin1; 
Query OK, 0 rows affected (0.00 sec) 

MariaDB [test]> set @a='123456789'; 
Query OK, 0 rows affected (0.00 sec) 

MariaDB [test]> explain select * from t1 where id = @a; 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
| 1 | SIMPLE  | t1 | const | PRIMARY  | PRIMARY | 18  | const | 1 |  | 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
1 row in set (0.00 sec) 

MariaDB [test]> set names utf8; 
Query OK, 0 rows affected (0.00 sec) 

MariaDB [test]> set @a='123456789'; 
Query OK, 0 rows affected (0.01 sec) 

MariaDB [test]> explain select * from t1 where id = @a; 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
| 1 | SIMPLE  | t1 | ALL | NULL   | NULL | NULL | NULL | 2 | Using where | 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

您需要任何修改表,或設置字符在會話中設置,或使用顯式轉換:

MariaDB [test]> explain select * from t1 where id = @a; 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
| 1 | SIMPLE  | t1 | ALL | NULL   | NULL | NULL | NULL | 2 | Using where | 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

MariaDB [test]> explain select * from t1 where id = convert(@a using latin1); 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra | 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
| 1 | SIMPLE  | t1 | const | PRIMARY  | PRIMARY | 18  | const | 1 |  | 
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------+ 
+0

轉換變量確實使用INDEX。現在我需要檢查我的服務器配置並查看那裏使用了哪些字符集。謝謝! – Johan

+0

也許這會避免charset問題:'CAST(@a AS CHAR)' –

+0

我不這麼認爲。作爲'CHAR'投射仍然會受到同樣的連接字符集設置的影響:如果當前字符集錯誤,投射將無濟於事;如果字符集正確,則不需要投射。 'CAST(@a AS BINARY)' - 更可能。 – elenst