2014-04-22 135 views
2

我使用此查詢從包含多種語言的字符串的數據庫中選擇一種語言字符串。該數據庫是這樣的:有沒有辦法改善這個查詢,如果在它呢?

`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)查詢來獲得機加字符串。如果請求的語言不存在,它會自動回退到任何語言。因此,例如,這個查詢將回退到英語或德語,如果我requst西班牙的字符串(= 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個子查詢。

你知道如何改善這個查詢嗎?添加新列以存儲預先計算的數據對我來說是一種選擇。

回答

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 

已更新。閃爍的謝謝你的注意。

+1

您似乎認爲'UNION ALL'會保證結果集中的某個順序。我不認爲你是對的。 – Twinkles

+0

@Twinkles已更新 – StanislavL

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 
+0

非常感謝您的同意(並且還在@StanislavL的解決方案中檢測問題)。我認爲它可行,但我有問題,MySQL不知道在第三級中的a.joined_string_id或some_other_table_joined_on_the_first_level.joined_string_id(因爲我在子查詢中運行這個)。我只使用臨時變量來工作,是否有另一種方法可以在最深的子查詢中知道列? –

+0

MySQL不支持完整的外連接(不幸的是 - 普通的解決方案是使用一對未查詢的查詢來模擬它)。 – Kickstart

+0

我編輯了我的答案,將「string_id」的檢查移出子查詢。 – Twinkles

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 

這得到最新的日期爲string_id這種語言是3,再加入以獲得匹配的文本去用它,併爲AA的最後日期string_id語言不是3,然後是一個連接來獲得匹配的文本。

,然後將其返回只是帶回使用IFNULL帶回語言3文本,如果沒有找到,那麼超過3

0

其他語言的文字雖然我測試了所有發佈的解決方案文本,對他們的複雜性感到頭疼,我認爲必須有更好的方式來做到這一點。來自@ Twinkles的COALESCE的啓發,我決定試用相同的代碼並使用另一個「臨時」表,該表確實包含了所有可能的解決方案。

這一點查詢生成表和註冊就存在於每一種語言definitelya項:

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倍比沒有「編譯」表的解決方案更快。如果有一些新的語言字符串根本不被編譯表所知,它就能夠回退到「舊」解決方案。

感謝所有的解決方案,我嘗試了所有,但每次我跑到「子子查詢」 - 問題到目前爲止。

+0

您似乎仍在使用相關的子查詢(即依賴於來自查詢外部的字段的子查詢),並且這可能很容易導致性能問題。 – Kickstart

+0

問題是我們在查詢中使用這個「snippet」作爲有點makro來獲取我們在特定上下文中需要的本地化字符串。它非常難以從'SELECT'區域移動/分割這個makro以符合您的模式。 –

+0

不夠公平,但在檢索大量行時(因爲它可能不得不爲每個查詢執行1000次子查詢),您可能會在此查詢中遇到性能問題。試圖保持通用性。 – Kickstart

相關問題