Mysql group_concat()重新命令结果集

时间:2014-10-16 13:34:54

标签: mysql sql group-by sql-order-by

考虑这样的mysql架构:

CREATE TABLE `translations` (
    group_id INT(11),
    lang VARCHAR(2),
    text VARCHAR(9),
    UNIQUE INDEX `group_id_lang` (`group_id`, `lang`)
)

这里的内容应该非常明显 - 我有一个group_id的翻译。

现在,如果我要运行以下查询,要获取所有使用首选语言的群组' EN',请回退到' RU',如果这些翻译都不存在,那么回归任何可用的语言,一切都作为魅力

SELECT *
FROM (
    SELECT *
    FROM translations
    ORDER BY lang = 'EN' DESC, lang = 'RU' DESC
) translations
GROUP BY group_id

+----------+------+---------+
| group_id | lang |  text   |
+----------+------+---------+
|        1 | EN   | Estonia |
|        2 | EN   | England |
|        3 | RU   | Швеция  |
+----------+------+---------+

如你所见,我会得到3排,最后一排倒回到RU' RU'因为' EN'没有出现在那个群体上。到目前为止一切都很好..

现在,如果我还想通过添加GROUP_CONCAT(lang) AS translations选择所有可用语言作为列,那么事情会有点模糊:

SELECT *, GROUP_CONCAT(lang) AS available_translations
FROM (
    SELECT *
    FROM translations
    ORDER BY lang = 'EN' DESC, lang = 'RU' DESC
) translations
GROUP BY group_id

+----------+------+-----------+--------------+
| group_id | lang |   text    | translations |
+----------+------+-----------+--------------+
|        1 | ET   | Eesti     | ET,EN,RU     |
|        2 | ET   | Inglismaa | ET,EN,RU     |
|        3 | RU   | Швеция    | RU,ET        |
+----------+------+-----------+--------------+

现在,mysql并不尊重我的ORDER BY子句,并且按照原生顺序进行分组,因为根本没有排序。

为什么GROUP_CONCAT会导致此问题?

  • 我最好的猜测是mysql正在重新排序结果以便于文本分组..

如何使其能够使用首选语言以及按行列出可用的翻译?

另外,如果你想摆弄here you go

1 个答案:

答案 0 :(得分:3)

在最外层查询之前不保证结果的顺序,因此即使您已经订购了子查询,这也不是读取记录的保证顺序,而是由外部查询返回。但主要的问题是,你是通过扩展来滥用MySQL Group,它允许你选择group by子句或聚合函数中没有包含的列。

为了简化您的查询,您需要:

SELECT  group_id, text
FROM    translations
GROUP BY group_id
ORDER BY lang = 'EN' DESC, lang = 'RU' DESC

除非text在功能上依赖于group_id,否则这会破坏sql标准,但是,MySQL允许它,但是,正如MySQL documents中所述

  

服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的。此外,添加ORDER BY子句不会影响每个组中值的选择。

因此,即使您有一个订单,但在每个组选择了一行之后才会适用,并且这一行是不确定的。您所做的只是订购最终结果集。

为了解决这个问题并获得确定结果,您可以使用GROUP_CONCAT将所有文本连接在一起,并明确说明您所需的顺序:

GROUP_CONCAT(text ORDER BY lang = 'EN' DESC, lang = 'RU' DESC)

然后使用SUBSTRING_INDEX提取第一个词。所以你的查询变为:

SELECT  Group_id,
        SUBSTRING_INDEX(
                    GROUP_CONCAT(lang
                                ORDER BY lang = 'EN' DESC, 
                                        lang = 'RU' DESC), 
                    ',', 1) AS lang,
        SUBSTRING_INDEX(
                    GROUP_CONCAT(text
                                ORDER BY lang = 'EN' DESC, 
                                        lang = 'RU' DESC), 
                    ',', 1) AS Text,
        GROUP_CONCAT(lang ORDER BY lang = 'EN' DESC, 
                                    lang = 'RU' DESC) AS available_translations
FROM    translations
GROUP BY Group_id;

<强> Example on SQL Fiddle