有趣的多对多sql连接

Posted

技术标签:

【中文标题】有趣的多对多sql连接【英文标题】:Interesting Many-many sql join 【发布时间】:2010-09-18 06:10:18 【问题描述】:

我有三个相关的表“A(id, val)”、“B(id, val)”和一个值为“AB(aid, bid, val)”的链接表

我正在查询 B 以带回 A 值,例如:

SELECT A.* 
FROM A INNER JOIN AB ON A.id = AB.aid INNER JOIN B ON AB.bid = B.id
WHERE B.val = 'foo';

每个 A 都有很多 B,每个 B 都有很多 A。

我要崩溃的问题是需要过滤集合,以便查询仅在 AB.val 是任何给定 A/B 对的最大值时才返回行

例如如果我有数据:

一个

id   val
1    something
2    somethingelse

B

id   val
1    foo
2    bar

AB

aid  bid  val
1    1    3
1    2    2
2    1    1
2    2    4

我只想选择 AB 的第一行和最后一行,因为它们是每个 A 的最大值,然后能够查询 B.val = 'foo' 以仅返回第一行。我不知道如何仅限制 AB 表中的 max val 行。

我能得到的最好的是

SELECT * 
FROM A 
INNER JOIN 
  (SELECT aid, bid, MAX(val) AS val FROM AB GROUP BY aid) as AB
  ON A.id = AB.aid 
INNER JOIN B ON AB.id = B.id
WHERE B.val = 'foo'

但这并不完全奏效。首先,它只是感觉是错误的方法,其次,它返回了错误的投标值。也就是说,从子查询返回的出价不一定与 max(val) 来自同一行。我相信这是一个已知的分组问题,当未为排序规则或分组指定列时选择返回的值是未定义的。

我希望上面的一些内容是有道理的,在过去的几个小时里,我一直在为此苦苦挣扎,任何帮助都将不胜感激。谢谢。

(对于那些想知道的人,它的实际用途是用于字典后端,其中 A 是单词表,B 是音素表。AB 是带有“位置”列的词素表。查询是查找所有单词以指定的音素结尾。(音素是单词的声音,用法类似于国际音标)

【问题讨论】:

【参考方案1】:

我认为您需要先进行另一个联接才能获得每个 a.id 的 ab 的最大 val。

类似这样的:

select a.*
from a
left join (
    select aid, max(val) as val 
    from ab 
    group by aid
) abmax on abmax.aid=a.id
inner join ab on ab.aid=abmax.aid and ab.val=abmax.val
inner join b on b.id=ab.bid
where b.val='foo'

【讨论】:

【参考方案2】:

这是我刚刚测试的另一种方法:

select a.*
from ab
   inner join b on(ab.bid=b.id)
   inner join a on (ab.aid=a.id)
where ab.val = (select max(val) from ab AS ab2 where ab2.aid = ab.aid)
   and b.val='foo'

【讨论】:

【参考方案3】:

我经常使用以下技巧来获得最大的每组:

SELECT a.*
FROM ab AS ab1
  LEFT OUTER JOIN ab AS ab2 ON (ab1.aid = ab2.aid AND ab1.val < ab2.val)
  JOIN a ON (ab1.aid = a.id)
  JOIN b ON (ab1.bid = b.id)
WHERE ab2.aid IS NULL
  AND b.val = 'foo';

诀窍是在外部连接中将 AB 表连接到自身。返回 ab1,其中不存在具有相同的辅助值和更大的值的行。因此,对于给定的辅助值,ab1 的每组行的 val 最大。

【讨论】:

当 ab 中有多个行且具有相同的帮助时,这种连接可能会变得非常昂贵。如果 ab 中只有 5 行相同,则这 5 行将在过滤之前产生 10 个结果行。六个将产生 16 行,依此类推。使用 max() 效率更高。 是的,不同的技术适用于不同的情况。【参考方案4】:

我不确定您使用的是哪个 sql,但在 MS SQL 中,我创建了一个表值数据库函数来返回表 A 中的最大值,然后将其连接到表 B。我发现这比复杂连接更容易理解稍后我会回顾我的查询。

【讨论】:

【参考方案5】:
SELECT *
FROM
(
  SELECT
    A.*,
    (SELECT top 1 AB.BID FROM AB WHERE A.AID = AB.AID ORDER BY AB.val desc) as BID
  FROM A
) as Aplus
JOIN B ON Aplus.BID = B.BID

【讨论】:

以上是关于有趣的多对多sql连接的主要内容,如果未能解决你的问题,请参考以下文章

多对多连接

SQL Server Analysis Services 中的多对多关系;第二个多对多关系不起作用

从 Prisma 中的多对多表中获取元信息

没有连接表的多对多映射

JPQL 基于条件的多对多连接

使用 SQL 和 Linq 的多对多关系(实体框架/实体)