Speed up Update statement T-SQL with Top 1 - sql

I'm creating a stored procedure for an ETL script that'll run once per hour to give results of specific operations to users.
I need to find the previous result to the current result. This is fine and I have a working query that I export into Excel. However now I wish to automate the process.
The stored procedure averages at 42 seconds per run. This isn't feasible when running once an hour on the server as I have other automated scripts also running.
My issue is one chunk of the stored procedure averages at 28 seconds, whilst everything else usually takes less than a second (shows up at 00:00:00 in SSMS).
I've managed to reduce the runtime of other chunks myself bringing it down to 42 seconds average, but I can't do this.
I was wondering if any of you know any specfic ways to speed this small chunk up?
UPDATE #tmp
SET prev_test_date = (
SELECT TOP 1 r.test_date
FROM [dbo].[results] r (NOLOCK)
WHERE r.number = #tmp.number
AND r.test_date < #tmp.test_date
ORDER BY r.test_date DESC
)
I was originally going to use joins for this to speed it up, although I can't do this due to the TOP 1 part of the query.
Any ideas?

For this query:
UPDATE #tmp
SET prev_test_date = (
SELECT TOP 1 r.test_date
FROM [dbo].[results] r
WHERE r.number = #tmp.number AND
r.test_date < #tmp.test_date
ORDER BY r.test_date DESC
)
You want an index on r(number, test_date).
If you are using SQL Server 2012+ and the test dates are not duplicated, you can also write this as:
with r as (
select r.*,
lag(r.test_date) over (partition by r.number order by r.test_date desc) as prev_test_date
from [dbo].[results] r
)
update t
set t.prev_test_date = r.prev_test_date
from #tmp t join
r
on t.number = r.number;
In fact, if this is the case, you might not need the temporary table. You might be able to modify the code just to use lag().

UPDATE #tmp
SET prev_test_date = (
SELECT max(r.test_date)
FROM [dbo].[results] r (NOLOCK)
WHERE r.number = #tmp.number
AND r.test_date < #tmp.test_date
)
Without more info it is hard to tell, but if there is simply too much processing You may need to make separate precalculated table and update it incrementally on data change.

UPDATE #tmp
SET #tmp.prev_test_date = tt.maxdate
from #tmp
join
(
select #tmp.number, max(r.test_date) maxdate
from #tmp
join [dbo].[results] r (NOLOCK)
on r.number = #tmp.number
AND r.test_date < #tmp.test_date
group by #tmp.number
) tt
on tt.number = #tmp.number
and have indexes on both #tmp and [results] on number, text_date

I'm having to make some assumptions about the structure and contents of your tables, but if my assumptions are correct, here's the approach I usually use in such situations:
with cteOrderedResults as (
-- Ideally R will be clustered by number, test_date for this
select R.number
,R.test_date
,row_number() over ( partition by R.number
order by R.test_date desc
-- So the most recent R.test_date from
-- before T.test_date gets RowNo=1
) as RowNo
from dbo.results R
inner join #tmp T on R.number=T.number
and R.test_date<T.test_date
)
update T
set T.prev_test_date=R.test_date
from #tmp T
inner join cteOrderedResults R on T.number=R.number
and 1=R.RowNo
This approach works quickly for me on rowsets ranging up to about the million mark. As I've commented, I believe the partitioned row_number() is going to be taking advantage of a corresponding clustered index if it exists; you might find this doesn't work so fast if you don't have the table clustered appropriately.
I agree with comments made elsewhere here, that you should only add the nolock hint back in if you're really sure you need it. If you do, you should use the full correct syntax, with (nolock). From the official MSDN page:
Omitting the WITH keyword is a deprecated feature: This feature will be removed in a future version of Microsoft SQL Server.

Related

How to fine tune SQL Server query execution plan generation?

