SQL 中的加入、条件和速度

Joins, conditions and speed in SQL

在准备一些请求时,我正在写这个:

SELECT *
FROM ta A
JOIN tb B
    ON A.col1 = B.col1 
JOIN tc C
    ON B.col2 = C.col2 
WHERE B.col3 = 'whatever'
AND C.col4 = 'whatever2'

然后我开始思考以下问题:

SELECT *
FROM ta A
JOIN (SELECT * FROM tb WHERE col3 = 'whatever') B
    ON A.col1 = B.col1
JOIN (SELECT * FROM tc WHERE col4 = 'whatever2') C
    ON B.col2 = C.col2

(如果我没记错的话,结果是一样的)。我想知道它是否会明显更快?我的猜测是它会,但我有兴趣知道 why/why 不是?

(因为我们的服务器暂时宕机,我现在不能自己测试,所以在这里问一下,希望大家不要介意。)

(以防万一,引擎是 Vertica,但我的问题并不是真正针对 Vertica)

您的第一个查询可以正常工作,但第二个查询将不会执行并导致错误。其背后的原因是,您正在服用 JOIN (SELECT * FROM tb WHERE B.col3 = 'whatever') B ON A.col1 = B.col1.

在这种情况下,您正在将列与 A.col1 = B.col1 匹配。这里你会从tatable得到A.col1,但是你不会得到B.col1。在连接中指定子查询时,不应使用 ' * ' 运算符。联接不会在子查询中识别此运算符。您需要指定所需的列名。就像下面查询中的示例一样,

SELECT *
FROM ta A
JOIN (SELECT col1,col2 FROM tb WHERE B.col3 = 'whatever') B
    ON A.col1 = B.col1
JOIN (SELECT col2 FROM tc WHERE C.col4 = 'whatever2') C
    ON B.col2 = C.col2

这将执行并为您提供结果。在第一个连接子查询 col1,col2 中采用了两列,因为您使用的条件 B.col2 来自 B table在第二个连接条件下。在 select 子句中,您可以提供 ' * ' 运算符,它为您提供来自所有三个 table 的所有列。但是您不应该在连接的子查询中使用运算符,因为连接是以这种方式编码的。

这两个查询没有太大区别,但是您的第一个逻辑比第二个逻辑执行得更快。在第二个逻辑中,使用了两个子查询,在数据库中进行多次搜索,并为您提供比第一个逻辑慢一点的结果。

你的第二个查询有点不对,应该是:

SELECT *
FROM ta A
JOIN (SELECT * FROM tb WHERE tb.col3 = 'whatever') B
    ON A.col1 = B.col1
JOIN (SELECT * FROM tc WHERE tc.col4 = 'whatever2') C
    ON B.col2 = C.col2

注意内联视图,其中子句需要在范围内引用 table,而不是视图的别名。 B 和 C 超出了内联视图的范围。

在任何情况下,因为您正在进行内部联接,所以从结果的角度来看并不重要,因为无论是预联接还是 post-联接,条件都是相同的。

您可以合理地依赖优化器来执行以下操作:

  1. 仅在需要时具体化所需的列。
  2. 将谓词下推到有意义的地方

也就是说,这两种说法应该没有区别。很可能它正在下推第一个谓词以使其更像第二个谓词。如果您收集了统计数据,优化器应该足够聪明以相同的方式(或非常接近)查询这些数据。

这并不是说我没有看到你在你的第二个查询中有什么 "fix" 我在 Vertica 中的查询问题......但通常只有当我使用多个 COUNT(DISTINCT ...) 表达式或 theta 连接等

现在,如果这是一个外连接,那么语句就会不同。第一个将在连接之后应用过滤器,第二个将在连接之前应用过滤器。

当然,我会提到您真的只需要对这两种方法进行解释。只需确保收集到统计信息即可。

希望对您有所帮助。