Query fast, but when in a VIEW, it's slow - due to ROW_NUMBER
Asked Answered
G

3

6

I have a query that when run, it's result is instant.

However, I paste the exact same query into VIEW, and the results take 6 seconds to reply.

For example,

SELECT ... FROM MyTables WHERE PersonID = x

runs fast.

But create a view with:

SELECT ... FROM MyTables 

And then call the view:

SELECT * FROM MyView WHERE PersonID = x

And it runs slow.

Actual Query:

select ROW_NUMBER() over(partition by h.Id order by h.[SysStartTime]) as VersionNUmber,
      h.Id,
      fac.HIC,
      ... plus 18 other columns from the joined tables.

from   [hist].[A_View] as h
inner join [dbo].[Facilities] as fac
      on fac.Id = h.FacilityId
inner join ref.FormStatus as r_fs
      on r_fs.Id = h.FormStatusId
inner join TableA as data
      on data.Id = h.dataId
inner join Consultants as c
      on c.Id = h.ConsultantId
inner join dbo.Specialties spec
      on spec.Id = h.SpecialtyId
inner join dbo.Users modifieduser
      on modifieduser.Id = h.ModifiedByUserId
left join ref.ARefTable as r_uc
      on r_uc.Id = h.refId
cross apply [dbo].[getPersonUrn](h.PersonId, h.AnotherIdId) as PersonURN

(Note, I am changing some table names and columns as we're in quite a confidential area)

I notice that 97% of the time, it's in a Sort (Top N Sort), when executing the view. In the query, that 34%, but the plans are completely different.

I suspected parameter sniffing, but don't think that's an issue with Views.

I've actually just 'fixed' it, but no idea why.

My first column in my select is a ROW_NUMBER.

SELECT ROW_NUMBER() over(partition by h.Id order by h.[SysStartTime]) as` VersionNumber,

Removing that, and I get instant results. Not sure why, as both the columns I order by and partition by, are already in the result set.

Gentes answered 14/12, 2016 at 0:39 Comment(6)
Have you checked Execution plan ? You can include Execution plan from the Toolbar. After you run query, you can hit the execution plan tab and see whats going on.Snowonthemountain
I did that, and that's how I reported "97% of the time, it's in a Sort (Top N Sort)".Gentes
Are you using top or order by clauses? Can you post actual queries, table script and execution plans? Are you running both in ssms?Defilade
Added the actual view query.Gentes
Did you have a row_number() in the query that runs instantly? Just making sure you're comparing apples to apples.Jalapa
The version that runs slow has the ROW_NUMBER. Remove that, and then run the view, and it's slow. That query above is the query from the view.Gentes
I
6

1) Here ROW_NUMBER applies to filtered data only:

SELECT ROW_NUMBER(), ... FROM MyTables WHERE PersonID = x

At first it filters by PersonID, then it computes ROW_NUMBER

2) Here ROW_NUMBER applies to all of the data:

CREATE VIEW MyView as
  select ROW_NUMBER(), ... FROM MyTables

SELECT * FROM MyView WHERE PersonID = x

and only after proceeding full data the filter by PersonID is applied

it's the same as

SELECT * FROM
(SELECT ROW_NUMBER(), ... FROM MyTables
) t
WHERE t.PersonID = x

check out the example:

GO
CREATE VIEW dbo.test_view
AS
    SELECT ROW_NUMBER() OVER (ORDER BY NAME) rn, o.name, o.[object_id]
    FROM sys.objects o
GO
SET SHOWPLAN_XML ON
GO
SELECT rn, o.name, o.[object_id] FROM dbo.test_view o
WHERE OBJECT_ID < 100
GO
SELECT ROW_NUMBER() OVER (ORDER BY NAME) rn, o.name, o.[object_id] FROM sys.objects o
WHERE OBJECT_ID < 100
GO
SET SHOWPLAN_XML OFF
GO
DROP VIEW dbo.test_view
GO

With the view filter operation is in the very end. So plans are different actually.

Imbibe answered 14/12, 2016 at 9:1 Comment(0)
L
0

I found that running it in a view, it counts every record with ROW_NUMBER() then selects. When you run it in a query it just counts the records returning with ROW_NUMBER()

Latif answered 7/10, 2020 at 22:41 Comment(0)
N
0

I found moving all my OVER clauses outside of the primary SELECT made a huge difference in performance when calling my view with a filter. If I ran my view's code by itself with a filter, it would return instant results, but when in a view with the same filter, it would take over almost two minutes to return the same results. After moving the OVERS out like below, the filtered view runs instantly now.

Old way:

CREATE VIEW as dbo.vw_SalesTotals 
AS
SELECT 
    OrderDetails.Item
    , OrderDetails.Sales
    , OrderDetails.DocID
    , SUM(OrderDetails.Sales) OVER (PARTITION BY OrderDetails.DocID) as TotalDocSales
FROM dbo.OrderDetails

New way:

CREATE VIEW as dbo.vw_SalesTotals 
AS
SELECT 
    Ord.Item
    , Ord.Sales
    , Ord.DocID
    , SUM(Ord.Sales) OVER (PARTITION BY Ord.DocID) as TotalDocSales
FROM (
    SELECT 
        OrderDetails.Item
        , OrderDetails.Sales
        , OrderDetails.DocID
    FROM dbo.OrderDetails
) Ord
Neiman answered 4/8, 2022 at 0:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.