The column prefix '%s' does not match with a table name or alias name used in the query
Asked Answered
T

1

6

i'm trying to run a query against a remote 2000 server; but the query that the local server is generating is incorrect, and causes the remote server to return the error:

The column prefix 'Tbl1002' does not match with a table name or alias name used in the query.

When you trace the remote server, you can see that the sp_cursorprepexec batch is, in fact, invalid SQL; it has a reference to a dervied table Tbl1002 that does no exist.

The query i am running on my local server is:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(*) 
         FROM Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM Positions P
WHERE P.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}' 

Where Employees and Positions are views that are simply selects from the linked server. In order to eliminate that confusion, we will eliminate the views - and use four part naming directly:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(*) 
         FROM WCLHR.CasinoHR.dbo.Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM WCLHR.CasinoHR.dbo.Positions P
WHERE P.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}' 

And the query still fails with:

The column prefix 'Tbl1002' does not match with a table name or alias name used in the query.

In order to eliminate any confusion around the guid in the WHERE clause, we'll eliminate the WHERE clause:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(*) 
         FROM WCLHR.CasinoHR.dbo.Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM WCLHR.CasinoHR.dbo.Positions P

And it still fails with:

The column prefix 'Tbl1002' does not match with a table name or alias name used in the query.

In order to eliminte any confusion around the use of * in the COUNT, we'll eliminate it, and instead only count a constant:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(1) 
         FROM WCLHR.CasinoHR.dbo.Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM WCLHR.CasinoHR.dbo.Positions P

And it still fails with:

The column prefix 'Tbl1002' does not match with a table name or alias name used in the query.

Further down we'll even eliminate the linked servers, and run the query locally on the 2000 machine.

What if you run it on the remote server itself?

If i run this query against the remote server itself:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(*) 
         FROM Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM Positions P

It works fine.

What is the generated query, how do you know it is bad?

Using Profiler, we can see the query coming in to the remote server. It's a huge horrendous mess, but it's definitely invalid. It tries to reference a derived table that isn't in scope. The whole batch will be familiar to anyone who's done work with remote servers in SQL Server:

declare @P1 int
set @P1=NULL
declare @P2 int
set @P2=NULL
declare @P3 int
set @P3=557064
declare @P4 int
set @P4=98305
declare @P5 int
set @P5=0
exec sp_cursorprepexec @P1 output, @P2 output, NULL, N'SELECT "Tbl1002"."PositionID", .....
select @P1, @P2, @P3, @P4, @P5

The real issue is the SQL statement that the server has been asked by another SQL Server to prepare. Trimmed down, it says:

SELECT 
   "Tbl1002"."PositionID" "Col1010", ...
   (   SELECT "Expr1007" 
       FROM (
          SELECT "Expr1006","Expr1006" "Expr1007" 
          FROM (
             SELECT COUNT(*) "Expr1006" 
             FROM (
                SELECT 
                   "Tbl1005"."EmployeeID" "Col1043", ...
                FROM "CasinoHR"."dbo"."Employees" "Tbl1005" 
                WHERE "Tbl1005"."PositionID"="Tbl1002"."PositionID"
             ) Qry1103
          ) Qry1104
       ) "Subquery_Source_Tbl" 
    ) "Expr1008" 
FROM "CasinoHR"."dbo"."Positions" "Tbl1002" 
WHERE "Tbl1002"."PositionID"={guid'D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7'}'

It's a messy read, but you can see the problem, it's referencing Tbl1002 inside some nested derived tables:

WHERE "Tbl1005"."PositionID"="Tbl1002"."PositionID"

But only declaring it outside; at the end:

FROM "CasinoHR"."dbo"."Positions" "Tbl1002"  

What versions of SQL Server are we talking about here?

The "remote" server that we are trying to query ("wclhr") is SQL Server 2000 with SP4:

Microsoft SQL Server 2000 - 8.00.2066 (Intel X86) May 11 2012 18:41:14

When issuing the query, we've tried from SQL Server 2005, and SQL Server 2008 R2. It used to work when both servers were SQL Server 2000.

Starting with SQL Server 2005, and continuing to 2008 R2, it is generating invalid SQL!

Other things we've tried

Surprising, a horrible hack is to run:

