SQL 循环 table 将记录插入新 table,然后获取新 ID 并插入其他 table
SQL loop table inserting records into new table, then taking new ID and inserting into other table
我正在迁移一个旧的遗留系统,该系统主要是扁平数据库 table,列太多。每个新设置都需要一个新列等,因此 table 变得非常大。我正在尝试将此结构更改为关系结构,并且正在努力将旧的现有数据迁移到新结构中。这是我们有的旧 table 的示例:
CREATE TABLE [dbo].[User_OLD](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
[Setting1] [bit] NULL,
[Setting1Value] [int] NULL,
[Setting2] [bit] NULL,
[Setting2Value] [int] NULL,
[Setting3] [bit] NULL,
[Setting3Value] [int] NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
这个 table 正在迁移到多个 table 中,这里是一个例子:
CREATE TABLE [dbo].[User_NEW](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
CREATE TABLE [dbo].[UserSetting](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[SettingName] [varchar](250) NOT NULL,
[SettingValue] [varchar](250) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
CONSTRAINT [PK_UserSetting] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT FK_UserSetting_User FOREIGN KEY ([UserId]) REFERENCES User_NEW(Id));
所以问题是我需要从 User_OLD 获取记录并将其值插入 User_NEW,然后我需要获取 User_NEW.Id 并将其插入 UserSetting table 相应的 Setting1、Setting1Value 进入新的 tables 列 SettingName 和 SettingValue。
如果您能帮助我编写一个可以实现该目标的脚本,我将不胜感激!
如果您正在寻找一个过程,允许您从新插入的行中获取密钥,以便您可以在后续代码中使用它,那么这就是您要寻找的 - SCOPE_IDENTITY( ).
Declare PassedKey Integer;
INSERT INTO User_New values (...)
SET PassedKey = SCOPE_IDENTITY()
INSERT INTO User_Setting values (PassedKey, ...)
可以通过output子句获取新插入的id
declare @OutputTbl table (ID INT)
declare @NewUserID int
insert into User_NEW (FullName, Email)
output inserted.Id into @OutputTbl(ID)
VALUES ('john doe', 'john@somewhere.com')
select @NewUserID = ID from @OutputTbl
现在您可以使用@NewUserID 作为所有插入客户端的键 tables
小心使用SCOPE_IDENTITY()
它可以给你另一个你期望的 ID,当有一个触发器插入另一个 table.
时,就会发生这种情况。
此外,通过使用输出子句,您可以捕获更多字段,然后仅捕获 Id
另见此
@@IDENTITY, SCOPE_IDENTITY(), OUTPUT and other methods of retrieving last identity
DECLARE @FullName nvarchar(50),
@Email nvarchar(50),
@Setting1 bit,
@Setting1Value int,
@Setting2 bit,
@Setting2Value int,
@Setting3 bit,
@Setting3Value int
DECLARE @recentId INT
DECLARE C CURSOR FOR
SELECT [FullName] , [Email] ,[Setting1], [Setting1Value], [Setting2], [Setting2Value], [Setting3], [Setting3Value]
FROM @User_OLD
OPEN C
FETCH NEXT FROM C INTO @FullName, @Email ,@Setting1 ,@Setting1Value ,@Setting2 ,@Setting2Value ,@Setting3 ,@Setting3Value
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO @User_NEW(FullName, Email)
VALUES(@FullName, @Email);
SELECT @recentId = SCOPE_IDENTITY()
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting1 , @Setting1Value , GETDATE())
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting2 , @Setting2Value , GETDATE())
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting3 , @Setting3Value , GETDATE())
FETCH NEXT FROM C INTO @FullName, @Email ,@Setting1 ,@Setting1Value ,@Setting2 ,@Setting2Value ,@Setting3 ,@Setting3Value
END
CLOSE C
DEALLOCATE C
处理此问题的最佳方法是不使用 SCOPE_IDENTITY 或其他强制您为此使用 RBAR 的方法。对您的新 table 稍作调整,这会变得更简单一些。
CREATE TABLE [dbo].[User_NEW](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
UserID_OLD int not null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
注意到新列 UserID_OLD 了吗?您可以在一条语句中插入所有用户,然后通过加入此 table.
来启动规范化过程
insert User_NEW
(
FullName
, Email
, UserID_OLD
)
select FullName
, Email
, Id
from User_OLD
insert UserSetting
(
UserId
, SettingName
, SettingValue
)
select Setting1
, Setting1Value
, un.Id
from User_OLD u
join User_NEW un on un.UserID_OLD = u.Id
然后您只需对所有 attribute/value 组合重复此插入。并在迁移完成后删除 UserID_OLD 列。
但是你需要意识到你所拥有的是一个实体属性值设计,这种类型的东西有很多陷阱。首先,您现在已将所有内容存储在 varchar 中,因此您无法在数据库级别验证数据。这里还有一个性能定时炸弹,因为所有内容都必须不断地转换为正确的数据类型。而且你必须小心你的转换,否则你会得到转换错误。 EAV 模式看起来非常棒,但在实践中它经常是有问题的。
我正在迁移一个旧的遗留系统,该系统主要是扁平数据库 table,列太多。每个新设置都需要一个新列等,因此 table 变得非常大。我正在尝试将此结构更改为关系结构,并且正在努力将旧的现有数据迁移到新结构中。这是我们有的旧 table 的示例:
CREATE TABLE [dbo].[User_OLD](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
[Setting1] [bit] NULL,
[Setting1Value] [int] NULL,
[Setting2] [bit] NULL,
[Setting2Value] [int] NULL,
[Setting3] [bit] NULL,
[Setting3Value] [int] NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
这个 table 正在迁移到多个 table 中,这里是一个例子:
CREATE TABLE [dbo].[User_NEW](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
CREATE TABLE [dbo].[UserSetting](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[SettingName] [varchar](250) NOT NULL,
[SettingValue] [varchar](250) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
CONSTRAINT [PK_UserSetting] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT FK_UserSetting_User FOREIGN KEY ([UserId]) REFERENCES User_NEW(Id));
所以问题是我需要从 User_OLD 获取记录并将其值插入 User_NEW,然后我需要获取 User_NEW.Id 并将其插入 UserSetting table 相应的 Setting1、Setting1Value 进入新的 tables 列 SettingName 和 SettingValue。
如果您能帮助我编写一个可以实现该目标的脚本,我将不胜感激!
如果您正在寻找一个过程,允许您从新插入的行中获取密钥,以便您可以在后续代码中使用它,那么这就是您要寻找的 - SCOPE_IDENTITY( ).
Declare PassedKey Integer;
INSERT INTO User_New values (...)
SET PassedKey = SCOPE_IDENTITY()
INSERT INTO User_Setting values (PassedKey, ...)
可以通过output子句获取新插入的id
declare @OutputTbl table (ID INT)
declare @NewUserID int
insert into User_NEW (FullName, Email)
output inserted.Id into @OutputTbl(ID)
VALUES ('john doe', 'john@somewhere.com')
select @NewUserID = ID from @OutputTbl
现在您可以使用@NewUserID 作为所有插入客户端的键 tables
小心使用SCOPE_IDENTITY()
它可以给你另一个你期望的 ID,当有一个触发器插入另一个 table.
时,就会发生这种情况。
此外,通过使用输出子句,您可以捕获更多字段,然后仅捕获 Id
另见此
@@IDENTITY, SCOPE_IDENTITY(), OUTPUT and other methods of retrieving last identity
DECLARE @FullName nvarchar(50),
@Email nvarchar(50),
@Setting1 bit,
@Setting1Value int,
@Setting2 bit,
@Setting2Value int,
@Setting3 bit,
@Setting3Value int
DECLARE @recentId INT
DECLARE C CURSOR FOR
SELECT [FullName] , [Email] ,[Setting1], [Setting1Value], [Setting2], [Setting2Value], [Setting3], [Setting3Value]
FROM @User_OLD
OPEN C
FETCH NEXT FROM C INTO @FullName, @Email ,@Setting1 ,@Setting1Value ,@Setting2 ,@Setting2Value ,@Setting3 ,@Setting3Value
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO @User_NEW(FullName, Email)
VALUES(@FullName, @Email);
SELECT @recentId = SCOPE_IDENTITY()
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting1 , @Setting1Value , GETDATE())
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting2 , @Setting2Value , GETDATE())
INSERT INTO @UserSetting(UserId, SettingName, SettingValue, CreatedOn)
VALUES(@recentId, @Setting3 , @Setting3Value , GETDATE())
FETCH NEXT FROM C INTO @FullName, @Email ,@Setting1 ,@Setting1Value ,@Setting2 ,@Setting2Value ,@Setting3 ,@Setting3Value
END
CLOSE C
DEALLOCATE C
处理此问题的最佳方法是不使用 SCOPE_IDENTITY 或其他强制您为此使用 RBAR 的方法。对您的新 table 稍作调整,这会变得更简单一些。
CREATE TABLE [dbo].[User_NEW](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FullName] [nvarchar](50) NULL,
[Email] [nvarchar](50) NULL,
UserID_OLD int not null
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC));
注意到新列 UserID_OLD 了吗?您可以在一条语句中插入所有用户,然后通过加入此 table.
来启动规范化过程insert User_NEW
(
FullName
, Email
, UserID_OLD
)
select FullName
, Email
, Id
from User_OLD
insert UserSetting
(
UserId
, SettingName
, SettingValue
)
select Setting1
, Setting1Value
, un.Id
from User_OLD u
join User_NEW un on un.UserID_OLD = u.Id
然后您只需对所有 attribute/value 组合重复此插入。并在迁移完成后删除 UserID_OLD 列。
但是你需要意识到你所拥有的是一个实体属性值设计,这种类型的东西有很多陷阱。首先,您现在已将所有内容存储在 varchar 中,因此您无法在数据库级别验证数据。这里还有一个性能定时炸弹,因为所有内容都必须不断地转换为正确的数据类型。而且你必须小心你的转换,否则你会得到转换错误。 EAV 模式看起来非常棒,但在实践中它经常是有问题的。