【问题标题】:how to use SCOPE_IDENTITY() not in insert如何在插入中使用 SCOPE_IDENTITY()
【发布时间】:2014-12-19 21:54:48
【问题描述】:

我想在插入之前获取新的 id(Identity)。所以,使用这个代码:

select SCOPE_IDENTITY()  AS NewId from tblName

但是得到这个:

1- 空

2- 空

【问题讨论】:

  • 为什么插入前需要值?
  • 该表中的列是否有标识规范?
  • 不,您不想在插入之前获得新身份。这样做的方法是使用 IDENT_CURRENT。然而,由于并发性,这是一个坏主意。你读过 SCOPE_IDENTITY 吗?它将返回当前范围内的最后一个标识值。如果您没有向具有标识的表中插入行,它将返回 NULL,因为没有值。
  • 为什么要向用户显示身份?
  • 好吧,如果您将其展示给用户,您需要保留该号码以停止使用它的另一个会话。最简单的方法是通过插入具有标识列的另一个表并使用从中分配的标识来对序列进行穷人的模拟。但是为什么用户在插入之前需要知道这一点呢?

标签: c# sql entity-framework sql-server-2008


【解决方案1】:

计算列版本

您必须在 sql server 上执行此操作才能添加列。

alter table TableName add Code as (name + cast(id as varchar(200)))

现在您的结果集将始终将 Code 作为名称 + id 值,这很好,因为即使字段发生更改(例如名称),此列仍将使用该表达式保持更新。


实体框架选项(不太理想)

您提到您正在使用实体框架。在插入过程中,您需要在同一记录中的字段上连接 ID。 SQL(触发器之外)或实体框架没有能力一步完成您想要的。

你需要做这样的事情:

var obj = new Thing{ field1= "some value", field2 = ""};
context.ThingTable.Add(obj);
context.SaveChanges();
obj.field2 = "bb" + obj.id; //after the first SaveChanges is when your id field would be populated
context.SaveChanges();

原始答案: 如果你真的必须向用户显示这个值,那么安全的方法是这样的:

begin tran
insert into test(test) values('this is something')
declare @pk int = scope_identity()
print @pk

您现在可以返回@pk 中的值并让用户确定它是否可以接受。如果是则发出COMMIT 否则发出ROLLBACK 命令。

然而,这不是一个很好的设计,我认为滥用了身份值的生成方式。此外,您应该知道,如果您执行回滚,则将使用的 ID 将丢失并且不会再次使用。

【讨论】:

  • 在用户提交之前锁定系统一段不确定的时间?
  • 那不会只是用挂起的插入锁定表吗?不是整个数据库。
  • 我在记录中使用了新的 id。 (Id=1,Name=aa,Code=aa1).(Id=2,Name=bb,Code=bb2)。代码 = 名称 + Id
  • 我就是这么想的。不确定为什么 OP 必须事先知道值,这对我来说没有意义。只是试图想出某种方法来实现它,即使它一开始是一个可怕的想法。
  • “触发器外没有容量”。计算列可以做到这一点。甚至不需要坚持。
【解决方案2】:

这对于评论来说太冗长了。

想想这个概念到底有多大缺陷。身份属性是尝试插入次数的运行记录。您希望将尚不存在的行的标识返回给用户。考虑一下如果插入中的值也导致它失败会发生什么。您已经告诉用户身份是什么,但插入失败,因此身份已被使用。您应该在该行实际存在时向用户报告该值,即插入之后。

【讨论】:

  • 好的,所以我将其更改为,我已在插入和新文件中使用它。请在实体框架中帮助我。但我在插入 id 之前得到 SCOPE_IDENTITY()。
  • 在插入之前不能使用 SCOPE_IDENTITY 来获取行的值。它不存在。干净利落。如果您要在与数据相同的表中使用身份,则无法解决。
  • 我在记录中使用了新的 id。 (Id=1,Name=aa,Code=aa1).(Id=2,Name=bb,Code=bb2)。代码 = 姓名 + 身份证。
  • @kakamishoo 所以你的对象上的一个字段需要与新的身份值连接?你不能以你正在尝试的方式做到这一点。您必须调用 context.SaveChanges() 然后在同一个对象实例上进行连接。然后再次 SaveChanges()。
【解决方案3】:

我不明白您为什么要在插入之前向用户显示该身份,我相信(正如@SeanLange 所说)这不是自定义且无用的,但如果您坚持我认为您可以采取一些不可靠的方式。其中之一是

  • 1) 插入新行,然后使用 SCOPE_IDENTITY() 获取 ID 并显示给用户
  • 2) 然后如果要取消操作删除行并重置
    使用DBCC CHECKIDENT('[Table Name]', RESEED, [Identity Seed]) 方法的身份(如有必要)

其他方式是不使用Identity列并自己管理id列,必须清楚这种方法不能在并发场景中工作。

【讨论】:

  • 但我在记录中使用了新的 id。 (Id=1,Name=aa,Code=aa1).(Id=2,Name=bb,Code=bb2)。代码 = 名称 + ID。在网络上使用没有问题?我该如何使用它?
  • @kakamishoo 请更多描述。我不明白你到底想做什么。如果可以,请详细说明您的问题或在此处告诉我。
  • 我在表格中有“代码”。它是=名称+ ID。例如,(1,AAA,AAA1),(2,BBB,BB2),... 所以,我应该在代码中标识。
  • 没关系。您可以按照我之前所说的插入记录,然后向用户显示,如果可以接受,则使用 id '更新' Code 列,如果不可接受,请删除并重置身份,如我之前所说。如果您满意,请接受我的帖子作为答案。
【解决方案4】:

我认为您可能将 SQL 标识与 ORACLE 序列混淆了。 它们的工作方式完全不同。 使用 ORACLE 序列,您将在插入记录之前获得序列。 使用 SQL 标识,在插入后生成的最后一个标识可通过 SCOPE_IDENTITY() 函数获得。

如果您确实需要在插入之前向用户显示 ID,最好的办法是在单独的表中保留一个计数器,并读取当前值,并将其加一。只要数字中的“差距”不是问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多