Sql 如何将值插入到具有外键关系的两个表中?

Sql 如何将值插入到具有外键关系的两个表中?,sql,sql-server,database,sql-server-2014,Sql,Sql Server,Database,Sql Server 2014,我创建了两个表: 表tblStaff包含列id主键、自动递增、名称、年龄、地址 表tblRoleOfStaff,列id为主键,自动递增,tblStaff的StaffId外键,RoleId 我有一个表格,可以用现有的角色创建新员工。要插入的数据示例: (name, age, address, roleId) = ('my name',20,'San Jose', 1) 我想在SQL Server 2014中编写一个存储过程,将新员工插入到tblStaff中,并将新记录插入到tbleRoleOfS

我创建了两个表:

表tblStaff包含列id主键、自动递增、名称、年龄、地址

表tblRoleOfStaff,列id为主键,自动递增,tblStaff的StaffId外键,RoleId

我有一个表格,可以用现有的角色创建新员工。要插入的数据示例:

(name, age, address, roleId) = ('my name',20,'San Jose', 1)
我想在SQL Server 2014中编写一个存储过程,将新员工插入到tblStaff中,并将新记录插入到tbleRoleOfStaff中,其中包含我刚刚插入的staffId

我该怎么办

如果我的问题与其他问题重复,我很抱歉。我是SQL的新手。 感谢您的帮助。

使用第二个插入到StaffId位置的TBLROLEOFTUF中。比如:

编辑

对这个答案有太多的评论,所以我想给出一个解释

如果OP保证他不会使用任何触发器,他可能会使用@IDENTITY错误实践,这足以满足他的需要,但最好使用SCOPE\u IDENTITY

作用域\标识,如@@IDENTITY,将返回在当前会话中创建的最后一个标识值,但它也将其限制在当前作用域内。换句话说,它将返回您显式创建的最后一个标识值,而不是由触发器或用户定义函数创建的任何标识

SCOPE_IDENTITY将保证您从当前操作获得标识,而不是从另一个连接或最后一个处理的连接获得标识

为什么不识别当前的?因为

当前标识不受范围和会话的限制;它仅限于指定的表。IDENT_CURRENT返回为任何会话和任何作用域中的特定表生成的标识值

因此,您将take设置为最后一个作用域,而不是当前作用域。是的,OP也可以使用它,但在那种情况下,如只使用@@IDENTITY,这是一种不好的做法


使用输出确实是一种很好的做法,但对于一个标识来说过于复杂。如果OP一次需要处理多于一行的数据-是的,他需要输出。

在插入到第一个表后,使用

声明@staffId INT SET@staffId=按id描述从tblStaff订单中选择前1个id 插入到tblRoleOfStaff staffId中,roleId值@staffId,2
因为您似乎一次讨论一行,有些人可能会告诉您使用系统变量,如@IDENTITY或其他一些变量,但为了确保更明确,我建议使用insert语句的OUTPUT子句。这种方法的优点是它可以很容易地适应于一次处理多行

DECLARE @Output AS TABLE (StaffId INT)

INSERT INTO tblStaff (name, age, address)
OUTPUT inserted.Id INTO @Output (StaffId)
VALUES (@name, @age, @address)

DECLARE @StaffId INT
SELECT @StaffId = StaffId FROM @Output

INSERT INTO tblRoleOfStaff (StaffId, RoleId)
VALUES (@StaffId,@RoleId)
在执行与您的链接的其他操作时不使用@标识的原因。例如,触发器将另一行插入到另一个表中,或更新数据库中的另一条记录。当触发器修改同一个表时,SCOPE_IDENTITY也有类似的不足。识别电流也有短路。进行internet搜索以了解更多信息。在这些方面有大量优秀的资源。

您可以使用第一条insert语句的输出

declare @tmp table(id int)
insert tblStaff (name, age, address)
OUTPUT inserted.Id INTO @tmp (id)
values (@name, @age, @address)

declare @roleId int = 1 --or whatever
insert tblRoleOfStaff (staffId,roleId)
select id, @roleId
from @tmp
您还可以同时插入多个角色

create table Roles (roleId int identity(1,1) primary key, 
RoleName varchar(50),
isDefaultRole bit default 0
)
--mark some roles as default (`isDefaultRole = 1`)
--the 2nd insert will be
insert tblRoleOfStaff (staffId,roleId)
select id, roleId
from @tmp
cross join Roles
where isDefaultRole = 1
试试这个:

Create Procedure Pro_XXX()
AS
BEGIN

INSERT INTO tblStaff (name, age, address, roleId) VALUES ('my name',20,'San Jose', 1);

INSERT INTO tbleRoleOfStaff VALUES (staffId, roleId) VALUES (IDENT_CURRENT('tblStaff'),0)

END

请注意IDENT_CURRENT、SCOPE_IDENTITY和@IDENTITY之间的区别。阅读相关内容

如果一次只插入一个员工行,可以执行以下操作:

begin try
 begin tran
  insert into tblStaff (name, age, address) values('my name',20,'San Jose');
  insert into tbleRoleOfStaff (StaffId, RoleId) values (SCOPE_IDENTITY(), 1); 
 commit
end try
begin catch
  IF @@trancount > 0 ROLLBACK;
end catch

SQL Server没有自动增量。您确定知道您正在使用的数据库吗?感谢Gordon Linoff,我正在使用SQL Server Management Studio,我将Id列的类型设置为int,然后手动设置标识,标识增量的值为1。这通常在SQL Server中称为标识列;MySQL中的一个自动递增列;Postgres中的连续列。它们都是一样的,但每个数据库都有不同的语法。大多数情况下@@identity可能会得到您想要的,但搜索@@identity,SCOPE_IDENTITY等。编写触发器或多语句存储过程非常容易,这将搞乱您真正想要获取的标识。OP问题是关于4个字段的简单表单@@在那种情况下,身份是他所需要的。如果他有插入大量数据的内容-是,@@IDENTITY不够。我建议使用SCOPE_IDENTITY,而不是像@IDENTITY这样的任何东西来获取新插入的标识值@谢谢你的解释,我把答案改成了范围_IDENTITY@VahidFarahmandian是的,这对繁忙的系统不好,我已经在上面的评论中写过了,但这里有一个简单的表单,有4个字段,SCOPE_标识就足够了。这在繁忙的系统中不起作用!您不能依赖最后插入的行具有最高Id这一事实-在您有机会读取该值之前,可能已经进行了其他插入!同意。但考虑到表结构和这是一个全新的SQL测试,我并不担心系统繁忙。我的错。@Sam,虽然很明显这在繁忙的系统上不起作用,但也是巧合
也可能出现,使用户的系统中断。或许可以用正确的方式来解释,对吗?Pinal Dave实际上在您链接的那篇文章中建议使用SCOPE_标识:为了避免以后添加触发器时可能出现的问题,请始终使用SCOPE_标识返回T SQL语句或存储过程中最近添加的行的标识。
begin try
 begin tran
  insert into tblStaff (name, age, address) values('my name',20,'San Jose');
  insert into tbleRoleOfStaff (StaffId, RoleId) values (SCOPE_IDENTITY(), 1); 
 commit
end try
begin catch
  IF @@trancount > 0 ROLLBACK;
end catch