【问题标题】:Non-equivalence joins非等价连接
【发布时间】:2011-07-21 21:24:03
【问题描述】:

我已经做 SQL 好几年了,在这段时间里,我对连接的想法是等价连接,如

select ... from t1 join t2 on t1.a = t2.b

注意连接是如何基于一个或多个等式的,这里是t1.a = t2.b。不过最近不记得在哪里看到了一个不等价的join(这个词是我自己编的,如果有真名请告诉我),其中join条件至少包含一个不等式,如

select ... from t1 join t2 on t1.a > t2.b

这可以做一些好事,尤其是外连接。让我用一个例子来说明这一点。

让我们考虑一个名为 products 的表,其中包含以下数据:

product   year  price
----------------------
apple   2009    4
apple   2008    2
apple   2007    5
apple   2006    6
apple   2005    2
banana  2009    9
banana  2008    12
banana  2007    16
banana  2006    15
banana  2005    10

我们想做通常的“给我每个产品最昂贵的一年”,据我所知,这通常是通过对按产品分组的同一个表进行内部连接来完成的,如下所示:

select t1.`name`, t1.`year`, t1.`price`
from products as t1 join
( select `name`, max(`price`) as `max_price` from products group by `name` ) as t2 on t1.`name`=t2.`name` and t1.`price`=t2.`max_price`

所以,在 t2 上,我们得到了每种产品的最高价格,然后我们将这个结果与同一个表连接起来,以获取该列的其余数据(对于平局来说有点棘手)

但是,对于非等价外连接,我们可以这样做:

select t1.`name`, t1.`year`, t1.`price`
from products as t1 left join products as t2 on t1.`name`=t2.`name` and t1.`price` < t2.`price`
where t2.`name` is null

这一次,我们两次加入同一张桌子,其中 t1 的价格低于 t2 的价格。这里的技巧是,因为这是一个左外连接,所以当连接不匹配时,结果连接上的 t2 值将为空,这发生在价格的最大值。

这两个查询产生相同的结果,但我不确定哪一个的性能更好。第一个查询具有昂贵的分组,而第二个查询必须手动检查所有 t1/t2 对以获得结果。不过,使用非等价连接似乎更容易打破平局。

所以,我的问题是:

是否有任何推荐的资源(书籍、网页)更深入地讨论非对等连接,解释你可以用它们做什么(我假设你可以做的不仅仅是在组中获得最大值),以及与其他方法做同样的事情相比,它们的表现如何?

编辑:我知道窗口函数也可用于执行我上面提到的简单示例。我不是在问如何获得表格的最大值。我知道如何做到这一点,我什至提供了两种方法来做到这一点。我想知道我还能用非等价连接做什么。

【问题讨论】:

  • 通常编写这些查询的最佳方式是使用分析/窗口函数。
  • @ypercube 谢谢你的名字。很棒的评论。
  • 不确定:一个你可以在连接上使用 b-tree 索引,另一个你不能。如果表格足够小,或者您需要访问每一行,这无关紧要。
  • @Oscar:想想这个t1.a &gt; t2.b(将被编入索引)。如果值之间有数千个可能的值,那么使用索引是没有用的,因为没有足够的选择性。如果您采用 max(t1.a) 那么您可以使用顶部值的索引扫描。理论上,两者都应该生成相同的底层查询计划,因为 SQL 是逻辑的而不是过程的,但如果他们这样做了,我会感到惊讶。 explainextended.com 有大量关于此类 API 级别以下内容的文章。

标签: mysql sql sql-server oracle postgresql


【解决方案1】:

我在这里 SQL Query Creating Start and End Dates 和这里 SQL Server logical grouping most recent time 做了类似的事情。

这种连接与“平等”连接没有什么不同。 on 子句只是一个逻辑评估。如果存在索引,则将其评估为索引上的 where 子句。

其他的,这取决于每个人的想象力。 SQL 很丰富,所以可以组合。

【讨论】:

  • 当然,SQL 引擎并不真正关心连接表达式中的内容,只要它可以计算为布尔值即可。但我不得不说,用外连接来做这件事,然后检查结果是否为空是非常巧妙的,我不得不同意我一开始就不会想出它。你的例子很好。我想知道你是否知道我在哪里可以找到更多关于这种连接的示例。
  • 我不知道在哪里可以找到示例。我向您展示的只是我对其他用户提出的一些问题的一些解决方案。其实我从来没有给自己提出过你提出的这个问题,我只是以这种方式编写了 sql 并且它起作用了。
【解决方案2】:

我们想做通常的“给我每个产品最昂贵的一年”,据我所知,这通常是通过内部连接到按产品分组的同一个表来完成的,就像这样

使用窗口函数更容易完成(正如 jonealres 已经提到的)

select name, 
       year, 
       price,
       max(price) over (partition by name) as most_expensive
from products

【讨论】:

  • 是的,不幸的是 MySQL 不支持窗口函数,这意味着我的 sql 不能移植。我的问题不是“如何做最昂贵的产品?”但是“我还能用非等价连接做什么?”
  • @Oscar Rodriguez:嗯,你列出的 4 个 DBMS 中有 3 个支持它。 “可移植 SQL”是一个神话,只是意味着它在每个 DBMS 上运行同样缓慢......
  • @Oscar Rodriguez:你能做什么取决于你,正如我之前所说,这取决于想象力。如果是用户权限,这种连接与等式连接具有相同的性能。
  • 是的,但 MySQL 没有。我的问题更多是关于 SQL 的理论方面。我找到了一个名为“non-equivalence join”的工具,我想知道我还能用它做什么。这就是我的问题。
猜你喜欢
  • 2018-03-10
  • 1970-01-01
  • 1970-01-01
  • 2015-09-24
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-15
  • 2021-11-08
相关资源
最近更新 更多