2014-10-06 235 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