O identificador multi-partes não poderia ser vinculado

I'já vi erros semelhantes na SO, mas não'não encontro uma solução para o meu problema. Eu tenho uma consulta SQL como:

SELECT DISTINCT
        a.maxa ,
        b.mahuyen ,
        a.tenxa ,
        b.tenhuyen ,
        ISNULL(dkcd.tong, 0) AS tongdkcd
FROM    phuongxa a ,
        quanhuyen b
        LEFT OUTER JOIN ( SELECT    maxa ,
                                    COUNT(*) AS tong
                          FROM      khaosat
                          WHERE     CONVERT(DATETIME, ngaylap, 103) BETWEEN 'Sep 1 2011'
                                                              AND
                                                              'Sep 5 2011'
                          GROUP BY  maxa
                        ) AS dkcd ON dkcd.maxa = a.maxa
WHERE   a.maxa <> '99'
        AND LEFT(a.maxa, 2) = b.mahuyen
ORDER BY maxa;

Quando eu executo esta consulta, o resultado do erro é: O identificador multi-partes "a.maxa" não pôde ser vinculado. Porquê?
P/s: se eu dividir a consulta em 2 consultas individuais, ela funciona bem.

SELECT DISTINCT
        a.maxa ,
        b.mahuyen ,
        a.tenxa ,
        b.tenhuyen
FROM    phuongxa a ,
        quanhuyen b
WHERE   a.maxa <> '99'
        AND LEFT(a.maxa, 2) = b.mahuyen
ORDER BY maxa;

e

SELECT  maxa ,
        COUNT(*) AS tong
FROM    khaosat
WHERE   CONVERT(DATETIME, ngaylap, 103) BETWEEN 'Sep 1 2011'
                                        AND     'Sep 5 2011'
GROUP BY maxa;
Solução

Você está misturando junções implícitas com junções explícitas. Isso é permitido, mas você precisa estar ciente de como fazer isso corretamente.

O problema é que as joinings explícitas (aquelas que são implementadas utilizando a palavra-chave JOIN) têm precedência sobre as implícitas (o 'comma' joinins, onde a condição de join é especificada na cláusula WHERE).

Aqui's um esboço da sua consulta:

SELECT
  …
FROM a, b LEFT JOIN dkcd ON …
WHERE …

Provavelmente estás à espera que ele se comporte assim:

SELECT
  …
FROM (a, b) LEFT JOIN dkcd ON …
WHERE …

ou seja, a combinação das tabelas a' eb' é unida à tabela `dkcd'. Na verdade, o que's está acontecendo é

SELECT
  …
FROM a, (b LEFT JOIN dkcd ON …)
WHERE …

isto é, como você já deve ter entendido, dkcd é unido especificamente contra b e apenas b, então o resultado da união é combinado com a e filtrado ainda mais com a cláusula WHERE. Neste caso, qualquer referência a a na cláusula ON é inválida, a é desconhecida nesse ponto. É por isso que você está recebendo a mensagem de erro.

Se eu fosse você, provavelmente tentaria reescrever esta consulta, e uma possível solução poderia ser:

SELECT DISTINCT
  a.maxa,
  b.mahuyen,
  a.tenxa,
  b.tenhuyen,
  ISNULL(dkcd.tong, 0) AS tongdkcd
FROM phuongxa a
  INNER JOIN quanhuyen b ON LEFT(a.maxa, 2) = b.mahuyen
  LEFT OUTER JOIN (
    SELECT
      maxa,
      COUNT(*) AS tong
    FROM khaosat
    WHERE CONVERT(datetime, ngaylap, 103) BETWEEN 'Sep 1 2011' AND 'Sep 5 2011'
    GROUP BY maxa
  ) AS dkcd ON dkcd.maxa = a.maxa
WHERE a.maxa  '99'
ORDER BY a.maxa

Aqui as tabelas a' eb' são unidas primeiro, depois o resultado é unido ao dkcd'. Basicamente, esta é a mesma consulta que a sua, utilizando apenas uma sintaxe diferente para uma das junções, o que faz uma grande diferença: a referênciaa.maxana condição de junçãodkcd`'s é agora absolutamente válida.

Como @Aaron Bertrand observou corretamente, você provavelmente deve qualificar maxa com um apelido específico, provavelmente a, na cláusula ORDER BY.

Comentários (9)

Às vezes este erro ocorre quando você usa o seu esquema (dbo) na sua consulta de uma forma errada.

por exemplo, se você escrever:

select dbo.prd.name
from dbo.product prd

você vai receber o erro.

Nestas situações, muda para:

select prd.name
from dbo.product prd
Comentários (1)

I'sou novo em SQL, mas deparei-me com este problema num curso que estava a fazer e descobri que atribuir a consulta ao projecto ajudou especificamente a eliminar o erro multi-partes. Por exemplo, o projeto que eu criei foi o CTU SQL Project, então eu me certifiquei de começar meu script com USE [CTU SQL Project] como minha primeira linha, como abaixo.

USE [CTU SQL Project]
SELECT Advisors.First_Name, Advisors.Last_Name...and so on.
Comentários (2)