我使用此查询从包含多种语言字符串的数据库中选择语言字符串。数据库如下所示:
`string_id` BIGINT
`language_id` BIGINT
`datetime` DATETIME
`text` TEXT
例如,数据可能如下所示:
`string_id` | `language_id` | `datetime` | `text`
1 | 1 | 2014.04.22 14:43:00 | hello world
1 | 2 | 2014.04.22 14:43:02 | hallo welt
所以这是德语和英语中的相同字符串。德语版改为英文版后两秒钟。
我使用此( sub )查询来获取加工字符串。如果请求的语言不存在,它会自动回退到任何语言。因此,例如,如果我要求西班牙语中的字符串(= id 3),此查询将回退到英语或德语:
SELECT
z.`text`
FROM
`language_strings` AS z
WHERE
a.`joined_string_id` = z.`string_id`
ORDER BY
IF(z.`language_id` = 3, 1, 0) DESC,
z.`datetime` DESC
LIMIT
1
这里的性能问题是IF(..., 1, 0)
删除了很多机会,因为每次执行查询时都必须计算结果。
我尝试了很多来改进这个查询,仍然创建了所有有用的索引。 MySQL能够使用内部缓存命中此查询,但没有缓存需要一些时间来计算。当获取大量行(例如1000)时,这是一个性能问题,因为MySQL必须执行1000个子序列。
您是否知道如何改进此查询?添加新列来存储预先计算的数据对我来说是一个选择。
答案 0 :(得分:1)
(SELECT
1 as ord, z.`text`
FROM
`language_strings` AS z
WHERE
a.`joined_string_id` = z.`string_id` and z.`language_id` = 3
limit 1)
union all
(SELECT
2 as ord, z.`text`
FROM
`language_strings` AS z
WHERE
a.`joined_string_id` = z.`string_id`
ORDER BY
z.`datetime` DESC
LIMIT 1)
ORDER BY ord
LIMIT 1
更新。 Twinkles谢谢你的说明。
答案 1 :(得分:1)
SELECT COALESCE(primary.`text`,fallback.`text`)
FROM (
SELECT 1 `ord`, z.`text`, z.`datetime`
FROM `language_strings` AS z
WHERE z.`language_id` = 3
) primary
FULL OUTER JOIN
(
SELECT 2 `ord`, z.`text`, z.`datetime`
FROM `language_strings` AS z
) fallback
ON (primary.`string_id` = fallback.`string_id`
AND primary.`string_id` = a.`joined_string_id`)
ORDER BY `ord` ASC, `datetime` DESC
LIMIT 1
答案 2 :(得分:1)
这似乎是一个相关的子查询,假设表a上有相当多的行,效率非常低。可能最好将其重新编码为已加入的子查询。
可能如下: -
SELECT a.*, IFNULL(ls1.`text`, ls2.`text`)
FROM some_table a
LEFT OUTER JOIN
(
SELECT string_id, MAX(datetime) AS MaxDateTime
FROM language_strings
WHERE language_id = 3
GROUP BY string_id
) AS MainLanguage1
ON a.joined_string_id = MainLanguage1.string_id
LEFT OUTER JOIN language_strings ls1
ON MainLanguage1.string_id = ls1.string_id AND MainLanguage1.datetime = ls1.MaxDateTime
LEFT OUTER JOIN
(
SELECT string_id, MAX(datetime)
FROM language_strings
WHERE language_id != 3
GROUP BY string_id
) AS MainLanguage2
ON a.joined_string_id = MainLanguage2.string_id
LEFT OUTER JOIN language_strings ls2
ON MainLanguage2.string_id = ls2.string_id AND MainLanguage2.datetime = ls2.MaxDateTime
这将获取string_id的最新日期,其中语言为3,然后是连接以获取匹配的文本,以及aa string_id的最新日期,其中语言不是3,然后是连接以获取与之匹配的文本。
然后返回的文本只是使用IFNULL带回来恢复语言3的文本,如果没有找到,则返回3以外语言的文本。
答案 3 :(得分:0)
虽然我测试了所有已发布的解决方案并且对它们的复杂性感到头疼,但我认为必须有更好的方法来实现这一点。来自@Twinkles的COALESCE
的启发,在我决定尝试使用另一个代码,#34;临时"之前我知道了。绝对包含所有可能解决方案的表格。
这个小小的查询会生成该表,并保证每种语言都有一个条目:
INSERT INTO
`language_strings_compiled`
(
`string_id`,
`language_id`,
`text`
)
SELECT
a.`string_id`,
b.`language_id`,
(
SELECT
z.`text`
FROM
`language_strings` AS z
WHERE
a.`string_id` = z.`string_id`
ORDER BY
IF(z.`language_id` = b.`language_id`, 1, 0) DESC,
z.`datetime` DESC
LIMIT 1
) AS `text`
FROM
`language_strings` AS a
JOIN
`languages` AS b
GROUP BY
a.`string_id`,
b.`language_id`
然后,我的子查询可能如下所示:
COALESCE
(
(
SELECT
z.`text`
FROM
`language_strings_compiled` AS z
WHERE
a.`joined_string_id` = z.`string_id`
AND
z.`language_id` = 3
LIMIT
1
),
(
SELECT
z.`text`
FROM
`language_strings` AS z
WHERE
a.`joined_string_id` = z.`string_id`
ORDER BY
IF(z.`language_id` = 3, 1, 0) DESC,
z.`datetime` DESC
LIMIT
1
)
)
此解决方案比没有"编译"的解决方案快10倍。表。它能够回归到旧的"解决方案,如果有一些新的语言字符串根本不为编译表所知。
感谢所有的解决方案,我尝试了所有这些,但每次我遇到"子子查询" -problem到目前为止。