Що насправді означає положення ON?


23

Нормальний JOIN ... ON ...синтаксис добре відомий. Але також можна розташувати ONпункт окремо від того, JOINщо йому відповідає. Це те, що рідко зустрічається на практиці, не зустрічається в навчальних посібниках, і я не знайшов жодного веб-ресурсу, який би навіть згадував, що це можливо.

Ось сценарій, з яким можна пограти:

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статей дозволені. Які правила регулюють це?

Також коли-небудь корисно написати такі запити?

Відповіді:


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 

... Або ви можете повторно замовити віртуальні таблиці та використовувати 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 

Це висвітлює тут Іцік Бен Ган

... Умови ПРИЄДНАННЯ повинні відповідати чітким відносинам до порядку столу. Тобто, якщо ви вказуєте таблиці 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.

але стаття має ряд неточностей, див. також подальший лист Любора Коллара .


Дякую Мартине, ця відповідь дуже корисна. Я погоджусь з іншим, адже його пункт про формальну граматику був тим, що допомогло мені повністю зрозуміти проблему. Зокрема, "хиастичні стосунки" здаються помилковою ідеєю. Це дерево, а не список плюс перевернутий список. Мустаччо надав основу для розуміння того, чому інтерпретація Іціка не зовсім правильна.
boot4life
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.