其中(隐式内部联接)与显式内部联接-是否影响索引



对于查询

SELECT * from table_a, b WHERE table_a.id = b.id AND table_a.status ='success'  

SELECT * from a WHERE table_a.status ='success' JOIN b ON table_a.id = b.id

不知怎的,我倾向于在table_a上为顶部表单创建一个索引(id,status)
而我对底部形式的自然倾向是创建两个独立的索引,表a上的id和状态。

这两个查询实际上是相同的,对吧?你会用同样的方式索引两者吗
如何索引tablea(假设这是系统中唯一存在的查询,以避免其他考虑因素)?一个或两个索引?

"传统样式"和SQL 92样式的内部联接在语义上是等效的,大多数DBMS都会对它们一视同仁(例如Oracle)。他们将对两种形式使用相同的执行计划(尽管如此,这取决于实现,并且没有任何标准保证)。

因此,索引在两种形式中的使用方式也是相同的。

与您使用的语法无关,适当的索引策略取决于实现:一些DBMS(如Postgres)通常更喜欢单列索引,并且可以非常有效地组合它们,而另一些DBMS(例如Oracle)则可以从组合(甚至覆盖)索引中获得更多优势(当然,这两种形式都适用于这两种DBMS)。

关于示例的语法,第二个WHERE子句的位置让我有点惊讶。

以下两个查询在大多数DBMS中以相同的方式处理:

SELECT * FROM table_a, b WHERE table_a.id = b.id AND table_a.status ='success'  

SELECT * FROM a JOIN b ON table_a.id = b.id WHERE table_a.status ='success' 

但是,您的第二个查询在FROM子句中移动了WHERE子句,在我看来,这不是有效的SQL。

的快速检查

SELECT * from a WHERE table_a.status ='success' JOIN b ON table_a.id = b.id

确认:MySQL 5.5、Postgres 9.3和Oracle 11g都会产生语法错误。

应该优化这两个查询以执行相同的方式;但是,联接语法是符合ANSI的,应该弃用旧版本。就索引的使用而言,您只想接触一次表(索引)。您正在使用的RDBMS和表格设计将决定是否需要在覆盖索引中包括PRIMARY KEY(假设这是您的示例中ID所代表的)的具体内容。此外,SELECT*可能包含也可能不包含;最好使用特定的列名。

您排除了其他查询,但仍然存在一些悬而未决的问题:尤其是关于数据分布的问题。例如,如何将table_a.status="成功"的行数与table_b的表大小进行比较?根据优化器的估计,必须做出两个重要的决定:

  1. 要使用的联接算法(嵌套循环;哈希或排序/合并)
  2. 处理表格的顺序是什么

不幸的是,这些决定会影响索引(并且会受到索引的影响!)

示例:假设只有一行WHERE table_a.status="成功"。如果在table_a.status上有一个索引就可以快速找到那一行。接下来,我们希望在table_b.id上有一个索引,以便使用嵌套循环联接快速查找相应的行。考虑到您选择了*,在这些索引中包含额外的列是没有意义的(不考虑系统中的任何其他查询)。

但现在想象一下,您在table_a.status上没有索引,而是在table_a.id上,并且与table_b相比,这个表是巨大的。为了进行演示,我们假设table_b只有一行(当然是极端情况)。现在,最好转到table_b,获取所有行(仅一行),而不是使用索引从table_a中获取相应的行。您看到索引是如何影响联接顺序的吗?(对于本例中的嵌套循环连接)

这只是事物如何相互作用的一个简单例子。大多数数据库都有三种连接算法可供选择(MySQL除外)。

如果您创建了上述三个索引,并查看数据库执行联接的方式(解释计划),您会注意到其中一个或两个索引对于为查询选择的特定联接算法/联接顺序仍然未使用。理论上,你可以去掉这些指数。但是,请记住,优化器是根据可用的统计数据做出决定的,优化器的估计可能是错误的。

你可以在我的网站上找到更多关于索引联接的信息:http://use-the-index-luke.com/sql/join

最新更新