Error from NOLOCK on CTE in SQL Server 2008 SP2 - sql-server

I've just set up SQL Server 2008 SP2 on my workstation to run a local version of a database. The production database is SQL Server 2008 R2. A colleague is running SQL Server 2005 on his workstation.
My DB is throwing an error with the following code, while other instances of the same DB on the other servers run this query with no error.
WITH Posts AS (
SELECT TOP 10 *
FROM TBL_MSG_LATEST (NOLOCK)
WHERE TBL_MSG_LATEST.STATUS = 1
)
SELECT * FROM Posts (NOLOCK)
...throws this error:
Msg 215, Level 16, State 1, Line 6
Parameters supplied for object 'Posts' which is not a function. If the parameters are intended as a table hint, a WITH keyword is required.
Removing the (NOLOCK) after Posts makes my DB happy.
I'm not familiar with SQL Server, so I don't totally understand CTEs but I believe that this NOLOCK might not even be necessary here.
However, we aren't happy about making a change to the codebase just to satisfy my dev environment.
Is there a config difference with my newer DB environment? Is there a valid reason to remove the NOLOCK that I can't decipher from the error message?

From this page: http://msdn.microsoft.com/en-us/library/ms187373.aspx
Omitting the WITH keyword is a deprecated feature and will be removed
in a future version of Microsoft SQL Server.
So this will work.
WITH Posts AS (
SELECT TOP 10 *
FROM TBL_MSG_LATEST WITH (NOLOCK)
WHERE TBL_MSG_LATEST.STATUS = 1
)
SELECT * FROM Posts WITH (NOLOCK)
Don't know if it is a good idea or not or if it has any effect.

This appears to be specific to CTE syntax. Adding the "WITH" won't change how the NOLOCK works; it will just allow it to work.
With NOLOCK hints in CTEs, these both work:
WITH Posts AS (
SELECT ... FROM TBL_MSG_LATEST NOLOCK
)
SELECT * FROM Posts NOLOCK;
WITH Posts AS (
SELECT ... FROM TBL_MSG_LATEST WITH (NOLOCK)
)
SELECT * FROM Posts WITH (NOLOCK);
but this fails:
WITH Posts AS (
SELECT ... FROM TBL_MSG_LATEST (NOLOCK)
)
SELECT * FROM Posts (NOLOCK);
These all work outside of a CTE:
SELECT ... FROM TBL_MSG_LATEST NOLOCK;
SELECT ... FROM TBL_MSG_LATEST (NOLOCK);
SELECT ... FROM TBL_MSG_LATEST WITH (NOLOCK);
The correct syntax is "WITH (NOLOCK)".
But ... if you're putting NOLOCK inside the CTE, why are you also putting it on the SELECT?
Note that the results are all the same in Denali CTP3.

Related

Microsoft SQL Server: Error with Group By

I'm new to Microsoft SQL Server 2014. I run this SQL code:
SELECT TOP(10) 'DBSG' as seek_entity, *
FROM DBSG..PM00200
and get this result:
Next, I want to find out total line items for that entity with code below.
WITH vw_pm00200_all AS
(
SELECT TOP(10)
'DBSG' as seek_entity, *
FROM
DBSG..PM00200
)
SELECT
seek_entity,
COUNT(*) AS total
FROM
vw_pm00200_all
GROUP BY
1
Sadly, I get this error. I have no idea why it failed.
Msg 164, Level 15, State 1, Line 9
Each GROUP BY expression must contain at least one column that is not an outer reference.
Lastly, please advise is Microsoft SQL Server based on Transact-SQL?
It looks like you are running into this problem here: Each GROUP BY expression must contain at least one column that is not an outer reference
As the answer points out, grouping by a constant literal is pointless as it is the same for all results. Count(*) will return the same result as Count(*) with a GROUP BY.
If this is just test code and you plan on using a CASE statement (with different values) in place of the string literal, you may have better luck.
Yes, T-SQL is Microsoft SQL Server's flavor of SQL.

Why are these DMVs returning rows for all databases except one?