I have a large "Deals" table (1.3 million rows) that needs to be displayed on a paginated grid in my application, the application also includes filters to help the user search through those rows, the generated SQL follows the structure below:
SELECT TOP 10 *
FROM (
SELECT
ROW_NUMBER() OVER(ORDER BY [DealID] DESC) AS RowNumber, *
FROM (
select d.DealID, a.[Description] as Asset,
from Deals d
inner join Assets a on d.AssetID = a.AssetID
) as Sorted
where Asset like '%*my asset%'
) as Sorted
My problem is with the execution plan generated for this query, because it's ordered by DealID, SQL Server is choosing the clustered index on DealID to execute this query and performs a clustered Index Scan on this table that has 1.3 million rows, but the query is also being filtered by Asset and there are only 171 rows that satisfy the filter, so it's much faster to use the non-clustered index on the asset first and then sort the resulting rows, I'm already able to fix this issue by adding the WITH INDEX(IX_Asset_ID)) hint into the query, but the problem is that since this is a generated query, this will add a lot of complexity to the code the generates this query.
So my question is, is there a way to get SQL Server to detect this situation without the hint? Maybe update statistics or something like that? Or even moving the hint to the end of the query would actually help since the middle of the query is actually a report written by the client.
--Edit--
As pointed out in the comments there are a few issues with the query, but those were actually created by the fact that I attempted to create a minimal reproducible example of the problem so I omitted the paging part of the query, structure below is a more complete version that should make more sense:
SELECT TOP #pageLength * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY [DealID] DESC) AS RowNumber, *
FROM (
SELECT d.DealID, a.[Description] AS Asset, FROM Deals d
INNER JOIN Assets a on d.AssetID = a.AssetID
) AS Sorted
WHERE Asset LIKE '%*my asset%'
) AS Paged
WHERE RowNumber > #startRow
ORDER BY RowNumber
OPTION(RECOMPILE)
It's much better to page based off your clustered index key values, something like this
SELECT TOP (#pageLength)
d.DealID,
a.[Description] AS Asset,
#startRowNumber + ROW_NUMBER() OVER(ORDER BY [DealID] DESC) AS RowNumber
FROM Deals d
INNER JOIN Assets a on d.AssetID = a.AssetID
WHERE DealId > #startDealId
and a.[Description] LIKE '%*my asset%'
ORDER BY DealId
This technique is sometimes called "keyset pagination", and it leverages the ordered index to allow SQL to seek directly to the next clustered index key after the last page. You track the rows based on the key value rather than the generated row number.
In the end the solution for me was to use the DISABLE_OPTIMIZER_ROWGOAL hint.
I think what's happening here is that SQL Server is being too optimistic about the query only requiring 10 rows and is scanning too much of the table because if thinks it won't take long to find the first 10 but in reality it's better to use the available indexes, adding the hint causes it to change the plan and the query runs quickly.
SELECT TOP #pageLength * FROM (
SELECT ROW_NUMBER() OVER(ORDER BY [DealID] DESC) AS RowNumber, *
FROM (
SELECT d.DealID, a.[Description] AS Asset, FROM Deals d
INNER JOIN Assets a on d.AssetID = a.AssetID
) AS Sorted
WHERE Asset LIKE '%*my asset%'
) AS Paged
WHERE RowNumber > #startRow
ORDER BY RowNumber
OPTION(RECOMPILE, USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'))

Order of Operation in SQL Server Query

I have the below query selecting items and one of its feature from a hundred thousand row of items.
But I am concerned about the performance of sub query. Will it be executed after or before the where clause ?
Suppose, I am selecting 25 items from 10000 items, this subquery will be executed only for 25 items or 10000 items ?
declare #BlockStart int = 1
, #BlockSize int = 25
;
select *, (
select Value_Float
from Features B
where B.Feature_ModelID = Itm.ModelID
and B.Feature_PropertyID = 5
) as Price
from (
select *
, row_number() over (order by ModelID desc) as RowNumber
from Models
) Itm
where Itm.RowNumber >= #BlockStart
and Itm.RowNumber < #BlockStart + #BlockSize
order by ModelID desc
The sub query in the FROM clause produces a full set of results, but the sub query in the SELECT clause will (generally!) only be run for the records included with the final result set.
As with all things SQL, there is a query optimizer involved, which may at times decide to create seemingly-strange execution plans. In this case, I believe we can be pretty confident, but I need to caution about making sweeping generalizations about SQL language order of operations.
Moving on, have you seen the OFFSET/FECTH syntax available in Sql Server 2012 and later? This seems like a better way to handle the #BlockStart and #BlockSize values, especially as it looks like you're paging on the clustered key. (If you end up paging on an alternate column, the link shows a much faster method).
Also, at risk of making generalizations again, if you can know that only one Features record exists per ModelID with Feature_PropertyID = 5, you will tend to get better performance using a JOIN:
SELECT m.*, f.Value_Float As Price
FROM Models m
LEFT JOIN Features f ON f.Feature_ModelID = m.ModelID AND f.Feature_PropertyID = 5
ORDER BY m.ModelID DESC
OFFSET #BlockStart ROWS FETCH NEXT #BlockSize ROWS ONLY
If you can't make that guarantee, you may get better performance from an APPLY operation:
SELECT m.*, f.Value_Float As Price
FROM Models m
OUTER APPLY (
SELECT TOP 1 Value_Float
FROM Features f
WHERE f.Feature_ModelID = m.ModelID AND f.Feature_PropertyID = 5
) f
ORDER BY m.ModelID DESC
OFFSET #BlockStart ROWS FETCH NEXT #BlockSize ROWS ONLY
Finally, this smells like yet another variation of the Entity-Attribute-Value pattern... which, while it has it's places, typically should be a pattern of last resort.

Is there a way to get different results for the same SQL query if the data stays the same?

I get a different result set for this query intermittently when I run it...sometimes it gives 1363, sometimes 1365 and sometimes 1366 results. The data doesn't change. What could be causing this and is there a way to prevent it? Query looks something like this:
SELECT *
FROM
(
SELECT
RC.UserGroupId,
RC.UserGroup,
RC.ClientId AS CLID,
CASE WHEN T1.MultipleClients = 1 THEN RC.Salutation1 ELSE RC.DisplayName1 END AS szDisplayName,
T1.MultipleClients,
RC.IsPrimaryRecord,
RC.RecordTypeId,
RC.ClientTypeId,
RC.ClientType,
RC.IsDeleted,
RC.IsCompany,
RC.KnownAs,
RC.Salutation1,
RC.FirstName,
RC.Surname,
Relationship,
C.DisplayName Client,
RC.DisplayName RelatedClient,
E.Email,
RC.DisplayName + ' is the ' + R.Relationship + ' of ' + C.DisplayName Description,
ROW_NUMBER() OVER (PARTITION BY E.Email ORDER BY Relationship DESC) AS sequence_id
FROM
SSDS.Client.ClientExtended C
INNER JOIN
SSDS.Client.ClientRelationship R WITH (NOLOCK)ON C.ClientId = R.ClientID
INNER JOIN
SSDS.Client.ClientExtended RC WITH (NOLOCK)ON R.RelatedClientId = RC.ClientId
LEFT OUTER JOIN
SSDS.Client.Email E WITH (NOLOCK)ON RC.ClientId = E.ClientId
LEFT OUTER JOIN
SSDS.Client.UserDefinedData UD WITH (NOLOCK)ON C.ClientId = UD.ClientId AND C.UserGroupId = UD.UserGroupId
INNER JOIN
(
SELECT
E.Email,
CASE WHEN (COUNT(DISTINCT RC.DisplayName) > 1) THEN 1 ELSE 0 END AS MultipleClients
FROM
SSDS.Client.ClientExtended C
INNER JOIN
SSDS.Client.ClientRelationship R WITH (NOLOCK)ON C.ClientId = R.ClientID
INNER JOIN
SSDS.Client.ClientExtended RC WITH (NOLOCK)ON R.RelatedClientId = RC.ClientId
LEFT OUTER JOIN
SSDS.Client.Email E WITH (NOLOCK)ON RC.ClientId = E.ClientId
LEFT OUTER JOIN
SSDS.Client.UserDefinedData UD WITH (NOLOCK)ON C.ClientId = UD.ClientId AND C.UserGroupId = UD.UserGroupId
WHERE
Relationship IN ('z-Group Principle', 'z-Group Member ')
AND E.Email IS NOT NULL
GROUP BY E.Email
) T1 ON E.Email = T1.Email
WHERE
Relationship IN ('z-Group Principle', 'z-Group Member ')
AND E.Email IS NOT NULL
) T
WHERE
sequence_id = 1
AND T.UserGroupId IN (Select * from iCentral.dbo.GetSubUserGroups('471b9cbd-2312-4a8a-bb20-35ea53d30340',0))
AND T.IsDeleted = 0
AND T.RecordTypeId = 1
AND T.ClientTypeId IN
(
'1', --Client
'-1652203805' --NTU
)
AND T.CLID NOT IN
(
SELECT DISTINCT
UDDF.CLID
FROM
SLacsis_SLM.dbo.T_UserDef UD WITH (NOLOCK)
INNER JOIN
SLacsis_SLM.dbo.T_UserDefData UDDF WITH (NOLOCK)
ON UD.UserDef_ID = UDDF.UserDef_ID
INNER JOIN
SLacsis_SLM.dbo.T_Client CLL WITH (NOLOCK)
ON CLL.CLID = UDDF.CLID AND CLL.UserGroup_CLID = UD.UserID
WHERE
UD.UserDef_ID in
(
'F68F31CE-525B-4455-9D50-6DA77C66FEE5',
'A7CECB03-866C-4F1F-9E1A-CEB09474FE47'
)
AND UDDF.Data = 'NO'
)
ORDER BY T.Surname
EDIT:
I have removed all NOLOCK's (including the ones in views and UDFs) and I'm still having the same issue. I get the same results every time for the nested select (T) and if I put the result set of T into a temp table in the beginning of the query and join onto the temp table instead of the nested select then the final result set is the same every time I run the query.
EDIT2:
I have been doing some more reading on ROW_NUMBER()...I'm partitioning by email (of which there are duplicates) and ordering by Relationship (where there are only 1 of 2 relationships). Could this cause the query to be non-deterministic and would there be a way to fix that?
EDIT3:
Here are the actual execution plans if anyone is interested http://www.mediafire.com/?qo5gkh5dftxf0ml. Is it possible to see that it is running as read committed from the execution plan? I've compared the files using WinMerge and the only differences seem to be the counts (ActualRows="").
EDIT4:
This works:
SELECT * FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY B.Email ORDER BY Relationship DESC) AS sequence_id
FROM
(
SELECT DISTINCT
RC.UserGroupId,
...
) B...
EDIT5:
When running the same ROW_NUMBER() query (T in the original question, just selecting RC.DisplayName and ROW_NUMBER) twice in a row I get different rank for some people:
Does anyone have a good explanation/example of why or how ROW_NUMBER() over a result set that contains duplicates can rank differently each time it is run and ultimately change the number of results?
EDIT6:
Ok I think this makes sense to me now. This occurs when people 2 people have the same email address (e.g a husband and wife pair) and relationship. I guess in this case their ROW_NUMBER() ranking is arbitrary and can change every time it is run.
Your use of NOLOCK all over means you are doing dirty reads and will see uncommitted data, data that will be rolled back, transient and inconsistent data etc
Take these off, try again, report back pleas
Edit: some options with NOLOCKS removed
Data is really changing
Some parameter or filter is changing (eg GETDATE)
Some float comparisons running on different cores each time
See this on dba.se https://dba.stackexchange.com/q/4810/630
Embedded NOLOCKs in udfs or views (eg iCentral.dbo.GetSubUserGroups)
...
As I said yesterday in the comments the row numbering for rows with duplicate E.Email, Relationship values will be arbitrary.
To make it deterministic you would need to do PARTITION BY B.Email ORDER BY Relationship DESC, SomeUniqueColumn . Interesting that it changes between runs though using the same execution plan. I assume this is a consequence of the hash join.
I think your problem is the first row over the partition is not deterministic. I suspect that Email and Relationship is not unique.
ROW_NUMBER() OVER (PARTITION BY E.Email ORDER BY Relationship DESC) AS sequence_id
Later you examine the first row of the partition.
WHERE T.sequence_id = 1
AND T.UserGroupId ...
If that first row is arbitrary then you are going to get an arbitrary where comparison. You need to add to the ORDER BY to include a complete unique key. If there is no unique key then you need to make one or live with arbitrary results. Even on a table with a clustered PK the select row order is not guaranteed unless the entire PK is in the sort clause.
This probably has to do with ordering. You have a sequence_id defined as a row_number ordered by Relationship. You'll always get a sensible order by relationship, but other than that your row_number will be random. So you can get different rows with sequence_id 1 each time. That in turn will affect your where clause, and you can get different numbers of results. To fix this to get a consistent result, add another field to your row_number's order by. Use a primary key to be certain of consistent results.
There's a recent KB that addresses problems with ROW_NUMBER() ... see FIX: You receive an incorrect result when you run a query that uses the row_number function in SQL Server 2008 for the details.
However this KB indicates that it's a problem when parallelism is invoked for execution, and looking at your execution plans I can't see this kicking in. But the fact that MS have found a problem with it in one situation makes me a little bit wary - i.e., could the same issue occur for a sufficiently complicated query (and your execution plan does look sufficiently large).
So it may be worth checking your patch levels of SQL Server 2008.
U Must only use
Order by
without prtition by.
ROW_NUMBER() OVER (ORDER BY Relationship DESC) AS sequence_id

