发布于 2014-01-24 07:54:57
这是一篇文章中相当严肃的微观优化。
我不会把它当作个人的福音,因为我曾与EF合作过很多次。当然,这些事情可能很重要,但总的来说,这是相当快的。
如果您有一个复杂的视图,然后在该视图上执行进一步的LINQ,那么当然,它可能会导致一些缓慢的表现,但我不会打赌。
这篇文章甚至没有任何长条旗!
如果性能对您的程序来说是一个严重的问题,那么缩小哪些查询是慢的,并在这里发布它们,看看SO社区是否可以帮助您优化查询。如果你问我的话,这比所有的微观优化要好得多。
发布于 2014-01-24 12:45:23
这取决于,虽然很少在很大程度上。
假设我们看到了这样的景象:
CREATE VIEW TestView
AS
Select A.x, B.y, B.z
FROM A JOIN B on A.id = B.id我们为此创建了一个实体映射。
我们还假设B.id是绑定的,因此它是不可空的,并且与A.id有一个外键关系-也就是说,每当有B行时,总是至少有一个对应的A。
现在,如果我们可以做一些类似from t in context.TestView where t.x == 3而不是from a in context.A join b in context.B on a.id equals b.id where a.x == 3 select new {a.x, b.y, b.z}的事情。
我们可以预期前者转换为SQL的速度会稍微快一些,因为这是一个稍微简单一些的查询(从Linq和SQL角度来看)。
我们可以预期后者将从一个SQL查询转换为一个SQLServer (或其他什么)内部查询,速度会稍微快一些。
我们可以预期,这个内部查询几乎是相同的,除非有什么事情变得有点奇怪。因此,我们希望那时的表现是相同的。
总之,他们之间没有太多的选择。如果我必须下一次赌注,我会在这上面下注,尤其是在第一次打电话的时候,我的视野会稍微快一些,但我不会下太多的赌注。
现在让我们考虑一下(from t in context.TestView select t.z).Distinct()。对(from b in context.B select b.z).Distinct().
这两者都应该变成一个非常简单的SELECT DISTINCT z FROM ...。
这两种方法都应该转化为表B的表扫描或索引扫描。
第一个可能不是(查询计划中的缺陷),但这将是令人惊讶的。(快速检查类似的视图确实会发现SQLServer忽略了无关的表)。
第一种方法可能需要稍长的时间来生成查询计划,因为必须推导出A.id上的联接与此无关这一事实。但是数据库服务器擅长于这类事情;这是一套计算机科学和问题,已经做了几十年的工作。
如果我不得不下一个赌注,我会把赌注押在观景上,使事情变得更慢,尽管我会更多地押注于它是如此微小的差别,以至于它消失了。对这两种查询的实际测试发现,这两种查询处于相同的差异范围内(即,这两种查询的不同时间范围相互重叠)。
在本例中,linq查询对SQL生成的影响为零(在这一点上它们实际上是相同的,但名称不同)。
让我们考虑一下是否在该视图上有一个触发器,以便插入或删除执行等效的插入或删除。在这里,我们将从使用一个SQL查询而不是使用两个(或更多)SQL查询中获得一些好处,并且更容易确保它发生在单个事务中。因此,在这种情况下,视图略有增加。
现在,让我们考虑一个更复杂的观点:
CREATE VIEW Complicated
AS
Select A.x, B.x as y, C.z, COALESCE(D.f, D.g, E.h) as foo
FROM
A JOIN B on A.r = B.f + 2
JOIN C on COALESCE(A.g, B.x) = C.x
JOIN D on D.flag | C.flagMask <> 0
WHERE EXISTS (SELECT null from G where G.x + G.y = A.bar AND G.deleted = 0)
AND A.deleted = 0 AND B.deleted = 0我们可以在linq级别完成所有这些工作。如果我们这样做了,随着查询生产的进行,它可能会有点昂贵,尽管这很少是linq查询的最昂贵的部分,尽管编译后的查询可能会平衡这一点。
我倾向于视图是更有效的方法,但如果这是我使用视图的唯一原因,我会分析一下。
现在让我们考虑:
CREATE VIEW AllAncestry
AS
WITH recurseAncetry (ancestorID, descendantID)
AS
(
SELECT parentID, childID
FROM Parentage
WHERE parentID IS NOT NULL
UNION ALL
SELECT ancestorID, childID
FROM recurseAncetry
INNER JOIN Parentage ON parentID = descendantID
)
SELECT DISTINCT (cast(ancestorID as bigint) * 0x100000000 + descendantID) as id, ancestorID, descendantID
FROM recurseAncetry从概念上讲,这个视图执行大量的选择;执行一个选择,然后根据该选择的结果递归地执行一个选择,等等,直到它有了所有可能的结果。
在实际执行中,这将转换为两个表扫描和一个懒惰的假脱机。
基于linq的等价物要重得多;实际上,您最好要么调用等效的原始SQL,要么将表加载到内存中,然后在C#中生成完整的图(但请注意,这将是对基于此的查询的浪费,而这些查询并不需要所有的东西)。
总之,在这里使用一个视图将是一个很大的节省。
总之,使用视图通常对性能的影响是可以忽略不计的,而且这种影响可以是任意一种。使用带有触发器的视图可以获得轻微的性能胜利,并通过强制在单个事务中实现数据完整性而变得更容易。在CTE中使用视图可以获得很大的性能胜利。
使用或避免视图的非性能原因是:
https://stackoverflow.com/questions/21327147
复制相似问题