2014-10-06 48 views
3

我剛剛發現了這個非常有用的MySQL函數GROUP_CONCAT。它對我來說似乎非常有用和過分簡化,我實際上不敢使用它。主要是因爲我開始使用網絡編程已經有相當長的一段時間了,我從來沒有見過它。一個真棒使用示例將如下不使用GROUP_CONCAT的原因?

clients持有客戶端(你不會說...)每個客戶端一行具有唯一的ID。
currencies有3列client_id,currencyamount

現在,如果我想獲得從clients表用戶15的name和他的平衡,以陣列的「老」方法會覆蓋我會做使用下面的SQL

SELECT id, name, currency, amount 
FROM clients LEFT JOIN currencies ON clients.id = client_id 
WHERE clients.id = 15 

然後在PHP我不得不遍歷結果集,做一個陣列覆蓋(我真的不是一個大風扇,特別是在大量的結果集)像

$result = array(); 
foreach($stmt->fetchAll() as $row){ 
    $result[$row['id']]['name'] = $row['name']; 
    $result[$row['id']]['currencies'][$row['currency']] = $row['amount']; 
} 

然而新發現的功能,我可以用這個

SELECT id, name, GROUP_CONCAT(currency) as currencies GROUP_CONCAT(amount) as amounts 
FROM clients LEFT JOIN currencies ON clients.id = client_id 
WHERE clients.id = 15 
GROUP BY clients.id 

然後在應用層面的東西是如此真棒,漂亮

$results = $stmt->fetchAll(); 
foreach($results as $k => $v){ 
    $results[$k]['currencies'] = array_combine(explode(',', $v['currencies']), explode(',', $v['amounts'])); 
} 

這個問題我想請教的是有沒有什麼缺點使用性能或任何這種功能在所有的,因爲對我來說只是看起來像純粹的迷人,這讓我認爲,人們不應該經常使用它的理由是一定的。

編輯:

我要問,最終,有什麼其他的選擇,除了陣列覆蓋從一個MySQL結果集多維數組中結束了,因爲如果我選擇15列這是一個真正的大痛苦在脖子上寫那個野獸......

+1

它很方便,但它並不普遍。它可以返回的字符串的長度有限,默認情況下通常爲1024個字符。如果你有一個'大'數據集,你的連接字符串可以很容易超過1024個字符,它會被無聲地截斷/損壞。 – 2014-10-06 21:22:05

+1

這是所有覆蓋在這裏http://stackoverflow.com/questions/276927/can-i-concatenate-multiple-mysql-rows-into-one-field/276949#276949 – 2014-10-06 21:23:09

+1

該長度可以配置 – 2014-10-06 21:23:14

回答

6
  • 使用GROUP_CONCAT()通常會調用group-by邏輯並創建臨時表,這通常會對性能產生很大的負面影響。有時,您可以添加正確的索引以避免分組查詢中的臨時表,但不是在任何情況下。

  • 正如@MarcB指出的那樣,組連接字符串的默認長度限制很短,很多人被截斷列表弄糊塗了。您可以使用group_concat_max_len來增加限制。

  • 在PHP中將字符串分解爲數組並不是免費的。僅僅因爲你可以在PHP中使用一個函數調用它並不意味着它是最好的性能。我沒有對差異進行基準測試,但是我懷疑你是否有這種差異。

  • GROUP_CONCAT()是一個MySQLism。它不被其他SQL產品廣泛支持。在某些情況下(例如SQLite),它們有一個GROUP_CONCAT()函數,但它不能和MySQL中的完全一樣,所以如果你必須支持多個RDBMS後端,這會導致錯誤。當然,如果你不需要擔心移植,這不是一個問題。

  • 如果你想從你的currencies表中取多列,那麼你需要多個GROUP_CONCAT()表達式。列表是否保證按照相同的順序排列?也就是說,一個列表中的第三個字段是否與下一個列表中的第三個字段相對應?答案是否定的 - 除非您在GROUP_CONCAT()中使用ORDER BY子句來指定訂單。

我通常贊成你的第一編碼格式,使用傳統的結果集,並遍歷結果保存到由客戶端ID索引的新陣,追加貨幣的數組。這是一個簡單明瞭的解決方案,可以使SQL簡單易用並且更易於優化,並且如果您有多個要讀取的列,效果會更好。

我不是想說GROUP_CONCAT()是壞的!這在許多情況下非常有用。但是試圖制定任何通用規則來使用(或避免)任何功能或語言功能都很簡單。

+0

我在這個答案中想的是一個解釋,爲什麼沒有一個好的理由,甚至需要group_concat首先。 – wvdz 2014-10-06 21:33:43

+0

@popovitsj數組重寫包含大量的結果集和大量的選定列,效率低下,代碼很痛苦,這不是一個好的理由嗎? (並不是說group_concat更高效,只是想) – 2014-10-06 21:36:00

+0

@popovitsj,請參閱我的回答https://programmers.stackexchange.com/questions/90456/why-dont-relational-databases-support-returning-information-in-嵌套格式,包括評論中的討論。 – 2014-10-06 21:39:46

2

我看到的GROUP_CONCAT最大的問題是它對MySql非常具體:如果你想移植你的代碼來運行任何其他平臺,你將不得不重寫所有使用GROUP_CONCAT的查詢。例如,您的第一個查詢更便於攜帶 - 您可以針對任何主要的RDBMS引擎運行它,而無需更改其中的單個字符。

如果你只適用於MySql(比如,因爲你正在編寫一個專門針對MySql的工具),那麼GROUP_CONCAT的查詢可能會更快,因爲RDBMS會爲你做更多的工作,節省數據傳輸的大小。

+3

端口我的代碼?如果我的房間溫度變化,我的代碼將無法工作! lol':D' – 2014-10-06 21:32:08