【问题标题】:One query for one join that uses first result in second (subquery) to get all rows一个连接的一个查询,它使用第二个(子查询)中的第一个结果来获取所有行
【发布时间】:2020-06-28 08:26:43
【问题描述】:

所以我一直在努力解决这个问题。虽然我确实可以针对这个问题进行两个单独的查询,但我想知道是否可以进行一个查询。我认为 SQL 专业人士肯定知道如何完成这项工作。所以事情是这样的:

我们有两个表 post 和 post_translations。为简单起见缩写。

posts
------
id

post_translations
-----------------
id
post_id (which is the FK on posts...)
locale
slug
content
... (and so on)

对我来说很清楚,我现在可以做一个非常简单的 INNER JOIN 来将所有帖子翻译成特定语言,如果你愿意,可以说“en”或“de”。所以我不会再打扰你了。

但由于该表还将包含子区域设置,例如 en(对于美国)、en-GB、en-AU、de(对于德国)、de-AT、de-CH .... 整个事情变成后面的场景有点复杂。

假设有些帖子仅翻译成“de”语言,也翻译成“de-AT”语言。该表将如下所示:

post_translations
id    post_id   locale    content
1        1       de         ...
2        1       de-AT      ...
3        2       de         ...

所以帖子 1 在“de”和“de-AT”中可用。帖子 2 仅适用于“de-AT”。

如果我想将所有帖子都放在“de”中,那很容易。我只是添加一个 WHERE locale = 'de' 我很好。但是,假设我想要 'de-AT' 中的所有帖子以及 'de' 中所有其他 未翻译 在 'de-AT' 中的帖子,所以我没有得到任何重复 - 我怎么能在一个查询中实现这一目标?如前所述,我可以在这里运行两个查询,首先我在'de-AT'中获取所有帖子,然后我在'de'中获取所有帖子并使用我从第一个查询中获得的'post_id's with a WHERE not IN查询,所以我没有得到任何重复。

这些查询将是:

SELECT 
    pt.post_id
FROM
    posts AS p
        INNER JOIN
    post_translations AS pt ON p.id = pt.post_id
WHERE
    pt.locale = 'de-AT';

从这个查询中,您将在这个查询中使用 post_id:

SELECT 
    *
FROM
    posts AS p
        INNER JOIN
    post_translations AS pt ON p.id = pt.post_id
WHERE
    pt.locale = 'de' AND pt.post_id NOT IN (*post_ids found in first search*);

因此,使用上述 post_translations 表,期望的结果将是:

p.id     pt.id   pt.post_id pt.locale pt.content
    1      2        1         de-AT      ...
    2      3        2         de         ...

显然,p 代表帖子,pt 代表 post_translations。

查询背后的想法是显示某个地区的特定帖子,在本例中为“de-AT”,但也显示为“de”用户编写的通用帖子。

我希望这是有道理的。将不胜感激任何帮助。谢谢。

【问题讨论】:

  • 嗨草莓,感谢您的提醒,为了更好地遵守该帖子,我想知道我应该添加什么。如果单独使用会起作用的 SQL 查询?
  • 这不是破坏了提供链接的意义吗?
  • 嗯,我发现它们很基本,我认为你是对的,因为也许其他人可能会觉得它们很有价值,我将添加它们。谢谢!
  • 发布样本数据和预期结果以阐明您的需求。
  • 请在代码问题中给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。给出尽可能少的代码,即您显示的代码可以通过您显示的代码扩展为不正常的代码。 (调试基础。)对于包含 DBMS 和 DDL(包括约束和索引)和输入为格式化为表的代码的 SQL。 How to Ask 暂停整体目标的工作,将代码砍到第一个表达式,没有给出你期望的内容,说出你的期望和原因。

标签: mysql join select subquery


【解决方案1】:

一个选项使用not exists

select pt.*
from post_translations pt
where 
    locale = 'de-AT'
    or (
        locale = 'de'
        and not exists (
            select 1 
            from post_translations pt1
            where pt1.post_id = pt.post_id and pt1.locale = 'de-AT'
        )
    )
    

或者,如果你运行的是 MySQL 8.0,你也可以使用row_number()

select *
from (
    select 
        pt.*, 
        row_number() over(partition by post_id order by (locale = 'de')) rn
    from post_translations pt
    where locale in ('de', 'de-AT')
) pt
where rn = 1

您可以轻松地将上述查询修改为joinposts 表。

【讨论】:

  • 哇,很快,感谢,我基本上将第一个查询(mysql
【解决方案2】:

显示某个地区的特定帖子,在本例中为“de-AT”,但也可显示 显示为“de”用户编写的通用帖子

我相信带有运算符IN 的简单WHERE 子句会返回您预期的结果。
然后您可以使用条件聚合来标记具有您想要的子语言环境的帖子,并可能首先对这些帖子进行排序:

SELECT post_id, 
       MAX(locale = 'de-AT') AS flag
FROM post_translations 
WHERE locale IN ('de', 'de-AT')
GROUP BY post_id
ORDER BY flag DESC

您可以将上述查询加入posts以获取每个帖子的详细信息:

SELECT p.*, t.flag
FROM posts AS p INNER JOIN (
    SELECT post_id, 
           MAX(locale = 'de-AT') AS flag
    FROM post_translations 
    WHERE locale IN ('de', 'de-AT')
    GROUP BY post_id
) t ON t.post_id = p.id
ORDER BY t.flag DESC

【讨论】:

    猜你喜欢
    • 2013-11-01
    • 2015-03-16
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 2013-03-01
    相关资源
    最近更新 更多