ON子句的位置实际上意味着什么?


23

普通JOIN ... ON ...语法是众所周知的。但是也可以将ON子句JOIN与其对应的子句分开放置。这是在实践中很少见到的东西,在教程中找不到,而且我还没有找到任何 Web资源甚至提到这是可能的。

这是一个可以使用的脚本:

SELECT *
INTO #widgets1
FROM (VALUES (1), (2), (3)) x(WidgetID)


SELECT *
INTO #widgets2
FROM (VALUES (1, 'SomeValue1'), (2, 'SomeValue2'), (3, 'SomeValue3')) x(WidgetID, SomeValue)

SELECT *
INTO #widgetProperties
FROM (VALUES
    (1, 'a'), (1, 'b'),
    (2, 'a'), (2, 'b'))
x(WidgetID, PropertyName)


--q1
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 ON w2.WidgetID = w1.WidgetID
LEFT JOIN #widgetProperties wp ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
ORDER BY w1.WidgetID


--q2
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 --no ON clause here
JOIN #widgetProperties wp
 ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
 ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID


--q3
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN (
    #widgets2 w2 --no SELECT or FROM here
    JOIN #widgetProperties wp
    ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b')
ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID

q1看起来很正常。q2和q3在ON子句中具有这些异常的位置。

该脚本不一定有意义。我很难构想出有意义的场景。

那么这些异常的语法模式是什么意思呢?如何定义?我注意到,并非两个ON子句的所有位置和顺序都被允许。有什么规则可以管理?

编写这样的查询也是一个好主意吗?

Answers:


32

如果查看FROM子句语法图,您会发现该ON子句只有一个地方:

<joined_table> ::= 
{
    <table_source> <join_type> <table_source> ON <search_condition> 
    ...
}

您发现令人困惑的是简单的递归,因为<table_source><joined_table上面的>中可以是另一个<joined_table>:

[ FROM { <table_source> } [ ,...n ] ] 
<table_source> ::= 
{
    table_or_view_name ... 
    ...
    | <joined_table> 
    ...
}

为避免混淆,在非显而易见的情况下(例如您的示例),应使用括号在外观上分开<table_sources>;它们对于查询解析器不是必需的,但对人类有用。


33

它确定参与连接的逻辑表。

一个简单的例子

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets1 w1
       LEFT JOIN #widgets2 w2
         ON w2.WidgetID = w1.WidgetID
       JOIN #widgetProperties wp
         ON w2.WidgetID = wp.WidgetID
            AND wp.PropertyName = 'b'
ORDER  BY w1.WidgetID 

#widgets1留给外部连接#widgets2-的结果形成一个内部连接到的虚拟表#widgetProperties。该谓词w2.WidgetID = wp.WidgetID将意味着将过滤掉初始外部联接中所有空扩展行,从而有效地使所有联接成为内部联接。

这不同于q2 ...

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets1 w1
       LEFT JOIN #widgets2 w2 --no ON clause here
                 JOIN #widgetProperties wp
                   ON w2.WidgetID = wp.WidgetID
                      AND wp.PropertyName = 'b'
         ON w2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID

#widgets2内部连接到#widgetProperties。然后,该联接产生的虚拟表就是“左外部联接”中的右表。#widgets1

通过使用派生表或公用表表达式可以实现相同的结果。

WITH VT2
     AS (SELECT w2.WidgetID,
                w2.SomeValue,
                wp.PropertyName
         FROM   #widgets2 w2 
                JOIN #widgetProperties wp
                  ON w2.WidgetID = wp.WidgetID
                     AND wp.PropertyName = 'b')
SELECT w1.WidgetID,
       VT2.SomeValue,
       VT2.PropertyName
FROM   #widgets1 w1
       LEFT JOIN VT2
         ON VT2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID 

...或者,您也可以重新排序虚拟表并使用a RIGHT JOIN

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets2 w2
       INNER JOIN #widgetProperties wp
               ON w2.WidgetID = wp.WidgetID
                  AND wp.PropertyName = 'b'
       RIGHT JOIN #widgets1 w1
               ON w2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID 

由Itzik Ben Gan在这里报道

... JOIN条件必须遵循与表顺序的chiastic关系。也就是说,如果按此顺序指定表T1,T2,T3和T4,并且JOIN条件将T1与T2,T2与T3和T3与T4匹配,则必须按与表顺序相反的顺序指定JOIN条件。 , 像这样:

FROM   T1
       <join_type> T2 T2
                  <join_type> T3 T3
                             <join_type> T4
                               ON T4.key = T3.key
                    ON T3.key = T2.key
         ON T2.key = T1.key 

为了以不同的方式看待这种联接技术,给定的JOIN条件只能引用其上方的表名或早先已引用并已解析的JOIN条件的表名。

但文章有许多错误之处,请参见Lubor Kollar后续信函


谢谢马丁,这个答案非常有帮助。不过,我会接受另一个,因为他对形式语法的观点是帮助我完全理解这个问题的。特别是,“夫妻关系”似乎是一个错误的主意。它是一棵树,而不是列表加反向列表。mustaccio提供了理解Itziks解释为什么不太正确的框架。
boot4life
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.