在什么情况SQL Server允许加入一个不依赖于连接表在所有条件中的任何原因?(Are there

2019-10-20 23:46发布

CREATE TABLE y (Id  INT NOT NULL PRIMARY KEY      );
CREATE TABLE x (YId INT NOT NULL REFERENCES y (Id));
DECLARE @YId INT = …;

如果我要选择的所有记录x ,其中YId等于@YId ,我可以给这三种不同的方法:

/* 1: */  SELECT x.* FROM x                              WHERE x.YId = @YId;
/* 2: */  SELECT x.* FROM x INNER JOIN y ON                    x.YId = @YId;
/* 3: */  SELECT x.* FROM x INNER JOIN y ON x.YId = y.Id WHERE y.Id  = @YId;

查询1显然是最直接的查询,以及一个具有最佳性能。 (请参阅执行计划下文。)查询3可能会比严格意义上更复杂,但也许也是一个相当普遍的解决方案。

令我惊讶的是查询2(其中,根据执行计划,得到就像查询3执行):它的INNER JOIN条款不依赖于y在所有!

问:为什么SQL服务器甚至接受这个看似荒谬的加盟条件是有效的? 有没有解释为什么任何具体原因和情况ON指连接表是不是必要条件?

执行计划:

Answer 1:

SQL Server支持它,因为JOIN语法(从MSDN)以下内容:

<joined_table> ::= 
{
    <table_source> <join_type> <table_source> ON <search_condition> 
    | <table_source> CROSS JOIN <table_source> 
    | left_table_source { CROSS | OUTER } APPLY right_table_source 
    | [ ( ] <joined_table> [ ) ] 
}
<join_type> ::= 
    [ { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } [ <join_hint> ] ]
    JOIN

其中<search_condition>如下:

指定在其基于的加盟条件。 此条件可指定任何谓词,虽然列和比较运算符是经常使用的。

当条件指定列的列不必具有相同的名称或相同的数据类型; 然而,如果数据类型是不一样的,他们必须是兼容或SQL Server可以隐式转换类型。 如果数据类型不能隐式转换,则条件必须明确使用CONVERT函数转换数据类型。

有可能是只涉及ON子句中的连接表的一个断言。 这样的谓词也可以在查询中的WHERE子句。 虽然这种谓词的放置不有所作为的内连接,它们可能在外部联接涉及导致不同的结果。 这是因为ON子句中的谓词之前应用于表的连接,而WHERE子句在语义上应用于联接结果。

有关搜索条件和谓词的更多信息,请参阅搜索条件(的Transact-SQL) 。

最重要的部分是强调了与大胆。

下面是一个例子:

SELECT
  U.UserName, P.PrivilegeName, P.Status
FROM
 dbo.Users U
LEFT JOIN dbo.Privileges P
  ON U.UserID = P.UserID AND P.PrivilegeName = 'EditComment'

此查询列出了所有用户,并增加了“EditComment”特权地位。 如果将条件WHERE子句,结果是不同的。 (只有拥有“EditComment”权限的用户将陆续上市。)

上面的查询将列出所有用户NULLStatusPrivilegeName在结果列,如果没有匹配的行。

同样的结果可以用一个子查询achived:

SELECT
  U.UserName, P.PrivilegeName, P.Status
FROM
 dbo.Users U
LEFT JOIN (SELECT UserID, Status FROM dbo.Privileges WHERE P.PrivilegeName = 'EditComment') P
  ON U.UserID = P.UserID

因此,这是某种形式的子查询方法的shortand的。

移动条件的WHERE子句

SELECT
  U.UserName, P.PrivilegeName, P.Status
FROM
 dbo.Users U
LEFT JOIN dbo.Privileges P
  ON U.UserID = P.UserID
WHERE
  P.PrivilegeName = 'EditComment'

如果条件是在WHERE子句中,结果将被过滤到具有mathing行中的权限表,该行的的行PrivilegeNameEditComment 。 这基本上是一个INNER JOIN 。 (过滤到在列WHERE这在子句LEFT JOIN βed表格滤波器所有NULL值从结果-除了如果有一个OR与条件IS NULL

执行计划

在执行计划的差别是因为的逻辑处理的SELECT查询。 FROM, ON, JOIN, WHERE, GROUP BY, WITH CUBE or WITH ROLLUP, HAVING, SELECT, DISTINCT, ORDER BY, TOP

JOIN之前被处理WHERE

更改INNER JOIN在查询中LEFT或任何OUTER加入和分析查询。 结果可能会有所不同。

编辑

第二个查询看起来像一个过滤交叉联接:

/* 4: */ SELECT x.* FROM x INNER JOIN y ON                    1=1;
/* 5: */ SELECT x.* FROM x CROSS JOIN y

当你在第四查询改变ON状态到原来的一个在第二个查询,即等于

/* 6: */ SELECT x.* FROM x CROSS JOIN y WHERE x.YId = @YId

为什么SQL服务器甚至接受这个看似荒谬的加盟条件是有效的?

因为它是有效的(它有不同的含义,但在语法上是有效的)

有没有解释为什么是指连接表不需要接通状态的任何具体原因或情况?

原因? 是的,它在语法上是有效的

现状:CROSS JOIN与例如过滤。 加入从表中的所有记录1行中的结果集(例如,从一个点在时间/日期/日历表中的当前日期的报告目的的记录)



Answer 2:

试图回答,不久,并重复@Pred答案

我认为答案是,恕我直言, 是的 。 它允许shorcut的子查询。

2 / SELECT x.* FROM x INNER JOIN y ON                    x.YId = @YId

在逻辑上等同于

select
    tx.*
from
    (
        select x.* from x where x.YId = @YId
    ) tx
    cross join y

这允许以减少X * Y探索的行数。

作为说明:

declare @ty table (Id int not null primary key)
declare @tx table (Id Char(1) not null primary key, yId int not null)

insert into @ty values (1) ,(2), (3)
insert into @tx values ('A', 1), ('B', 1), ('C', 2)

declare @YId int = 1

SELECT x.* FROM @tx x                             WHERE x.YId = @YId
SELECT x.* FROM @tx x INNER JOIN @ty y ON   x.yId = @YId
SELECT x.* FROM @tx x INNER JOIN @ty y ON x.YId = y.Id WHERE y.Id  = @YId

会给

A   1
B   1

A   1
A   1
A   1
B   1
B   1
B   1

A   1
B   1

是一个明显的可以得到同样的结果。

在另一端:为什么加入,如果你既不需要从连接表返回或测试?

是不是真正的问题,或重新问题:为何查询优化器是不是更聪明吗?



Answer 3:

在简单地说,答案是ISO标准SQL是一个奇怪的野兽。 微软只是按照标准的IT行业已经顽强地坚持到最后的三十年。



Answer 4:

SQL Server允许加入一个不依赖于连接表的所有条件

这对全交叉连接:

insert into y values(1),(2);
insert into x values(1),(2);

查询2的结果:

YID
1
1

查询1和3必须得到相同的结果,但SQL Server是没有足够的智慧来识别它。 这就是为什么DBA的生活。



文章来源: Are there any reasons why SQL Server allows JOIN conditions that don't depend on the joined table at all?