[英]Optimize MySQL query with dependent sub-query
我需要找到一種消除依賴子查詢的方法。
我有一個可以有多種語言的文章表。 簡化的表結構如下:
id,title,language,translation_set_id
1 A en 0
2 B en 2
3 B_ru ru 2
4 C en 4
5 C_ru ru 4
6 D en 6
7 D_fr fr 6
當文章沒有翻譯時,translation_set_id為0,或者設置為基本翻譯的id。 因此B是原始的英文文章,而B_ru是該文章的俄文翻譯。
我需要一個允許我返回所有俄語文章的查詢,或者如果它們不存在原始語言文章。 所以它會回來。
1 A en 0
3 B_ru ru 2
5 C_ru ru 4
6 D en 6
到目前為止我有這個:
SELECT id, title, language, translation_set_id
FROM articles a
WHERE
a.translation_set_id = 0
OR (a.language = 'ru')
OR (a.id = a.translation_set_id AND
0 = (SELECT COUNT(ac.id)
FROM articles ac
WHERE ac.translation_set_id = a.translation_set_id
AND ac.language = 'ru')
)
但是這會為每一行執行子查詢,從而創建一個從屬查詢。 有沒有辦法消除依賴查詢?
更新: Neels的解決方案似乎有效,謝謝!
但我想知道是否有辦法將解決方案推廣到多語言回退? 首先嘗試獲取法語,如果不存在,請嘗試使用俄語,如果不存在,請顯示基本翻譯(英語或其他任何內容,具體取決於原始創建語言)?
UPDATE2:我使用Neel的解決方案和DRapp的解決方案構建了我所需的查詢。 它可以在http://www.sqlfiddle.com/#!2/28ca8/18找到,但為了完整起見,我也會在這里通過查詢。
修訂數據:
CREATE TABLE articles (
id INT,
title VARCHAR(20),
language VARCHAR(20),
translation_set_id INT);
INSERT INTO articles values
(1,'A','en',0),
(2,'B','en',2),
(3,'B_ru','ru',2),
(4,'C','en',4),
(5,'C_ru','ru',4),
(6,'D','en',6),
(7,'D_fr','fr',6),
(8,'E_ru','ru', 0),
(9,'F_fr','fr', 0),
(10,'G_ru','ru', 10),
(11,'G_fr','fr', 10),
(12,'G_en','en', 10);
具有2個相關子查詢的原始查詢:
SELECT id, title, language, translation_set_id
FROM articles a
WHERE
a.translation_set_id = 0
OR (a.language = 'fr')
OR (a.language = 'ru' AND
0 = (SELECT COUNT(ac.id)
FROM articles ac
WHERE ac.translation_set_id = a.translation_set_id
AND ac.language = 'fr'))
OR (a.id = a.translation_set_id AND
0 = (SELECT COUNT(ac.id)
FROM articles ac
WHERE ac.translation_set_id = a.translation_set_id
AND (ac.language = 'fr' OR ac.language = 'ru'))
);
修改后的查詢:
SELECT a.*
FROM articles a
LEFT JOIN articles ac ON ac.translation_set_id = a.id
AND ac.language = 'fr'
LEFT JOIN articles ac2 ON ac2.translation_set_id = a.id
AND ac2.language = 'ru'
WHERE a.translation_set_id = 0
OR a.language = 'fr'
OR (a.language = 'ru' AND ac.id IS NULL)
OR (a.id = a.translation_set_id AND ac2.id IS NULL AND ac.id IS NULL);
根據更簡化的where子句從Ypercube稍作修改調整,並且你不能使用coalesce(),我已修改為以下內容。
獲取Translated = 0的所有文章,或者ID與翻譯相同,表明在將其翻譯成其他內容之前,它必須是原始文檔。 也就是說,保證所有原始文件。
現在,左連接。 如果有相應的“俄語”文章(或其他感興趣的語言翻譯),請抓住該ID及其翻譯的標題。 因此返回的記錄包含原始和已翻譯的引用。
SELECT
a1.id as OriginalAricleID,
a1.title as OriginalTitle,
a1.language as OriginalLanguage,
a2.id as TranslatedAricleID,
a2.title as TranslatedTitle
from
Articles a1
LEFT JOIN Articles a2
ON a1.id = a2.translation_set_id
AND a2.language = 'ru'
where
a1.translation_set_id = 0
OR a1.id = a1.translation_set_id
它經過一次表,沒有重復。 左連接指向相同的文章表,但僅適用於基於原始文章的俄語語言集。
你可以使用LEFT JOIN
:
SELECT a.id, a.title, a.language, a.translation_set_id
FROM articles a
LEFT JOIN articles ac ON ac.translation_set_id = a.translation_set_id
AND ac.language = 'ru'
WHERE a.translation_set_id = 0
OR (a.language = 'ru')
OR ( a.id = a.translation_set_id
AND ac.id IS NULL
)
GROUP BY a.id, a.title, a.language, a.translation_set_id
看看這個SQL小提琴:
http://www.sqlfiddle.com/#!2/c05d0/15
您可以使用此簡單查詢來實現結果。
SELECT a.*
FROM articles a
LEFT OUTER JOIN articles ac ON ac.translation_set_id = a.translation_set_id
AND ac.language = 'ru'
WHERE a.translation_set_id = 0
OR a.language = 'ru'
OR (a.id = a.translation_set_id AND ac.id IS NULL);
重寫這部分:
AND
0 = (SELECT COUNT(ac.id)
FROM articles ac
WHERE ac.translation_set_id = a.translation_set_id
AND ac.language = 'ru')
進入反連接條件:
AND NOT EXISTS (
SELECT 1
FROM articles ac
WHERE ac.translation_set_id = a.translation_set_id
AND ac.language = 'ru'
)
這可能會加快查詢速度,因為MySql必須始終讀取所有行以獲取count(),
但是當使用NOT EXISTS
(或EXISTS
)時,它會在找到符合條件的第一行時停止讀取表格。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.