A sintaxe normal JOIN ... ON ...
é bem conhecida. Mas também é possível posicionar a ON
cláusula separada daquela a JOIN
que corresponde. Isso é algo que raramente é visto na prática, não é encontrado em tutoriais e não encontrei nenhum recurso da web que sequer mencione que isso é possível.
Aqui está um script para brincar:
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 parece normal. q2 e q3 têm esses posicionamentos incomuns da ON
cláusula.
Este script não necessariamente faz muito sentido. Foi difícil para mim inventar um cenário significativo.
Então, o que esses padrões de sintaxe incomuns significam? Como isso é definido? Percebi que nem todas as posições e ordenações para as duas ON
cláusulas são permitidas. Quais são as regras que regem isso?
Além disso, é sempre uma boa ideia escrever consultas como esta?
Ele determina as tabelas lógicas envolvidas na junção.
Com um exemplo simples
#widgets1
é deixada externamente unida a#widgets2
- o resultado disso forma uma tabela virtual que é unida internamente a#widgetProperties
. O predicadow2.WidgetID = wp.WidgetID
significará que quaisquer linhas estendidas nulas da junção externa inicial serão filtradas, efetivamente tornando todas as junções internas.Isso difere de q2...
#widgets2
é unido internamente em#widgetProperties
. A tabela virtual resultante dessa junção é a tabela à direita na junção externa esquerda em#widgets1
O mesmo resultado pode ser alcançado usando uma tabela derivada ou Common Table Expression...
... Ou, alternativamente, você pode reordenar as tabelas virtuais e usar um em seu
RIGHT JOIN
lugar.Isso é coberto por Itzik Ben Gan aqui
mas o artigo contém várias imprecisões, veja também a carta de acompanhamento de Lubor Kollar .
Se você observar o
FROM
diagrama de sintaxe da cláusula , verá que há apenas um lugar para aON
cláusula:O que você acha confuso é a recursão simples, porque
<table_source>
em<joined_table
> acima pode ser outro<joined_table
>:Para evitar confusão, você deve usar parênteses em casos não óbvios (como seus exemplos) para separar visualmente
<table_sources>
; eles não são necessários para o analisador de consulta, mas são úteis para humanos.