SELECT TOP 99.999999 PERCENT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(1) 
         FROM WCLHR.CasinoHR.dbo.Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM WCLHR.CasinoHR.dbo.Positions P

That stops the local SQL Server 2008 R2 from generating invalid sql for the 2000 machine.

The local servers are not 64-bit, but we upgraded the catalogs on SQL Server 2000 anyway. It didn't fix it.

Isn't your original query just as wrong?

@Damien the Unbeliever doesn't believe that the scoping can be the problem. Rest assured, it is. My original query runs correctly against SQL Sever 2000:

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember,
    (    SELECT COUNT(*) 
         FROM Employees E 
         WHERE E.PositionID = P.PositionID
    ) AS EmployeeCount
FROM Positions P
WHERE P.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}' 

Unfortunately, the SQL Server 2005/2008/2008R2 optimizer transforms that query into an equivalent query - but unfortunately one that SQL Server 2000 is unable to execute:

SELECT 
   Tbl1002.PositionID, 
   Tbl1002.Name AS PositionName, 
   Tbl1002.CompCommitteeMember,
   (    SELECT RecordCount
        FROM (
            SELECT COUNT(*) AS RecordCount
            FROM (
                SELECT 
                    Employees.EmployeeID
                FROM Employees
                WHERE Employees.PositionID=Tbl1002.PositionID
            ) Qry1103
        ) Qry1104
    ) AS EmployeeCount
FROM Positions Tbl1002
WHERE Tbl1002.PositionID= 'D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7'

Which, on SQL Server 2000, gives:

Msg 107, Level 16, State 2, Line 12
The column prefix 'Tbl1002' does not match with a table name or alias name used in the query.

SQL Server 2000 seems to have scoping issues with correlated sub-queries; that were "improved" in SQL Server 2005.

Bonus Reading

Tommi answered 17/4, 2013 at 20:57 Comment(10)
Did you try using OPENQUERY instead of the four part naming directly?Allegraallegretto
Show us your LinkedServer settings for the 2000 server on the 2005/2008 server.Pfeffer
Looking at the query, can it not be changed to E.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}'? Considering the fact that outer query looks for a specific PositionID, why use a JOIN in a subquery?Overweight
I don't understand your point about "But only declaring it outside; at the end:" - isn't exactly the same true in your original query where you're referencing P in the correlated subquery, yet you only "declare" it in your FROM clause at the end?Ninfaningal
@Ninfaningal You are correct. My query, when i run it natively on the 2000 machine runs fine. The remote query, generated by the 2005 machine, when run natively on the 2000 machine fails.Tommi
@Overweight "Why use a JOIN in a sub-query?" That's a good question. Unfortunately i didn't write that query, SQL Server 2005 did! My query has no join in the subquery. SQL Server took my perfectly good query and make it invalid T-SQL.Tommi
@IanBoyd: I mean, in your original query (of which you have control for modification) - why do you need a join?Overweight
@Overweight Ahh, i get you now. You're looking at the case where i simply want the one position. But with a simple changing of the where clause i can get multiple positions, or all positions. Remove the where clause and the join is needed.Tommi
@IanBoyd: Here is what I mean, change the inner query to FROM WCLHR.CasinoHR.dbo.Employees E WHERE E.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}'. Do you find it useful?Overweight
@Overweight That certainly stops the error from happening.Tommi
B
2

Based on the reading you attached it looks like trying to truly work around this problem would require you to restructure your query so as to avoid the correlated subquery on the linked server.

One possibility could be to include your linked table as a join in a grouped select and evaluate the aggregate count in that statement.

SELECT
    P.Code, P.Name AS PositionName, P.CompCommitteeMember, Count(*)
FROM Positions P
Left Join Employees E on E.PositionID = P.PositionID
WHERE P.PositionID = '{D1B0912D-B1A5-11D4-BBDD-0004ACC5B8A7}' 
group by P.Code, P.Name, P.CompCommitteeMember
Backpack answered 18/4, 2013 at 3:41 Comment(1)
Accepted for "try to rework the query to get around the optimizer bug". They are scheduled, in 5 or 6 months, to upgrade that server to 2008 R2. Hopefully we can just live with the crash until then.Tommi

© 2022 - 2024 — McMap. All rights reserved.