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( ).

https://docs.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-2017

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 模式看起来非常棒,但在实践中它经常是有问题的。