SQL循环表将记录插入新表,然后获取新ID并插入其他表

时间:2019-03-26 13:38:05

标签: sql sql-server ssms

我正在迁移一个旧的遗留系统,该遗留系统的数据库表大部分都是平面的,列太多。每个新设置都需要一个新列,以此类推,表格变得越来越大。我试图将这种结构更改为关系结构,并努力将旧的现有数据迁移到新结构中。这是我们拥有的旧表的示例:

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));

此表正在迁移到多个表中,下面是一个示例:

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表中,并将相应的Setting1,Setting1Value插入新表中列SettingName和SettingValue。

如果您可以通过一个脚本帮助我实现这一目标,我将不胜感激!

4 个答案:

答案 0 :(得分:2)

处理此问题的最佳方法是不使用SCOPE_IDENTITY或其他强制您使用RBAR的方法。对您的新表进行一些细微调整会使此过程变得更简单。

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?您可以在一条语句中插入所有用户,然后通过加入此表来开始规范化过程。

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

然后,对所有属性/值组合重复此插入操作。迁移完成后,删除UserID_OLD列。

但是,您需要意识到所拥有的是实体属性值设计,并且这种类型的东西有很多陷阱。首先,您现在将所有内容都存储在varchar中,因此无法在数据库级别验证数据。您在这里还有一个性能炸弹,因为所有内容都必须不断转换为正确的数据类型。而且您必须小心转换,否则会出现转换错误。 EAV模式看起来确实很棒,但是在实践中它经常是有问题的。

答案 1 :(得分:1)

您可以使用输出子句获取新插入的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用作所有插入客户表的键

提防使用SCOPE_IDENTITY()
它可能会给您另一个您期望的ID,例如当触发器插入另一个表时,就会发生这种情况。
另外,通过使用output子句,您可以捕获更多字段,而不仅仅是ID

另请参阅
@@IDENTITY, SCOPE_IDENTITY(), OUTPUT and other methods of retrieving last identity

答案 2 :(得分:0)

如果您正在寻找一个过程,该过程将允许您从新插入的行中获取密钥,以便可以在后续代码中使用它,那么这就是您要寻找的-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, ...)

答案 3 :(得分:0)

<!DOCTYPE html>
<html>

<head>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
  <main class='container'>
    <section class="row">
      <form id='ui' class='col'>
        <fieldset class="board border border-success rounded"></fieldset>
        <section class='row'>
          <article class='col'>
            <fieldset class="btn-toolbar justify-content-between">
              <label class='btn-group'>
            <button class="btn btn-success add" type='button'>ADD</button>
            <button class="btn btn-success color" type='button'>COLOR</button>
            <button class="btn btn-success move" type='button'>MOVE</button>
            <button class="btn btn-success clear" type='reset'>CLEAR</button>
            </label>
            </fieldset>
          </article>
        </section>
      </form>
    </section>
  </main>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</body>

</html>