2016-09-15 50 views
9

我有一個非常複雜的問題的列表,但我將範圍縮小到這一點,首先,讓我給你一些測試數據:用逗號MySQL的奇怪的行爲分隔的數字

運行以下命令:

CREATE TABLE `test` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `value` text NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; 

INSERT INTO test (value) VALUES 
(1), 
('1'), 
('1,2'), 
('3'); 

現在運行此查詢:

SELECT * FROM test WHERE value = 1; 

我希望在這種情況下,得到的只有前兩排,其中值,可以以數字1或「1」字符輸入,但由於某種原因,這是我得到:

1, 1 
2, 1 
3, 1,2 

我的問題是,爲什麼我得到第三行?

注:這是我的MySQL版本:5.6.28-0ubuntu0.14.04.1

而且,我已經用FIND_IN_SET解決了我原來的問題,我知道,這不是有一個很好的主意這個以逗號分隔的列表類型結構,也就是說,它應該首先用連接表完成。不幸的是,我正在一個非常大的系統中工作,目前這種變化是不實際的。

我只是對這種特定行爲發生的原因感興趣。

+1

我們可以改爲拒絕問題的前提?不要以逗號分隔的數字列表開頭。 – Strawberry

+3

@Strawberry:無論存儲以逗號分隔的數字列表是否是個好主意,這都是一個很好的問題。行爲是意想不到的。 – recursive

+0

您是否嘗試過InnoDb表格格式相同的行爲? – Whome

回答

9

您得到第三行的原因是由MySQL執行的隱式數據類型轉換。您查詢具有WHERE子句

WHERE value = 1 

在相等比較運算符(等號)右側的謂詞(條件),我們有一個數值文字。在左側,我們有一個數據類型爲TEXT的列。

MySQL不可能對這兩種不同的數據類型進行比較。

因此,MySQL將一邊或另一邊轉換爲兼容的類型,因此可以執行比較。在這種情況下,MySQL正在將列中的值轉換爲數字,因此它與數字字面值相比較。作爲示例,我們可以添加一個零(強制MySQL進行轉換),並在SELECT中展示結果。

SELECT t.value, t.value + 0 FROM test t 

t.value t.value + 0 
------- ----------- 
1     1 
1     1 
1,2    1 
3     3 

它被記錄在MySQL參考手冊的某處,MySQL如何進行轉換。在錯誤地指明手冊內容的情況下,MySQL會從左到右逐字符地讀取字符串,直到它遇到不能再轉換爲數字的字符。

在這種情況下,如果字符串'1,2',那恰好是逗號字符。這就是MySQL停止的地方。因此,轉換返回的數值爲1.您應該指出其他數據庫會嘗試將該字符串轉換爲數字時發生錯誤。但MySQL不會拋出錯誤或警告。

參考:Type Conversion in Expression Evaluation http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html

基本上,在查詢謂詞相當於指定:

WHERE value + 0 = 1 

這迫使的列value的到數字內容的變換,然後進行比較的數字文字。

這就是爲什麼第三行被返回。

爲了得到不同的結果,認爲相對於一個字符串字面

WHERE value = '1' 
+0

_注意將來的讀者_:Postgres會拋出一個錯誤,表示_operator不存在:text = integer_。 –

+0

這對我有意義,謝謝。 –

+1

@MikeNakis - 人們實現這種類型轉換是有原因的。你的評論不具有建設性,你甚至不知道MySQL的嚴格模式。我不會在這裏進行討論,但是這些評論「這是足夠理由切換到X」使你看起來像一個初學者。爲了所有閱讀本文的人,請不要這樣做。閱讀這些「我現在不用任何知識就會得出結論」的評論非常令人討厭,它確實對任何人都沒有幫助。 –