簡體   English   中英

使用從屬子查詢優化MySQL查詢

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM