EF Core Insert 操作 - SQL 命令如何工作?

EF Core Insert operation - how does the SQL command work?

以下 SQL 命令如何工作?

exec sp_executesql N'SET NOCOUNT ON;
DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);
MERGE [OrderLine] USING (
VALUES (@p1, @p2, 0),
(@p3, @p4, 1),
(@p5, @p6, 2),
(@p7, @p8, 3)) AS i ([Item], [OrderId], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Item], [OrderId])
VALUES (i.[Item], i.[OrderId])
OUTPUT INSERTED.[Id], i._Position
INTO @inserted0;

SELECT [t].[Id] FROM [OrderLine] t
INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])
ORDER BY [i].[_Position];

',N'@p1 nvarchar(64),@p2 int,@p3 nvarchar(64),@p4 int,@p5 nvarchar(64),@p6 int,@p7 nvarchar(64),@p8 int',@p1=N'Item-1',@p2=1,@p3=N'Item-2',@p4=1,@p5=N'Item-3',@p6=1,@p7=N'Item-4',@p8=1

(由 EF Core 生成并插入一些 OrderLine 个实体)。

编辑:
我了解 TABLE 类型变量的声明,并对 MERGE 操作有基本的了解。但是很难理解实际数据是如何以及何时插入 OrderLine table.

how and when actually data gets inserted in the OrderLine table.

有效位是ON 1=0

这意味着行永远不会匹配,因此每一行都会转到

WHEN NOT MATCHED THEN
INSERT ([Item], [OrderId])
VALUES (i.[Item], i.[OrderId])

这是一个有趣的问题,值得用整篇文章来回答。幸运的是,Brent Ozar 写道 The Case of Entity Framework Core’s Odd SQL.

MERGE 语句基本上做了 INSERT ... OUTPUT inserted.ID VALUES (),(),() 会做的事情。 ON 1=0 子句确保只执行 INSERT 分支。那么为什么要使用如此复杂的语法呢?

这个奇怪 SQL 的原因是批量插入的性能。具体来说,10K 行的性能提高了 248%。

只有几种方法可以在 table 中插入多行:

  • 您可以在一个长批中编写 5000 个 INSERT 查询,但那是。比单个大 INSERT 慢近 5000 倍,因为每个语句都必须修改索引等。
  • 您可以传递一个 table-type 参数。但这也很慢,因为服务器无法知道该参数中有多少项并假定只有一行。这可能会导致非常糟糕的执行计划。
  • 存储在table变量中?与 table 参数
  • 相同的问题
  • 您可以先将所有这些行写入临时 table,然后将它们插入到目标中,但这有其自身的问题 - table 应该命名为什么?它是独一无二的吗?

ID 呢?

table变量用于收集生成的ids,_Position用于在并行执行的情况下保持顺序。我怀疑,这就是 INSERT VALUES 未被使用的原因。

由于 EF Core 刚刚在一个批次中发送了 5000 个项目,它需要一种方法来检索 5000 个新 ID,以允许它识别这些 ID 属于哪些对象。通常,人们会使用 ID 来标识行,但没有 ID 开头!

唯一剩下的就是 return 以对象插入的顺序排列 ID。 INSERT OUTPUT VALUES 保证 - 如果没有 ORDER 子句,服务器可以以最便宜的方式免费 return 数据。

在这种情况下,保留 ID 和顺序的唯一安全方法是在 @inserted0 中使用明确的 _Position 值存储它们,并按此顺序 return 它们。

结论

这关乎性能,正如 Brent Ozar 所说:

So yes, the SQL isn’t perfect, but it’s 248% faster.

Brent says: hoo, boy. This is…not ideal.