簡體   English   中英

SQL SELECT 帶條件 WHERE 子句

[英]SQL SELECT with conditional WHERE clause

抽象的:

我有一個 SELECT 查詢,其中包含我希望滿足的 WHERE 條件。 在某些情況下,不會滿足這個 WHERE 條件,在這種情況下,我想使用不同的 WHERE 條件。 這就是我面臨的抽象問題。 這是一個更具體的例子:

例子:

我有一個tag表和一個tag_l11n表。 標簽表包含標簽的基本信息, tag_l11n表包含標簽的本地化名稱。 在以下簡化的 SELECT 中,我請求帶有英文名稱的標簽 ( tag_l11n_language = 'en' )

詢問:

SELECT 
    `tag_3`.`tag_id` as tag_id_3,
    `tag_l11n_2`.`tag_l11n_title` as tag_l11n_title_2
FROM 
    `tag` as tag_3 
LEFT JOIN 
    `tag_l11n` as tag_l11n_2 ON `tag_3`.`tag_id` = `tag_l11n_2`.`tag_l11n_tag` 
WHERE 
    `tag_l11n_2`.`tag_l11n_language` = 'en' 
ORDER BY 
    `tag_3`.`tag_id` ASC 
LIMIT 25;

問題:

如果標簽沒有特定的翻譯,問題就開始了。 例如,標簽可能存在於英語中,但不存在於例如意大利語中。 但是,意大利人也會接受英語(或任何其他語言) IFF (當且僅當)意大利語翻譯不存在的標簽。

最后,我更喜歡一個可以指定不同優先級的解決方案(1. 用戶本地化,2. 英語,3. 任何其他語言)。

我在這里有點不知所措。 雖然我可以輕松地省略條件(語言 =??)並在輸出/演示期間過濾結果,但我認為這不是 go 的方法。

您可以在WHERE中添加一個子句,如果意大利語翻譯不存在,它將返回英語翻譯,使用NOT EXISTS子句:

WHERE 
    `tag_l11n_2`.`tag_l11n_language` = 'it' 
OR
    `tag_l11n_2`.`tag_l11n_language` = 'en'
    AND NOT EXISTS (SELECT *
                    FROM tag_l11n t3 
                    WHERE t3.tag_l11n_tag = tag_3.tag_id 
                      AND t3.tag_l11n_language = 'it')

另一個(可能性能更高,因為它沒有OR條件)解決方案是向tag_l11n_2 LEFT JOIN兩次,一次用於所需語言,一次用於備份,並使用COALESCE優先考慮所需的語言結果:

SELECT 
    `tag_3`.`tag_id` as tag_id_3,
    COALESCE(`tag_l11n_2`.`tag_l11n_title`, `tag_l11n_3`.`tag_l11n_title`) as tag_l11n_title_2
FROM 
    `tag` as tag_3 
LEFT JOIN 
    `tag_l11n` as tag_l11n_2 ON `tag_3`.`tag_id` = `tag_l11n_2`.`tag_l11n_tag` 
                            AND `tag_l11n_2`.`tag_l11n_language` = 'it' 
LEFT JOIN 
    `tag_l11n` as tag_l11n_3 ON `tag_3`.`tag_id` = `tag_l11n_3`.`tag_l11n_tag` 
                            AND `tag_l11n_3`.`tag_l11n_language` = 'en' 
ORDER BY 
    `tag_3`.`tag_id` ASC 
LIMIT 25;

請注意,通過將LEFT JOIN和適當的列添加到COALESCE ,可以將其擴展到所需的盡可能多的備份語言。

dbfiddle 上的演示(兩個查詢)

如果您正在運行 MySQL 8.0,我建議使用row_number()來處理優先級:

SELECT tag_id_3, tag_l11n_title_2
FROM (
    SELECT 
        t.`tag_id` as tag_id_3,
        tl.`tag_l11n_title` as tag_l11n_title_2,
        ROW_NUMBER() OVER(PARTITION BY t.`tag_id` ORDER BY
            (tl.`tag_l11n_language` = 'it') desc,
            (tl.`tag_l11n_language` = 'en') desc,
            tl.`tag_l11n_language`
        ) rn
    FROM `tag` as t
    LEFT JOIN `tag_l11n` as tl ON t.`tag_id` = tl.`tag_l11n_tag` 
) t
WHERE rn = 1
ORDER BY t.`tag_id`
LIMIT 25;

使用此解決方案,您可以通過向ROW_NUMBER()ORDER BY子句添加更多條件來管理任意多個級別的優先級。 目前,這將意大利語翻譯放在首位,然后是英語翻譯,然后是任何其他可用的翻譯(按字母順序)。

