[英]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
,可以将其扩展到所需的尽可能多的备份语言。
如果您正在运行 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()
的新想法,这将涵盖所有可能的语言,而无需为每种语言加入单独的表格,但有一个子选择。 与其他答案相比,我不确定性能,但如果语言按数值索引而不是字符串( en
, it
等),它会更快。
当然,子选择中应排除带有 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.