繁体   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