Mysql 有没有办法用if来改进这个查询?

Mysql 有没有办法用if来改进这个查询?,mysql,sql,database-performance,Mysql,Sql,Database Performance,我使用此查询从包含多种语言字符串的数据库中选择语言字符串。数据库如下所示: `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

我使用此查询从包含多种语言字符串的数据库中选择语言字符串。数据库如下所示:

`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
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
    )
)
例如,数据可以如下所示:

`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
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
    )
)
这是德语和英语中相同的字符串。德语的在英语的两秒钟后被换了

我使用这个(sub)查询来获取maching字符串。如果请求的语言不存在,它会自动回退到任何语言。例如,如果我在西班牙(=ID3)重新要求字符串,则此查询将返回到英语或德语:

这里的性能问题是,
IF(…,1,0)
删除了很多机会,因为每次执行查询时都必须计算结果

为了改进这个查询,我做了很多努力,但仍然创建了所有有用的索引。MySQL能够使用内部缓存命中此查询,但如果没有缓存,则需要一些时间进行计算。当获取大量行(例如1000行)时,这是一个性能问题,因为MySQL必须执行1000个子查询

你知道如何改进这个查询吗?添加新列以存储预先计算的数据将是我的一个选择

(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感谢您的注意。

这似乎是一个相关子查询,假设表a上有相当多的行,这将非常低效。最好将其重新编码为联接子查询

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
可能如下:-

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
这将获取语言为3的字符串id的最新日期,然后获取连接以获取与其匹配的文本,以及语言不是3的字符串id的最新日期,然后获取连接以获取与其匹配的文本


然后使用IFNULL返回语言3的文本,如果没有找到,则返回语言3以外的文本。

当我测试了所有发布的解决方案并对它们的复杂性感到头疼时,我想一定有更好的方法来实现这一点。受@Twinkles的
COALESCE
启发,在我决定使用另一个“临时”表来尝试相同的代码之前,我并不知道该表包含了所有可能的解决方案

这个小查询生成该表并保证每种语言都有一个明确的条目:

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`
然后,我的子查询可以如下所示:

`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
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倍。如果编译表根本不知道一些新的语言字符串,那么它可以回退到“旧”解决方案


感谢所有的解决方案,到目前为止,除了每次遇到“sub-sub-query”问题外,我都尝试了它们。

您似乎觉得
联合all
可以保证结果集中的特定顺序。我不认为你是对的。谢谢你(也谢谢你在@StanislavL的解决方案中发现了这个问题)。我认为这是可行的,但我有一个问题,MySQL不知道
a.joined\u string\u id
a.joined\u other\u table\u在第一级上,在第三级上,joined\u string\u id
(因为我在子查询中运行它)。我只使用临时变量就可以了,还有其他方法可以让最深的子查询中的列知道吗?MySQL不支持完全的外部连接(不幸的是,正常的解决方案是使用一对未定义的查询来模拟它)。我已经编辑了我的答案,以便将对
字符串\u id
的检查移出子查询。@Kickstart Bummer,完全忘记了。如果有人正在使用符合ANSI SQL标准的数据库阅读此内容,我将保留我的答案。第一次选择中是否缺少
joined\u string\u id
?您似乎仍在使用相关子查询(即依赖于查询外部字段的子查询),这很容易给您带来性能问题。问题是我们在查询中使用这个“片段”作为makro,以获得特定上下文中所需的本地化字符串。很难从
SELECT
区域移动/拆分此makro以匹配您的模式。这很公平,但在检索大量行时,您可能会遇到此查询的性能问题(因为它可能需要为每个查询执行1000次子查询)。试图保持事物通用性的缺点。