想象一下,我在名为“messages”的表中有以下数据:
message_id | language_id | message
------------------------------------
1 en Hello
1 de Hallo
1 es Hola
2 en Goodbye
2 es Adios
(请注意,我没有“Goodbye”的德语翻译。)
我想为说英语和德语但更喜欢德语的用户选择信息。
意思是,我想要一个看起来像的结果集:
message_id | language_id | message
------------------------------------
1 de Hallo
2 en Goodbye
但是,嗯,它证明是棘手的。有什么想法吗?
答案 0 :(得分:2)
select message_id, language_id, message
from
(select if(language_id="de",0,1) as choice, m.*
from messages m where m.language_id in ("de","en")
order by choice) z
group by message_id
通过选择中的“if”设置首选项以强制首选语言到结果集的顶部,因此分组依据将选择它。
您也可以这样做,但上面的回答可能更适合您想要使用它的内容。
select *
from messages m where m.language_id = "de" or
(language_id = "en" and not exists (select 1 from messages n
where n.language_id = "de" and
n.message_id = m.message_id))
继续你的评论。如果您对使用GROUP BY的特定MySQL行为(没有聚合函数)感到不舒服,那么您可以使用这个更标准的代码:
select *
from messages m where m.language_id in ("de","en")
and if(m.language_id="de",0,1) <= (select min(if(n.language_id="de",0,1))
from messages n where n.message_id = m.message_id)
答案 1 :(得分:2)
此查询将完全符合您的需求:
SELECT * FROM (
SELECT * FROM messages
WHERE language_id IN ('en', 'de')
ORDER BY FIELD(language_id, 'en', 'de') DESC
) m
GROUP BY message_id;
FIELD(language_id, 'en', 'de')
中的语言应按优先级排序:最新的语言(本例中为“de”)具有更高的优先级,然后是“en”,然后是其他所有语言。
WHERE
子句在这里是可选的,只有在“en”和“de”都没有翻译的情况下你不想要任何结果时才需要。
编辑: Sean提到非聚合列上的GROUP BY子句可能会产生不可靠的结果。这可能是真的,至少MySQL手册says so(虽然在实践中,第一个匹配行总是(?)使用)。
无论如何,还有另一个具有相同想法的查询,但没有提到的问题。
SELECT m1.*
FROM messages AS m1
INNER JOIN (
SELECT message_id, MAX(FIELD(language_id, 'en', 'de')) AS weight
FROM messages
WHERE language_id IN ('en', 'de')
GROUP BY message_id
) AS m2
USING(message_id)
WHERE FIELD(m1.language_id, 'en', 'de') = m2.weight;
答案 2 :(得分:0)
这是一个可能的解决方案:
首先我要设置你的表:
DROP TEMPORARY TABLE IF EXISTS messages;
CREATE TEMPORARY TABLE messages (
message_id INT,
language_id INT,
message VARCHAR(64)
);
INSERT INTO messages VALUES
(1, 1, "Hello"),
(1, 2, "Hellode"),
(1, 3, "Hola"),
(2, 1, "Goodbye"),
(2, 3, "Adios");
并添加了一个新的语言偏好:
DROP TEMPORARY TABLE IF EXISTS user_language_preference;
CREATE TEMPORARY TABLE user_language_preference (
user_id INT,
language_id INT,
preference INT
);
INSERT INTO user_language_preference VALUES
(1, 1, 10), # know english
(1, 2, 100); # but prefers 'de'
查询..
您好:
SET @user_id = 1;
SET @message_id = 1;
# Returns 'Hellode', 'Hello'
SELECT
m.language_id,
message
FROM messages AS m, user_language_preference AS l
WHERE message_id=@message_id
AND m.language_id=l.language_id
AND user_id=@user_id
ORDER BY preference DESC;
再见:
SET @message_id = 2;
# Returns 'Goodbye' as 'de' doesn't have a message there
SELECT
m.language_id,
message
FROM messages AS m, user_language_preference AS l
WHERE message_id=@message_id
AND m.language_id=l.language_id
AND user_id=@user_id
ORDER BY preference DESC;
编辑:回复评论:
SELECT
m.message_id,
m.language_id,
message
FROM messages AS m, user_language_preference AS l
WHERE m.language_id=l.language_id
AND user_id=@user_id
ORDER BY m.message_id, preference DESC;
答案 3 :(得分:0)
使用group-concat技巧在一个查询中获取此内容:
select message_id,
substring(max(concat(if(language_id='de', 9, if(language_id='en',8,0)), message)),2) as message,
substring(max(concat(if(language_id='de', 9, if(language_id='en',8,0)), language_id)),2) as language
from messages
group by message_id;
只需在IF子句中添加条件和适当的优先级即可添加更多后备语言。
答案 4 :(得分:0)
SELECT *
FROM messages
WHERE (message_id,CASE language_id WHEN 'de' THEN 1 WHEN 'en' THEN 2 ELSE NULL END) IN (
SELECT message_id, MIN(CASE language_id WHEN 'de' THEN 1 WHEN 'en' THEN 2 ELSE NULL END) pref_language_id
FROM `messages`
GROUP BY message_id
)
您必须将用户首选语言中的 CASE language_id WHEN'de'THEN 1'EN'TEN 2 ELSE NULL END 更改为用户首选语言。如果他有第三个,只需添加另一个案例,例如。 CASE language_id WHEN'de'那么1当'en'那么2当'es'然后3'结束时。
答案 5 :(得分:0)
这是分组最大查询的一个很好的示例。 http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
这是我想出的。使用与simendsjo相同的数据和架构。
SELECT prefered.message_id, p2.language_id, message FROM
(SELECT message_id, MAX(preference) AS prefered FROM messages m
JOIN user_language_preference p ON p.language_id = m.language_id AND p.user_id = 1
GROUP BY m.message_id) AS prefered
JOIN user_language_preference p2 ON prefered = p2.preference AND p2.user_id = 1
JOIN messages m2 ON p2.language_id = m2.language_id AND m2.message_id = prefered.message_id
这是它的工作原理。
prefered
选择所有邮件,将其加入用户语言首选项,计算每封邮件的最大首选项(GROUP BY m.messsage id
)。如果现在有翻译,则最大值将用于下一个首选语言,依此类推...... MAX(preference) = prefered = p2.preference
)获取语言ID。 m2
只选择已知首选语言_id和message_id的翻译。PS。不要忘记更改两次出现的user_id。
答案 6 :(得分:0)
编辑添加一些与问题性质相对应的替代解决方案。 :d
(FWIW:第二选择是我的第一次实施)
这个应该能够提供最好的表现,尽管有点难以理解 更重要的是,它可以更好地扩展到包括第4,第5,第6等语言 该解决方案需要一个定义语言优先级的临时表(使用mysql中最好的技术) 解决方案的核心在于'finder'子查询;一旦确定了可用的最佳优先级语言,加入回来获取实际消息是一件简单的事情。
declare @prio table (prio_id int, lid varchar(5))
insert into @prio values(1, 'de')
insert into @prio values(2, 'en')
insert into @prio values(3, 'es')
select m.*
from (
select message_id, MIN(prio_id) prio_id
from @messages m
inner join @Prio p on
p.lid = m.language_id
group by message_id
) finder
inner join @Prio p
on p.prio_id = finder.prio_id
inner join @messages m
on m.message_id = finder.message_id
and m.language_id = p.lid
以下查询结构应该很容易遵循 每个联合添加到结果集中的任何消息ID都不在结果集中 UNION ALL足够,因为每个后续查询都不保证重复 (language_id,message_id)上的索引应该提供最佳性能(特别是如果它是聚类的)。
select message_id, language_id, message
from messages
where language_id = 'de'
union all
select message_id, language_id, message
from messages
where language_id = 'en'
and message_id not in (select message_id from messages where language_id in ('de'))
union all
select message_id, language_id, message
from messages
where language_id = 'es'
and message_id not in (select message_id from messages where language_id in ('de', 'en'))
这是一个使用COALESCE功能的有趣信息 但是,我不认为它会在大量数据上表现良好。
select *,
COALESCE(
(select language_id from @messages where message_id = m.message_id and language_id = 'de'),
(select language_id from @messages where message_id = m.message_id and language_id = 'en'),
(select language_id from @messages where message_id = m.message_id and language_id = 'es')
) language_id,
COALESCE(
(select message from @messages where message_id = m.message_id and language_id = 'de'),
(select message from @messages where message_id = m.message_id and language_id = 'en'),
(select message from @messages where message_id = m.message_id and language_id = 'es')
) message
from (
select distinct message_id
from @messages
) m
答案 7 :(得分:-2)
本文描述了我发现的最快的解决方案,它提供了我所追求的结果集:
http://onlamp.com/pub/a/mysql/2007/03/29/emulating-analytic-aka-ranking-functions-with-mysql.html