This behavior, although unintuitive, is very well defined in the Microsoft Knowledge Base:
KB # 298674: PRB: Subquery resolves column names for external tables
From this article:
To illustrate the behavior, use the following two table structures and query:
CREATE TABLE X1 (ColA INT, ColB INT) CREATE TABLE X2 (ColC INT, ColD INT) SELECT ColA FROM X1 WHERE ColA IN (Select ColB FROM X2)
The query returns a result in which the ColB column is viewed from table X1.
When qualifying the column name, an error message appears, as shown in the following query:
SELECT ColA FROM X1 WHERE ColA in (Select X2.ColB FROM X2)
Server: msg 207, level 16, state 3, line 1
Invalid column name 'ColB'.
People have been complaining about this problem for many years, but Microsoft is not going to fix it. This, in the end, is consistent with the standard, which essentially states:
If you do not find column x in the current area, go to the next outer area and so on until you find the link.
For more information, see the following “Error” messages, as well as some official evidence that this design behavior will not change (so you have to change yours — that is, always use aliases ):
Connection # 338468: CTE column name resolution in Sub Query not checked
Connect # 735178: T-SQL subquery does not work in some cases when the IN statement is used
Connection # 302281: non-existent column ignores subquery
Connection # 772612: an alias error is not reported when inside an IN statement
Connect # 265772: Error using sub-block selection
In your case, this “error” is likely to be much less likely if you use more meaningful names than ID, OID, and PID. Does Order.PID
to Person.id
or Person.PID
? Create your spreadsheets so people can figure out relationships without asking you. A PersonID
should always be PersonID
, regardless of where it is in the schema; same with OrderID
. Saving multiple input characters is not a good price to pay for a completely ambiguous scheme.
Instead, you can write an EXISTS
clause:
... FROM dbo.Person AS p WHERE EXISTS ( SELECT 1 FROM dbo.[Order] AS o WHERE o.PID = p.id