SQL CTE in a View vs Temp Table in a Stored Procedure

Please bear with me -- I know this is complex.
I have a table that contains apartments, and another that contains leases for those apartments. My task is to select the "most relevant" lease from the list. In general, that means the most recent lease, but there are a few quirks that make it more complex than just ordering by date.
That has led me to create this common table expression query inside a View, which I then JOIN with a number of others inside a Stored Procedure to get the results I need:
WITH TempTable AS (
SELECT l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM dbo.NPleaseapplicant AS l INNER JOIN
dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
)
SELECT BuildingID, ApartmentID, LeaseID, ApplicantID
FROM TempTable
WHERE RowNumber = 1
This works and returns the correct result. The challenge I'm facing is very slow performance.
As a test, I created a temp table inside the Stored Procedure instead of using View, and got much, much better performance:
CREATE TABLE #Relevant (
BuildingID int,
ApartmentID int,
LeaseID int,
ApplicantID int,
RowNumber int
)
INSERT INTO #Relevant (BuildingID, ApartmentID, LeaseID, ApplicantID, RowNumber)
SELECT l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM dbo.NPleaseapplicant AS l INNER JOIN
dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
WHERE (l.BuildingID = #BuildingID)
DROP TABLE #Relevant
At first glance this doesn't add up to me. I've heard that temp tables are notoriously bad for performance. The wrinkle is that I'm able to better limit the query in the Temp Table with a WHERE clause that I can't in the View. With over 10,000 leases across 16 buildings in the table, the ability to filter with the WHERE may drop the rows affected by 90% - 95%.
Bearing all that in mind, is there anything glaring that I'm missing here? Am I doing something wrong with the View that might cause the dreadful performance, or is it just a matter of a smaller result set in the Temp Table beating the unrestricted result set in the CTE?
EDIT: I should add that this business logic of selecting the "most relevant lease" is key to many reports in the system. That's why it was placed inside a View to begin with. The View gives us "Write Once, Use Many" capabilities whereas a Temp Table in a Stored Procedure would need to be recreated for every other Stored Proc in the system. Ugly.
EDIT #2: Could I use a Table Based Function instead of a view? Would that allow me to limit the rows affected up front, and still use the resulting dataset in a JOIN with other tables? If it works -- and has decent performance -- this would allow me to keep the business logic in one place (the function) instead of duplicating it in dozens of Stored Procedures.
Just to put a bow on this one, here's what I ended up doing:
Instead of using a View to join all possible rows from 2 or 3 tables, I created a Table Based Function that makes the same basic query. As one of the parameters I pass in the Building ID, and use that in a WHERE clause, like so:
SELECT l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM dbo.NPleaseapplicant AS l INNER JOIN
dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
WHERE (l.BuildingID = #BuildingID)
The result is that it drastically reduces the number of joins required, and it speeds up the query immensely.
I then change all the stored procedures that rely on the View to use the Function instead, and bingo -- huge performance gains.
You could also re-write the view with subquery syntax:
SELECT BuildingID, ApartmentID, LeaseID, ApplicantID
FROM
(
SELECT l.BuildingID, l.ApartmentID, l.LeaseID, l.ApplicantID,
ROW_NUMBER() OVER (PARTITION BY l.ApartmentID ORDER BY s.Importance DESC, MovedOut, MovedIN DESC, LLSigned DESC, Approved DESC, Applied DESC) AS 'RowNumber'
FROM dbo.NPleaseapplicant AS l INNER JOIN
dbo.NPappstatus AS s ON l.BuildingID = s.BuildingID AND l.AppStatus = s.Code
)subquery
WHERE RowNumber = 1
Doing this will allow the bounding Where (where the view is being used) to be applied to the subquery, whereas the CTE case isn't bounded.
Views have fewer issues around parallel execution plans than Table Valued Functions (although this one will probably be inlined anyway, making them effectively identical)

SQL2000 to SQL2005. Query now a lot slower

This query used to take 3secs in SQL2000, now it takes about 70secs. Both databases give the same results. The 2005 database is not running in compatibility mode.
Currently we're rebuilding the query to run in SQL2005.. by a process of elimination and understanding the logic.
However - can anyone see anything obvious that we've missed.
And/or are there any tools that could help here?
We've been looking at the Execution plan... and profiler. And index tuning wizard.
Profiler points to a massive number more records being queried to get the same results.
I know that this is a very hard question to debug without the data... another pair of eyes is always good if there is anything obvious!
Cheers
Dave
ALTER PROCEDURE [dbo].[GetNodeList]
#ViewID int,
#UserID int = null
as
Select ProcessList.*,
A.NDOC_DOC_ID,
A.NDOC_Order,
A.OMNIBOOK_ID,
A.Node_Order
from (
(SELECT N.NOD_ID,
N.NOD_Name,
N.NOD_Procname,
N.NOD_Xpos,
N.NOD_Ypos,
N.NOD_Zpos,
VN.VNOD_VIE_ID
FROM Node N
INNER JOIN View_NODe VN
ON N.NOD_ID = VN.VNOD_NOD_ID
Where VN.VNOD_VIE_ID = #ViewID) ProcessList
Left Join
(
SELECT N.NOD_ID,
N.NOD_Name,
N.NOD_Procname,
N.NOD_Xpos as NOD_Xpos,
N.NOD_Ypos as NOD_Ypos,
N.NOD_Zpos as NOD_Zpos,
VN.VNOD_VIE_ID,
ND.NDOC_DOC_ID as NDOC_DOC_ID,
ND.NDOC_Order as NDOC_Order,
null as OMNIBOOK_ID,
null as Node_Order
FROM Node N
INNER JOIN View_NODe VN
ON N.NOD_ID = VN.VNOD_NOD_ID
LEFT JOIN NODe_DOCument ND
ON N.NOD_ID = ND.NDOC_NOD_ID
WHERE VN.VNOD_VIE_ID=#ViewID
and ND.NDOC_DOC_ID is not null
and (#UserID is null
or exists (Select 1
from Document D
where Doc_ID = ND.NDOC_DOC_ID
and dbo.fn_UserCanSeeDoc(#UserID,D.Doc_ID)<>0
)
)
UNION
SELECT N.NOD_ID,
N.NOD_Name,
N.NOD_Procname,
N.NOD_Xpos,
N.NOD_Ypos,
N.NOD_Zpos,
VN.VNOD_VIE_ID,
null,
null,
NOM.OMNIBOOK_ID,
NOM.Node_Order
FROM Node N
INNER JOIN View_NODe VN
ON N.NOD_ID = VN.VNOD_NOD_ID
LEFT JOIN NODe_OMNIBOOK NOM
ON N.NOD_ID = NOM.NODE_ID
WHERE VN.VNOD_VIE_ID=#ViewID
and NOM.OMNIBOOK_ID is not null
and exists (select 1 from Omnibook_Doc where OmnibookID = NOM.OMNIBOOK_ID)
) A
--On ProcessList.NOD_ID = A.NOD_ID
ON ProcessList.NOD_Xpos = A.NOD_Xpos
And ProcessList.NOD_Ypos = A.NOD_Ypos
And ProcessList.NOD_Zpos = A.NOD_Zpos
And ProcessList.VNOD_VIE_ID = A.VNOD_VIE_ID
)
ORDER BY
ProcessList.NOD_Xpos,
ProcessList.NOD_Zpos,
ProcessList.NOD_Ypos,
Coalesce(A.NDOC_Order,A.Node_Order),
Coalesce(A.NDOC_DOC_ID,A.OMNIBOOK_ID)
I've seen this before when the statistics haven't kept up with the data. It's possible in this instance that SQL Server 2005 uses the statistics differently to SQL Server 2000. Try rebuilding your statistics for the tables used in the query; so for each table:
UPDATE STATISTICS <table> WITH FULLSCAN
Yes, I'd add the FULLSCAN unless you know your data well enough to think that a sample of records will give good enough results. It'll slow down the stats creation, but will make it more accurate.
Is it possible that your statistics haven't come across? in the 2k5 dbase? So the dbase doesn't have the info needed to make a good plan? As opposed to your old database which has good stats on the table and can choose a better plan for the data?
Could it be an issue with "parameter sniffing", i.e. SQL Server caching a query plan optimized for the parameters supplied for the first execution?
Microsoft technet has more
A college has come up with a solution... regarding bringing the function fn_UserCanSeeDoc back into the SQL.
Shown below is the old commented out function code, then the new inline SQL below it. The code now runs super quick (from over 1 minute to about a second)
Looking at the old SQL I'm surprised how good a job SQL2000 did of running it!
Cheers
--and dbo.fn_UserCanSeeDoc(#UserID,D.Doc_ID)<>0
-- if exists(Select 1 from Omnibook where Omnibook_ID = #DocID)
-- Begin
-- Set #ReturnVal = 1
-- End
--
-- else
-- Begin
-- if exists(
-- Select 1
-- from UserSecurityModule USM
-- Inner join DocSecurity DS
-- On USM.SecurityModuleID = DS.SecurityModuleID
-- where USM.UserID = #UserID
-- and DS.DocID = #DocID
-- )
--
-- Set #ReturnVal = 1
--
-- else
--
-- Set #ReturnVal = 0
-- End
AND D.Doc_ID IN (select DS.DocID from UserSecurityModule USM
Inner join DocSecurity DS
On USM.SecurityModuleID = DS.SecurityModuleID
where USM.UserID = #UserID)