假设我有4个表:PostsLikesCommentsFiles.现在我想 Select 一个特定帖子的所有喜欢, comments 和文件,比如在实体框架中使用LINQ.这很简单,只要做这样的事情:

var post = m_dbContext.Posts
                      .Include(x => x.Likes)
                      .Include(x => x.Comments.OrderBy(c => c.CreatedAt))
                      .Include(x => x.Files)
                      .FirstOrDefault(x => x.Id == id);

现在假设我们的帖子有100条赞记录、10条 comments 和5个文件.数据库的结果将有100x10x5=5k行.值得注意的是,对于Like和Comment的每个组合,所有选定的文件记录都将被复制.可以通过在SQL查看器中执行此查询来确认这一点.

最后,让我们假设每个文件记录平均有100KB的数据(比方说一个Base64编码的字符串).

这是否意味着,当执行上述查询时,由于联接导致的行"冗余",数据库将不得不通过网络将价值超过5k x 100kb=500MB的数据从数据库传输到服务器?或者,在数据库发送非冗余数据而实体框架将重新构建它的幕后,是否存在一些聪明的做法?当我手动执行查询时,我在UI中只得到了5k行.

我在哪里可以了解到关于这个问题的更多信息?

推荐答案

It depends if you are using 100 or not.这取决于您的全局配置以及文档中记录的AsSplitQuery()的每个查询使用情况.

EF将把你的查询,连同它的Include,转换成一个SQL语句.然后由服务器决定如何处理该语句.

如果您没有使用拆分查询,则会得到您所怀疑的笛卡尔交叉联接,这可能会非常低效.但在某些情况下会更好:例如,如果各种联接没有很好地建立索引.

您需要判断正在执行的实际SQL,但我希望看到类似以下内容的结果:

SELECT
  p.*,
  l.*,
  c.*,
  f.*
FROM (
    SELECT TOP (1)
      p.*
    FROM Posts p
    WHERE p.Id = @id
) p
JOIN Likes l ON ...
JOIN Comments c ON ...
JOIN Files f ON ...
ORDER BY c.CreatedAt;

您应该判断查询计划以确保它是有效的.如果正如您所怀疑的那样,查询只是一个巨大的连接,那么在您的例子中,它将是缓慢的.

如果您使用拆分查询(这是EF的现代版本中的默认设置),那么您将得到四个单独的SELECT个查询,每个表一个,但假设索引良好,则这将更加高效,因为它避免了笛卡尔交叉联接.

为了确保无论您的EF核心配置如何,都可以获得拆分查询,您可以使用.AsSplitQuery().您可以全局启用,如下所示

optionsBuilder
    .UseSqlServer(
            @"SomeConnectionStringHere",
            o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));

Sql相关问答推荐

PostgreSQL:获取每家店铺收入最高的员工

使用SQL/R循环查找邻居

JSON列之间的Postgr聚合

如何用3个(半)固定位置建模团队,并有效地搜索相同/不同的团队?

每组显示一行(表1中的分组值),表2中的不同列表用逗号分隔

具有多个条件的SQL否定

数据库SQL-CTE命名空间(错误?)使用临时视图

SQL数据库规范化与数据插入

在Oracle SQL中按月生成日期

为什么SQL in中的空子查询有时被视为null

如何创建snowflake表(动态查找数据类型)并从阶段加载(AWS S3)?

如何使用最后一个非 NULL 值在 PostgreSQL 列中填充 NULL 值

计算组内多个日期间隔go 年的累计天数

计算 ID 满足条件的次数

SQL Server:时态表并在运行时添加计算列

使用row_number() over partition by保留首次出现且值不为空的行的方法

如何获取每个组中最近的n条记录并将它们聚合成数组

根据行号将列转置为没有任何id或键列的行

SQL - 只需要 GROUP BY SELECT 的一列

为每组填写行以进行旋转