您的 WHERE 條件將 LEFT 更改為 INNER 連接,當en沒有 no 行時,不會返回任何行。

經典解決方案使用一個左連接 perm 語言,然后使用 COALESCE 來獲取第一個非 NULL 值:

SELECT 
    `tag_3`.`tag_id` as tag_id_3,
    coalesce(`tag_l11n_it`.`tag_l11n_title`  -- same order as join order
            ,`tag_l11n_en`.`tag_l11n_title`
            ,`tag_l11n_2`.`tag_l11n_title`) as tag_l11n_title_2
FROM 
    `tag` as tag_3 
LEFT JOIN 
    `tag_l11n` as tag_l11n_it
ON `tag_3`.`tag_id` = `tag_l11n_it`.`tag_l11n_tag` 
AND -- WHERE changes the LEFT join to INNER 
    `tag_l11n_it`.`tag_l11n_language` = 'it'  -- main language

LEFT JOIN 
    `tag_l11n` as tag_l11n_en 
ON `tag_3`.`tag_id` = `tag_l11n_en`.`tag_l11n_tag` 
AND
    `tag_l11n_en`.`tag_l11n_language` = 'en'  -- 2nd language

LEFT JOIN 
    `tag_l11n` as tag_l11n_2
ON `tag_3`.`tag_id` = `tag_l11n_2`.`tag_l11n_tag` 
AND
    `tag_l11n_2`.`tag_l11n_language` = 'de'   -- default language
ORDER BY 
    `tag_3`.`tag_id` ASC 
LIMIT 25;

雖然 GMB 的解決方案在沒有默認語言的情況下工作,但此解決方案需要默認(沒有缺少翻譯的語言),在您的情況下可能是de :-)

也許可以通過在連接應用 ROW_NUMBER 來改進 GMB 的查詢:

SELECT tag_id_3, tag_l11n_title_2
FROM `tag` as t
LEFT JOIN
 (
    SELECT 
        `tag_l11n_tag` as tag_id_3,
        `tag_l11n_title` as tag_l11n_title_2,
        ROW_NUMBER()
        OVER(PARTITION BY t.`tag_id`
             ORDER BY
               CASE WHEN `tag_l11n_language` = 'it' THEN 1
                  WHEN `tag_l11n_language` = 'en' THEN 2
                  ELSE 3
               END
            ,tl.`tag_l11n_language`) AS rn
    FROM `tag_l11n`
  ) AS t1
ON t.`tag_id` = tl.`tag_l11n_tag` 
WHERE t1.rn = 1
ORDER BY t.`tag_id`
LIMIT 25;

而且這種優先邏輯邏輯也可以與聚合一起使用:

SELECT tag_id_3, tag_l11n_title_2
FROM `tag` as t
LEFT JOIN -- INNER JOIN should be possible
 (
    SELECT 
        `tag_l11n_tag` as tag_id_3,
        COALESCE(MAX(CASE WHEN `tag_l11n_language` = 'it' THEN `tag_l11n_title` END)
                ,MAX(CASE WHEN `tag_l11n_language` = 'en' THEN `tag_l11n_title` END)
                ,MAX(CASE WHEN `tag_l11n_language` = 'de' THEN `tag_l11n_title` END)
                -- if there's no default you can get a random value using ,MAX(`tag_l11n_title`)
                ) AS tag_l11n_title_2
    FROM `tag_l11n`
    GROUP BY `tag_l11n_tag`
  ) AS t1
ON t.`tag_id` = tl.`tag_l11n_tag` 
ORDER BY t.`tag_id`
LIMIT 25; -- might be possible to move into Derived Table

感謝@Nick 的回答,我想出了使用 function FIELD()的新想法,這將涵蓋所有可能的語言,而無需為每種語言加入單獨的表格,但有一個子選擇。 與其他答案相比,我不確定性能,但如果語言按數值索引而不是字符串( enit等),它會更快。

當然,子選擇中應排除帶有 NULL 的翻譯。

SELECT 
    `tag`.`tag_id` as tag_id,
    COALESCE(`tag_l11n`.`tag_l11n_title`,"No translation") as tag_l11n_title
FROM 
    `tag` as tag
LEFT JOIN (
      SELECT
          `tag_l11n_tag` as tag_id,
          `tag_l11n_title` as tag_l11n_title,
      FROM 
          `tag_l11n`
      WHERE 
          `tag_l11n_title` IS NOT NULL,
      ORDER BY
          FIELD(`tag_l11n_language`,"it","en","es")
      LIMIT 1
      ) as tag_l11n ON `tag_l11n`.`tag_id` = `tag`.`tag_id`
ORDER BY 
    `tag`.`tag_id` ASC 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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