I am trying to query the DMVs in SQL Server 2008 R2.
On this server are two user databases called histrx and OpenLink. To prove I have their names correct:
select db_id('histrx') -- Returns 5
select db_id('OpenLink') -- Returns 7
If I run the following query, picking out entries for the histrx database, I get 25 rows in the result set:
select top 25
total_worker_time/execution_count as avg_worker_time,
total_logical_reads/execution_count as avg_logical_reads,
db_name(s.dbid) as [db_name],
object_name(s.objectid, s.dbid) as [object_name],
execution_count,
plan_generation_num,
last_execution_time,
creation_time,
[text],
p.query_plan
from
sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) s
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) p
where
db_name(s.dbid) = 'histrx'
order by
avg_logical_reads desc
If I then change the where clause to the following, no rows are returned:
where
db_name(s.dbid) = 'OpenLink'
I know that there is a significant amount of activity on the OpenLink database. If I look at Recent Expensive Queries in the Activity Monitor, I can see entries for OpenLink, and I'm pretty sure this is using the DMVs underneath.
I'm running the Activity Monitor and the DMV query under the same
login
That login is the owner of the OpenLink database
If I run select * from fn_my_permissions (NULL, 'server'); then I can see I have VIEW SERVER STATE permissions
If I remove the where clause, I see entries for other databases such as msdb and distribution
Here is a screenshot of the mappings for my login. I'm pretty sure I shouldn't be the owner, but that's a different question.
Can anyone tell me why my DMV query is returning zero rows for this database?
Quote from Books Online 2008 R2 > sys.dm_exec_sql_text:
dbid smallint ID of database. Is NULL for ad hoc and prepared SQL
statements.
1) So, for this type of SQL statements, where db_name(s.dbid) = 'OpenLink' means where NULL = 'OpenLink' and this predicate is evaluated to UNKNOWN.
2) dbid column is NOT NULL for "non-adhoc and non-prepared SQL" statements (ex. for stored procedures).
3) For SQL 2008R2 you might get the dbid for "ad hoc and prepared SQL statements" from sys.dm_exec_plan_attributes ( plan_handle ) function (link) using this query:
SELECT
...
ISNULL(src.dbid,CONVERT(SMALLINT,att.value)) AS my_dbid,
DB_NAME(ISNULL(src.dbid,CONVERT(SMALLINT,att.value))) my_dbname,
...
FROM
sys.dm_exec_query_stats qs
...
CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) att
WHERE att.attribute='dbid'

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

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
Microsoft Connect: sql2005 sp2 - issue with linked server (sql2000) and correlated subquery in where clause
Correlated subquery fails when using a Table on Linked Server
KB825019: FIX: A linked server query fails with the error message "Statement(s) could not be prepared" in SQL Server 2000
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

Is Join syntax different in SQL Server 2012?

I'm new to SQL Server and I'm using SQL Server Managment Studio 2012. I'm trying to do a very basic join, and I even copyed the syntax from an instruction video on PluralSight (using SQL Server 2008). Yet it does not excute.
This is the query:
USE [TestDB];
SELECT * FROM Cities JOIN Persons
This is the message:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'Persons'.
The thing is when I use a "cross join" it seems to work fine with the expected results.
What am I doing wrong? And if I'm not doing anything wrong what could be the problem?
A join (other than a cross join) needs an on clause. Not knowing anything about your schema or how these two tables are related, perhaps you meant something like this:
SELECT * FROM dbo.Cities AS c
INNER JOIN dbo.Persons AS p
ON c.CityID = p.CityID;

SQL Server 2008 Full-Text Search (FTS) extremely slow when more than one CONTAINSTABLE in query

SQL Server 2008 Full-Text Search (FTS) is extremely slow in this scenario:
Query 1:
SELECT [...] FROM ContentItem CI WHERE
(**EXISTS** (SELECT TOP 1 * FROM **CONTAINSTABLE**([**Table1**], *, '"[search_string]*"') FT
WHERE FT.[Key] = CI.ContentItem_Id))
ORDER BY [...]
Results: super fast on SQL 2005 and SQL 2008
Query 2:
SELECT [...] FROM ContentItem CI WHERE
(**EXISTS** (SELECT TOP 1 * FROM **CONTAINSTABLE**([**Table2**], *, '"[search_string]*"') FT
WHERE FT.[Key] = CI.ContentItem_Id))
ORDER BY [...]
Results: super fast on SQL 2005 and SQL 2008
Query 3:
SELECT [...] FROM ContentItem CI WHERE
(**EXISTS** (SELECT TOP 1 * FROM **CONTAINSTABLE**([**Table1**], *, '"[search_string]*"') FT
WHERE FT.[Key] = CI.ContentItem_Id)
**OR EXISTS** (SELECT TOP 1 * FROM **CONTAINSTABLE**([**Table2**], *, '"[search_string]*"') FT
WHERE FT.[Key] = CI.ContentItem_Id))
ORDER BY [...]
Results: super fast on SQL 2005 (about a second), but extremely slow (3 min+) on SQL 2008
I'm aware of performance issues with SQL 2008 FTS (even on stackoverflow), but haven't find any reasonable solution yet.
Can you rewrite Query 3 to
SELECT ... WHERE EXISTS ... CONTAINSTABLE(Table1...)
UNION
SELECT ... WHERE EXISTS ... CONTAINSTABLE(Table2...)
ORDER BY ...
?
UNION ALL may be faster than UNION, but possibly result in duplicate records.
You probably get a bad plan. If you can post plan, it will help diagnose the problem. In case a bad join was choose, you can use query hint to solve the problem.
We just upgraded to SQL 2008 and ran into this issue. I found if I put this at the bottom of the query, it worked great for me: OPTION (MAXDOP 1)

Resources