【问题标题】:How to bulk insert data into two SQL tables如何将数据批量插入到两个 SQL 表中
【发布时间】:2015-02-10 05:45:19
【问题描述】:

我的任务是读取一个大文件并将包含的数据插入 SQL 数据库。在“真实”任务中,几个表中有 10 列,但为了便于讨论,我将使用更简单的模式。

数据如下所示: FName、LName、地址。

因此,我声明了一个 SQL 类型 BulkInsertData 来保存这三个字段,以便它们可以作为表值参数传入。

这是我的桌子:

dbo.Person
---+-------+-------+----------+
Id | Fname | Lname | AddressId|
---+-------+-------+----------+
 1 | Bob   | Smith |      42  |
 2 | Sue   | Baker |     234  |
---+-------+-------+----------+

dbo.Address
----+----------------------+
 Id | Address              |
----+----------------------+
 42 | 1600 Pennsylvania Ave|
234 | 10 Downing St        |
----+----------------------+

我的目标是在 dbo.Person 表中插入名字和姓氏,在 dbo.Address 表中插入地址,以及正确的外键。将数据插入多个表很容易,但我被外键关系卡住了。

如果我只插入一条记录,我可能会将地址插入到 dbo.Address 表中,然后使用SELECT @@IDENTITY 获取要插入到 dbo.Person 的新地址的 ID 以及第一个和姓氏。

这里,Does SQL Server have something like @@IDENTITY that returns multiple values?,另一个 Stacker 向我解释说 INSERT 语句与 OUTPUT 语句一起使用以保存有关多个插入行的信息。

我想我想做的是有一个临时表,它不仅可以保存传入的数据,还可以保存与每个人关联的主键和外键:一个可以保存 Fname、Lname、地址、PersonId 和 AddressId。

但是,据我所知,我可以使用 OUTPUT 关键字在临时表中创建新行,但我不知道如何使用它来修改现有行。

我感觉这个任务应该不难,那我该怎么做呢?

我的 SQL 服务器是 MS SQL Server 2008。

如果我把下面的数据...

Adam, Dumas, 300 Broadway
Greg, Ho, 213 Main St

并将其批量插入我上面描述的表中,结果应该如下所示:

dbo.Person
---+-------+-------+----------+
Id | Fname | Lname | AddressId|
---+-------+-------+----------+
 1 | Bob   | Smith |      42  |
 2 | Sue   | Baker |     234  |
 3 | Adam  | Dumas |     501  |
 4 | Greg  | Ho    |     502  |
---+-------+-------+----------+

dbo.Address
----+----------------------+
 Id | Address              |
----+----------------------+
 42 | 1600 Pennsylvania Ave|
234 | 10 Downing St        |
501 | 300 Broadway         |
502 | 213 Main St          |
----+----------------------+

【问题讨论】:

    标签: sql database sql-server-2008


    【解决方案1】:
    CREATE TABLE TVP_ADDRESS
    (
     ID INT IDENTITY(1,1) PRIMARY KEY,ADDRESS VARCHAR(100)
    )
    
    CREATE TABLE TVP_PERSON
    (
     ID INT IDENTITY(1,1) PRIMARY KEY,FNAME VARCHAR(100),LNAME VARCHAR(100),ADDRESS_ID INT,CONSTRAINT fk_address_id_tvp_address FOREIGN KEY(address_id) REFERENCES tvp_address(id) ON DELETE CASCADE
    )
    
    CREATE TYPE TVPDATA
    AS 
    TABLE
    (
     FNAME VARCHAR(100),LNAME VARCHAR(100),ADDRESS VARCHAR(100)
    )
    GO
    CREATE PROCEDURE inser_tvpdata
    (
     @TVP  TVPDATA READONLY
    )
    AS 
    BEGIN
        DECLARE @T TABLE(ID INT,ADDRESS VARCHAR(100))
    
    --first insert address get ids
    
        INSERT INTO TVP_ADDRESS(address) OUTPUT Inserted.* INTO @T
        SELECT DISTINCT
         t.address
        FROM @TVP t --LEFT OUTER JOIN TVP_address td ON t.address = td.address
        --WHERE td.address IS NULL 
        -- insert persons and get new ids 
    
        INSERT INTO tvp_person(Fname,lname,address_id)
        SELECT t.fname,t.lname,temp.id
        FROM @TVP t
        INNER JOIN @T temp ON temp.ADDRESS=t.address
    END
    GO
    DECLARE @t  TVPDATA
    
    INSERT INTO @t(fname,lname,address) VALUES ('john','r','test'),('steve','r','test2')
    
    EXEC inser_tvpdata @t
    
    SELECT *
    FROM TVP_ADDRESS
    
    SELECT *
    FROM TVP_PERSON
    

    【讨论】:

    • SQL 可以通过不在一行上做这么多事情来提高可读性,但其他方面看起来不错。 +1
    • 我错了,还是这段代码假定Address 中的值是唯一的?我不能做出这样的假设。
    • 我的做法有点不同,但你让我指出了正确的方向。谢谢!
    • @DanielAllenLangdon 你最后是怎么做到的?当您的问题中的地址不是唯一时,我正在寻找类似情况的解决方案/
    猜你喜欢
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 1970-01-01
    • 2017-03-23
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多