简体   繁体   English

SQL SELECT 带条件 WHERE 子句

[英]SQL SELECT with conditional WHERE clause

Abstract:抽象的:

I have a SELECT query with a WHERE condition which I would like to have met.我有一个 SELECT 查询,其中包含我希望满足的 WHERE 条件。 In some cases this WHERE condition will not be met and in such cases I would like to use a different WHERE condition.在某些情况下,不会满足这个 WHERE 条件,在这种情况下,我想使用不同的 WHERE 条件。 That is the abstract problem I'm facing.这就是我面临的抽象问题。 Here is a more specific example:这是一个更具体的例子:

Example:例子:

I have a tag table and a tag_l11n table.我有一个tag表和一个tag_l11n表。 The tag table contains basic information about a tag and the tag_l11n table contains the localized name of the tag.标签表包含标签的基本信息, tag_l11n表包含标签的本地化名称。 In the following simplified SELECT I'm requesting the tag with an English name ( tag_l11n_language = 'en' )在以下简化的 SELECT 中,我请求带有英文名称的标签 ( tag_l11n_language = 'en' )

Query:询问:

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;

Problem:问题:

The problem starts if a tag doesn't have a certain translation.如果标签没有特定的翻译,问题就开始了。 For example a tag may exist in English language but not in eg Italian language.例如,标签可能存在于英语中,但不存在于例如意大利语中。 However, the Italian guy would also accept the tags in English language (or any other language) IFF (if and only if) the Italian translation doesn't exist.但是,意大利人也会接受英语(或任何其他语言) IFF (当且仅当)意大利语翻译不存在的标签。

In the end I would prefer a solution where I could specify different priorities (1. localization of the user, 2. English, 3. any other language).最后,我更喜欢一个可以指定不同优先级的解决方案(1. 用户本地化,2. 英语,3. 任何其他语言)。

I'm a little bit at a loss here.我在这里有点不知所措。 While I could easily omit the condition (language =??) and filter the result during the output/presentation, I don't think this is the way to go.虽然我可以轻松地省略条件(语言 =??)并在输出/演示期间过滤结果,但我认为这不是 go 的方法。

You could add a clause to your WHERE which will return a translation in English if the Italian translation didn't exist, using a NOT EXISTS clause:您可以在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')

Another (possibly more performant given it will not have an OR condition) solution would be to LEFT JOIN to tag_l11n_2 twice, once for the desired language, and once for the backup, and using COALESCE to prioritise the desired languages result:另一个(可能性能更高,因为它没有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;

Note this can be expanded to as many backup languages as are desired by adding LEFT JOIN s and the appropriate columns to the COALESCE .请注意,通过将LEFT JOIN和适当的列添加到COALESCE ,可以将其扩展到所需的尽可能多的备份语言。

Demo (of both queries) on dbfiddle dbfiddle 上的演示(两个查询)

If you are running MySQL 8.0, I would recommend row_number() to handle the prioritization:如果您正在运行 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;

With this solution, you can manage as many levels of prioritization as wanted, by adding more conditions to the ORDER BY clause of ROW_NUMBER() .使用此解决方案,您可以通过向ROW_NUMBER()ORDER BY子句添加更多条件来管理任意多个级别的优先级。 Currently this puts the Italian translation first, followed by the English translation, followed by any other available translation (alphabetically-wise).目前,这将意大利语翻译放在首位,然后是英语翻译,然后是任何其他可用的翻译(按字母顺序)。

Your WHERE-condition changes the LEFT to an INNER join, resulting in no row returned when there's no no row for en .您的 WHERE 条件将 LEFT 更改为 INNER 连接,当en没有 no 行时,不会返回任何行。

The classic solution utilizes one left join perm language and then COALESCE to get the first non-NULL value:经典解决方案使用一个左连接 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;

While GMB's solution works without default language, this solution requires a default (the language with no missing translation), in your case probably de :-)虽然 GMB 的解决方案在没有默认语言的情况下工作,但此解决方案需要默认(没有缺少翻译的语言),在您的情况下可能是de :-)

Maybe GMB's query canbe improved by applying ROW_NUMBER before the join:也许可以通过在连接应用 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;

And this priorization logic logic can also be used with aggregation:而且这种优先逻辑逻辑也可以与聚合一起使用:

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

Thanks to @Nick answer I came up with new idea to use function FIELD() which would cover all possible languages without joining separate table for each language but with one subselect.感谢@Nick 的回答,我想出了使用 function FIELD()的新想法,这将涵盖所有可能的语言,而无需为每种语言加入单独的表格,但有一个子选择。 I am not sure about performance comparing to the other answers, but it would be faster if the languages are indexed by numerical values than strings ( en , it etc.).与其他答案相比,我不确定性能,但如果语言按数值索引而不是字符串( enit等),它会更快。

Of course translations with NULL should be excluded in subselect.当然,子选择中应排除带有 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