AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 155735
Accepted
ErikE
ErikE
Asked: 2016-11-19 12:03:59 +0800 CST2016-11-19 12:03:59 +0800 CST 2016-11-19 12:03:59 +0800 CST

依赖 INSERT 的 OUTPUT 子句的顺序是否安全?

  • 772

鉴于此表:

CREATE TABLE dbo.Target (
   TargetId int identity(1, 1) NOT NULL,
   Color varchar(20) NOT NULL,
   Action varchar(10) NOT NULL, -- of course this should be normalized
   Code int NOT NULL,
   CONSTRAINT PK_Target PRIMARY KEY CLUSTERED (TargetId)
);

在两个稍微不同的场景中,我想插入行并从标识列返回值。

方案 1

INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
   (VALUES
      ('Blue', 'New', 1234),
      ('Blue', 'Cancel', 4567),
      ('Red', 'New', 5678)
   ) t (Color, Action, Code)
;

方案 2

CREATE TABLE #Target (
   Color varchar(20) NOT NULL,
   Action varchar(10) NOT NULL,
   Code int NOT NULL,
   PRIMARY KEY CLUSTERED (Color, Action)
);

-- Bulk insert to the table the same three rows as above by any means

INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM #Target
;

问题

我是否可以依赖从dbo.Target表插入返回的标识值按照它们在 1)VALUES子句和 2)#Target表中存在的顺序返回,以便我可以通过它们在输出行集中的位置将它们关联回原始输入?

以供参考

下面是一些精简的 C# 代码,演示了应用程序中发生的事情(场景 1,即将转换为使用SqlBulkCopy):

public IReadOnlyCollection<Target> InsertTargets(IEnumerable<Target> targets) {
   var targetList = targets.ToList();
   const string insertSql = @"
      INSERT dbo.Target (
         CoreItemId,
         TargetDateTimeUtc,
         TargetTypeId,
      )
      OUTPUT
         Inserted.TargetId
      SELECT
         input.CoreItemId,
         input.TargetDateTimeUtc,
         input.TargetTypeId,
      FROM
         (VALUES
            {0}
         ) input (
            CoreItemId,
            TargetDateTimeUtc,
            TargetTypeId
         );";
   var results = Connection.Query<DbTargetInsertResult>(
      string.Format(
         insertSql,
         string.Join(
            ", ",
            targetList
               .Select(target => $@"({target.CoreItemId
                  }, '{target.TargetDateTimeUtc:yyyy-MM-ddTHH:mm:ss.fff
                  }', {(byte) target.TargetType
                  })";
               )
         )
      )
      .ToList();
   return targetList
      .Zip( // The correlation that relies on the order of the two inputs being the same
         results,
         (inputTarget, insertResult) => new Target(
            insertResult.TargetId, // with the new TargetId to replace null.
            inputTarget.TargetDateTimeUtc,
            inputTarget.CoreItemId,
            inputTarget.TargetType
         )
      )
      .ToList()
      .AsReadOnly();
}
sql-server sql-server-2012
  • 1 1 个回答
  • 3902 Views

1 个回答

  • Voted
  1. Best Answer
    Martin Smith
    2016-11-19T12:19:52+08:002016-11-19T12:19:52+08:00

    我是否可以依赖从 dbo.Target 表插入返回的标识值以它们在 1) VALUES 子句和 2) #Target 表中存在的顺序返回,以便我可以通过它们在输出行集中的位置来关联它们到原始输入?

    不,如果没有实际的书面保证,您就不能依赖任何保证。该文件明确指出没有这样的保证。

    SQL Server 不保证使用 OUTPUT 子句的 DML 语句处理和返回行的顺序。由应用程序包含一个适当的 WHERE 子句来保证所需的语义,或者理解当多行可能符合 DML 操作的条件时,没有保证的顺序。

    这将依赖于许多未记录的假设

    1. 从恒定扫描中输出行的顺序与 values 子句的顺序相同(我从未见过它们不同,但 AFAIK 不能保证这一点)。
    2. 插入行的顺序将与它们从恒定扫描中输出的顺序相同(绝对并非总是如此)。
    3. 如果使用“宽”(每个索引)执行计划,则输出子句中的值将从聚集索引更新运算符中提取,而不是从任何二级索引中提取。
    4. 保证以后保留该顺序 - 例如,在打包行以通过网络传输时。
    5. 即使订单现在看起来是可预测的,对并行插入等功能的实现更改也不会在未来更改订单(当前如果在 INSERT...SELECT 语句中指定了 OUTPUT 子句以将结果返回给客户端,那么并行计划是一般禁用,包括 INSERTs)

    如果向子句(Color, Action)添加 600 行,则可以看到第二点失败的示例(假设 PK 的集群)。然后计划在插入之前有一个排序运算符,因此会丢失子句VALUES中的原始顺序。VALUES

    不过,有一个记录在案的方法可以实现您的目标,这是在源代码中添加编号并使用MERGE而不是INSERT

    MERGE dbo.Target
    USING (VALUES (1, 'Blue', 'New', 1234),
                  (2, 'Blue', 'Cancel', 4567),
                  (3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
    ON 1 = 0
    WHEN NOT MATCHED THEN
      INSERT (Color,
              Action,
              Code)
      VALUES (Color,
              Action,
              Code)
    OUTPUT t.SourceId,
           inserted.TargetId; 
    

    在此处输入图像描述

    @a_horse_with_no_name

    合并真的有必要吗?你不能做一个insert into ... select ... from (values (..)) t (...) order by sourceid吗?

    是的,你可以。SQL Server 中的排序保证……声明

    使用带有 ORDER BY 的 SELECT 来填充行的 INSERT 查询保证了标识值的计算方式,但不保证插入行的顺序

    所以你可以使用

    INSERT dbo.Target (Color, Action, Code)
    OUTPUT inserted.TargetId
    SELECT t.Color, t.Action, t.Code
    FROM
    (VALUES (1, 'Blue', 'New', 1234),
            (2, 'Blue', 'Cancel', 4567),
            (3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
    ORDER BY t.SourceId
    

    在此处输入图像描述

    这将保证标识值按顺序分配,t.SourceId但不能保证它们以任何特定顺序输出,或者分配的标识列值没有间隙(例如,如果尝试并发插入)。

    • 30

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve