我有一个由Entity Framework生成的SQL查询,它包含表TableA、TableB、TableC1和TableC2上的两级嵌套LEFT OUTER JOIN,具有以下外键关系,如箭头所示。
TableA->TableB->TableC1
->TableC2
TableA包含1000行,所有其他表包含大约100000行。SQL语句如下所示:
select * from TableA A
LEFT JOIN TableB B on A.Id = B.TableAId
LEFT JOIN TableC1 C1 on B.Id = C1.TableBId
LEFT JOIN TableC2 C2 on B.Id = C2.TableBId
当SQL在Microsoft SQL Server上执行时,大约需要30秒。但是,如果我选择每个表,并在内存中以列表形式检索行,然后用C#编程连接它们,则需要3秒左右。谁能给予SQL Server为什么这么慢?
谢谢
1条答案
按热度按时间but5z9lq1#
当你在SQL中使用连接时,你创建了一个叫做笛卡尔积的东西。
例如,如果我有两个表,表A和表B,表A有10行,每行有10个表B引用:
如果我分别加载这两个表,我将加载110行,其中10行来自表A,100行来自表B。
如果我JOIN这些表,我将加载100行,但是,这100行分别表示两个表的合并数据。如果表A有10列,表B有20列,则分别加载这些表所读取的总数据将是10 x10 + 100 x20或2100列。使用JOIN,我加载了30 x100或3000列的数据,这并不是一个很大的区别,但是当我连接更多的表时,它会变得更复杂。
如果每个表B都有一个平均5行10列的表C,单独加载将增加5000(500 x10)或现在的7600列数据。在联接时,将变为3000 x5 x10或150,000列的总数据被加载到内存中或被筛选。如果您开始使用连接执行SELECT * FROM...,您应该会看到它是如何快速滚雪球的。
当EF开始构建您正在加载实体图的查询时(相关实体),所得到的查询将经常使用JOIN,从而得到它加载的这些笛卡尔结果,然后筛选以构建所得到的对象图,将结果压缩回10个A、10个B和5个C,但是它仍然需要内存和时间来咀嚼所有扁平化的结果数据。EF核心可以提供查询分割,本质上执行起来更像你的反比较,单独加载相关的表来拼凑在一起,大大减少了被读取的数据总量。
最终提高EF生成的查询的性能:
1.使用
Select
或Automapper的ProjectTo
只从相关表中选择值,而不是加载Entities /wInclude
,以便在阅读实体“集合”(如搜索结果)时立即加载相关实体。加载实体/wInclude
用于单个实体,如在更新一个实体时。1.确保在查询上述数据时,检查执行计划中的索引建议。
1.如果确实需要加载大量相关数据,请考虑使用查询拆分。