MariaDb - 仅当存在单个右侧记录时如何确保(左)连接

Mar*_*tin 8 mariadb join

我有两张桌子需要连接在一起。但是,我只想在存在独特匹配时连接记录,而不是选择多个匹配之一进行连接。

版本控制:

使用 MariaDB 版本 10.3.34

示例数据:

核心(左)数据towns

ID 邮政编码
1 哈德菲尔德 HD11 4ER
2 曼彻斯特 MN14 3JE
3 麦克尔斯菲尔德 MK17 9FL
4 爱丁堡 ED5 3MJ
5 利物浦 LV9 8XT

连接(右)数据peoples

ID 名字 邮政编码
1 吉米·萨维尔 HD11 4ER
2 贾森·邦姆 IP14 8FK
3 米奇老鼠 MK17 9FL
4 鲍比·迪利安 ED5 3MJ
5 莱尼·戴维斯 ED5 3MJ

我的SQL:

我最初的查询是这样的:

SELECT towns.id, towns.town, peoples.name FROM towns 
       LEFT JOIN people ON towns.postcode = peoples.postcode
Run Code Online (Sandbox Code Playgroud)

但这将包括Edinburgh但爱丁堡有两个人,我只想在有一个独特的排可以加入时加入。

我使用 LEFT join 因为我需要返回 的所有行,towns但仅返回唯一行peoples

预期成绩:

ID 名字
1 哈德菲尔德 吉米·萨维尔
2 曼彻斯特 <空>
3 麦克尔斯菲尔德 米奇老鼠
4 爱丁堡 <空>
5 利物浦 <空>

我尝试过的

我尝试过COUNT()在 JOIN 中使用但无法让它工作,

SELECT towns.id, towns.town, peoples.names FROM towns 
       LEFT JOIN people ON towns.postcode = peoples.postcode AND count(peoples.id) = 1 
Run Code Online (Sandbox Code Playgroud)

出现语法错误。

我无法考虑如何限定此连接,它仅在找到单个结果时才连接。互联网搜索给了我很多更加模糊和偏离主题的参考资料。

我确信这很简单,但我做不到。另外,如果可能的话,我想避免子查询?


数据库小提琴

J.D*_*.D. 6

Lennart 窗口函数答案的另一种方法是仅对表使用GROUP BYandHAVING子句peoples来过滤掉具有相同内容的子句,postcode如下所示:

SELECT towns.id, towns.town, peoples.names
FROM towns
LEFT JOIN
(
    SELECT MAX(names) AS names, postcode
    FROM peoples
    GROUP BY postcode
    HAVING COUNT(name) = 1
) peoples
ON towns.postcode = peoples.postcode
Run Code Online (Sandbox Code Playgroud)

  • 在 MySQL 中,它由“ONLY_FULL_GROUP_BY” SQL 模式控制。在 5.7 之前,此功能默认关闭,此后默认开启。@SalmanA 任何理智的 RDBM 都应该需要它,除非 `names` 在功能上依赖于 `postcode`,但它不在这里。虽然当您专门限制 count=1 时确实是多余的,但我认为数据库不需要检测这种特殊情况。 (2认同)

Len*_*art 5

您应该能够通过以下方式加入人们:

(select name, postcode
from (
  select name, postcode, count(1) over (partition by postcode) as cnt
  from peoples
) as t
where cnt = 1)
Run Code Online (Sandbox Code Playgroud)

IE

SELECT t.id, t.town, p.name 
FROM towns t
LEFT JOIN (SELECT name, postcode
           FROM (
               SELECT name, postcode
                    , count(1) over (partition by postcode) as cnt
               FROM peoples
           ) as x
           WHERE cnt = 1
) p
    USING (postcode)
Run Code Online (Sandbox Code Playgroud)

编辑:

鉴于更新中提供的 ddl,我创建了db<>fiddle

SELECT t.id, t.town, p.names 
FROM towns t
LEFT JOIN (
    SELECT names, postcode
    FROM (
       SELECT names, postcode
            , count(1) over (partition by postcode) as cnt
       FROM peoples
    ) as x
    WHERE cnt = 1
) p
    USING (postcode);
Run Code Online (Sandbox Code Playgroud)

它似乎给出了预期的结果


Ric*_*mes 5

更简单:

SELECT t.id, t.town, 
       IF (COUNT(DISTINCT p.names) = 1, MAX(p.names), NULL) AS names
    FROM towns AS t
    LEFT JOIN peoples AS p  ON t.postcode = p.postcode 
    GROUP BY t.id
Run Code Online (Sandbox Code Playgroud)

我相信它避免了之前评论中提到的“唯一完整的分组依据”问题。(如果没有,请参阅此答案的评论。)