Postgres多表连接

68

这是一个Postgres数据库。我试图从breed表中提取狗品种名称(如Cane Corso、Labrador等),以根据位于animal表中的外键进行显示。我的问题是animal表有两个外键指向这个单一的breed表,而且我的查询一直出现错误。第一个breed名称将基于左连接返回,但由于已经有一个左连接,我无法获取第二个名称。以下是我试图执行的简化概述:

breed table (ID, BreedName)
animal table (ID, breedID, breed2ID)

SELECT animal.ID, breed.BreedName FROM animal LEFT JOIN breed ON animal.breedID=breed.ID WHERE animal.ID='7';

我需要做的是还要获取BreedName以供animal.breed2ID使用,但我失败了。我可以轻松地硬编码品种名称并在应用程序中显示,但这样不利于数据库中品种名称的更改、添加或删除。

3个回答

130

在同一张表上执行另一个连接操作:

SELECT animal.ID, breed1.BreedName as BreedName1, breed2.BreadName as BreadName2 
FROM animal 
   LEFT JOIN breed as breed1 ON animal.breedID=breed1.ID 
   LEFT JOIN breed as breed2 ON animal.breedID=breed2.ID 
WHERE animal.ID='7';

1
非常感谢。这完全澄清了我的问题,并且按计划正常工作!我之前一直在弄breed.AnimalName AS breedName1,breed.AnimalName AS breedName2,结果搞砸了。完全忽略了表的别名!再次感谢,这是我在技术网站上看到的最好的帮助之一! - user1137376
不用客气,这就是我们在这里的原因:D 我也同意StarShip3000的帖子。但是我假设有三个或更多品种的狗不会出现。但如果确实发生了,那么一定要采取Star建议的推荐方法。 - kingdaemon
确保使用"explain analyze"来确保您无法从自然连接中获得更好的速度。您会感到惊讶的。 - Andrew Scott Evans

25

虽然Ivan已经为您解决了当前数据库设计的问题,但从长远考虑或者作为学习,使您的表格设计更加标准化是很有必要的。这样每次想要为动物添加品种时都不需要添加新的连接。通过这种简单的设计,实际上让您的生活变得更加复杂。

当您在一个实体中看到重复的属性类型时,比如breedID和breed2ID,在非常少数的情况下,你应该开始察觉到其中的问题。

在理想的设计中,您应该按照以下步骤进行:

1. 保留breed表的内容

2. animal表设计为 animal(animal_id,animal_name) 的形式

3. 添加一个新的animal_breed表,它看起来像是这样animal_breed(animal_breed_id,animal_id,breed_id),其中animal_bread_id是主键,(animal_id,breed_id)上有唯一键,并且分别指向相应的表的外键。

这种设计允许一个动物拥有一个或多个品种类型,而无需修改查询以返回多个品种。而您目前的设计每次有一个动物拥有额外的品种时都变成了噩梦。它可能会影响性能,使维护成为噩梦,而且不是一个合理的数据库设计。


在哪些罕见情况下,所讨论的模式设计是正确的选择? - Jon Vogel
根据我的经验,如果你遇到了某种性能瓶颈,或者你有足够的经验知道你正在处理的用例需要这种类型的设计来处理你所期望的处理和数据量,那么就需要这样做。 - Kuberchaun

1
为了避免多次连接操作,可以使用单个连接同时满足多个条件。
SELECT a.ID, b.ID, b.BreedName
FROM animal a
JOIN breed b
ON b.ID = a.breedID OR b.ID = a.breed2ID
WHERE a.ID='7';

为什么要使用JOIN而不是LEFT JOIN?
假设您不需要从动物表中获取所有记录(无论是否匹配),您可以使用默认的(内部)连接而不是左连接,仅检索匹配的行。
还有其他解决方案吗?
使用IN而不是OR:JOIN breed ON b.id IN (a.breedID, a.breed2ID) - 考虑到只有2个ID,可能可以忽略不计,但您需要检查执行计划以确定哪个更快。
还有其他注意事项吗?
由于这两个外键(breedID和breed2ID)没有约束条件,可能会出现重复的品种结果。如果您想要唯一的品种记录,可以使用GROUP BY或DISTINCT。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接