查询速度快,但在 VIEW 中时,速度很慢 - 由于 ROW_NUMBER

Cra*_*aig 4 sql sql-server sql-server-2016

我有一个查询,运行时,结果是即时的。

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
Run Code Online (Sandbox Code Playgroud)

runs fast.

But create a view with:

SELECT ... FROM MyTables 
Run Code Online (Sandbox Code Playgroud)

And then call the view:

SELECT * FROM MyView WHERE PersonID = x
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

(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,
Run Code Online (Sandbox Code Playgroud)

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.

Iva*_*tin 5

1) 这里 ROW_NUMBER 仅适用于过滤数据:

SELECT ROW_NUMBER(), ... FROM MyTables WHERE PersonID = x
Run Code Online (Sandbox Code Playgroud)

首先它按 PersonID 过滤,然后计算 ROW_NUMBER

2) 这里 ROW_NUMBER 适用于所有数据:

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

SELECT * FROM MyView WHERE PersonID = x
Run Code Online (Sandbox Code Playgroud)

并且只有在处理完完整数据后,才会应用 PersonID 过滤器

它和

SELECT * FROM
(SELECT ROW_NUMBER(), ... FROM MyTables
) t
WHERE t.PersonID = x
Run Code Online (Sandbox Code Playgroud)

查看示例:

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
Run Code Online (Sandbox Code Playgroud)

与视图filter操作是到底。所以计划实际上是不同的。