使用外键批量插入嵌套的xml作为第一个表的标识列

尼克希尔

我有一个XML,如下所示:

<Records>
  <Record>
    <Name>Best of Pop</Name>
    <Studio>ABC studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>John</ArtistName>
        <Age>36</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Jessica</ArtistName>
        <Age>20</Age>            
      </Artist>
    </Artists>
  </Record>
  <Record>
    <Name>Nursery rhymes</Name>
    <Studio>XYZ studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>Judy</ArtistName>
        <Age>10</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Rachel</ArtistName>
        <Age>15</Age>            
      </Artist>
    </Artists>
  </Record>
</Records>

此文件可能包含数百万条记录。运行在Azure SQL数据库上的我的MS SQL数据库具有以下2个表来存储这些记录:

  1. Record (RecordId [PK,身份,自动递增],名称,工作室)

  2. Artist (RecordId [外键指的是Record.RecordId],ArtistName,Age)

是否可以使用xml节点方法将记录批量插入Record表中,获取RecordIds,然后将艺术家信息批量插入Artistxml中?

很长时间以来,我一直在寻找一种有效的方法来这样做,但徒劳无功。

我尝试了与此处此处所述的方法类似的方法,但是无法找到解决方案。

任何指向解决方案方向的指示都将有很大帮助。

更新:@srutzky:谢谢您的解决方案。这完全符合我的要求。但是有一个陷阱。我必须使用节点方法来解决。我已经更改了查询的第一部分。但是我被困在第二半。这就是我要做的。

DECLARE @Record TABLE (RecordId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       Name NVARCHAR(400) UNIQUE,
                       Studio NVARCHAR(400));
DECLARE @Artist TABLE (ArtistId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       RecordId INT NOT NULL,
                       ArtistName NVARCHAR(400), Age INT);

INSERT INTO @Record (Name, Studio)
   SELECT  T.c.value(N'(Name/text())[1]', 'NVARCHAR(400)'),
           T.c.value(N'(Studio/text())[1]', 'NVARCHAR(400)')
 FROM @ImportData.nodes('/Records/Record') T(c);

SELECT * FROM @Record

您能帮我解决第二部分吗?我是这种xml处理方法的新手。

UPDATE2:我明白了。。。我绞尽脑汁几个小时,尝试了几件事,终于找到了解决方案。

DECLARE @Record TABLE (RecordId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       Name NVARCHAR(400) UNIQUE,
                       Studio NVARCHAR(400));
DECLARE @Artist TABLE (ArtistId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       RecordId INT NOT NULL,
                       ArtistName NVARCHAR(400), 
                       Age INT);

INSERT INTO @Record (Name, Studio)
   SELECT  T.c.value(N'(Name/text())[1]', 'NVARCHAR(400)'),
           T.c.value(N'(Studio/text())[1]', 'NVARCHAR(400)')
 FROM @ImportData.nodes('/Records/Record') T(c);

INSERT INTO @Artist (RecordId, ArtistName, Age)
    SELECT  (SELECT RecordId FROM @Record WHERE Name=T.c.value(N'(../../Name/text())[1]', 'NVARCHAR(400)')),
            T.c.value(N'(ArtistName/text())[1]', 'NVARCHAR(400)'),
           T.c.value(N'(Age/text())[1]', 'INT')
 FROM @ImportData.nodes('/Records/Record/Artists/Artist') T(c);

 SELECT * FROM @Record
 SELECT * FROM @Artist

@srutzky:非常感谢您向我指出了正确的方向。欢迎提出任何改进此解决方案的建议。

所罗门·鲁兹基(Solomon Rutzky)

无论如何,这不可能一次完成,因为您不能在同一DML语句中插入两个表中(当然,在Triggers和OUTPUT子句之外,这两个都不起作用)。但是可以通过两次有效地完成。关键是<Name>元素内的事实<Record>是唯一的,因为这使我们可以将该Record表用作第二遍的查找表(即,当我们获取Artist行时)。

首先,您需要(当然,应该)创建UNIQUE INDEXon Record (Name ASC)在下面的示例中,我使用UNIQUE CONSTRAINT,但这仅是由于我使用表变量而不是临时表来使示例代码更易于重新运行(顶部不需要显式的IF EXISTS DROP)。该索引将有助于第二遍的性能。

该示例使用OPENXML,.nodes()因为与同一函数相比,它很有可能会更高效,因为同一文档需要遍历两次。OPENXML函数的最后一个参数,用于2指定文档是“基于元素”的,因为默认解析正在寻找“基于属性”。

DECLARE @DocumentID INT, @ImportData XML;

SET @ImportData = N'
<Records>
  <Record>
    <Name>Best of Pop</Name>
    <Studio>ABC studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>John</ArtistName>
        <Age>36</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Jessica</ArtistName>
        <Age>20</Age>            
      </Artist>
    </Artists>
  </Record>
  <Record>
    <Name>Nursery rhymes</Name>
    <Studio>XYZ studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>Judy</ArtistName>
        <Age>10</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Rachel</ArtistName>
        <Age>15</Age>            
      </Artist>
    </Artists>
  </Record>
</Records>';


DECLARE @Record TABLE (RecordId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       Name NVARCHAR(400) UNIQUE,
                       Studio NVARCHAR(400));
DECLARE @Artist TABLE (ArtistId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                       RecordId INT NOT NULL,
                       ArtistName NVARCHAR(400), Age INT);

EXEC sp_xml_preparedocument @DocumentID OUTPUT, @ImportData;

-- First pass: extract "Record" rows
INSERT INTO @Record (Name, Studio)
   SELECT Name, Studio
   FROM   OPENXML (@DocumentID, N'/Records/Record', 2) 
             WITH (Name    NVARCHAR(400)  './Name/text()', 
                   Studio  NVARCHAR(400)  './Studio/text()');


-- Second pass: extract "Artist" rows
INSERT INTO @Artist (RecordId, ArtistName, Age)
   SELECT rec.RecordId, art.ArtistName, art.Age
   FROM   OPENXML (@DocumentID, N'/Records/Record/Artists/Artist', 2) 
             WITH (Name        NVARCHAR(400)  '../../Name/text()',
                   ArtistName  NVARCHAR(400)  './ArtistName/text()', 
                   Age         INT  './Age/text()') art
   INNER JOIN @Record rec
           ON rec.[Name] = art.[Name];


EXEC sp_xml_removedocument @DocumentID;
-------------------

SELECT * FROM @Record ORDER BY [RecordID];
SELECT * FROM @Artist ORDER BY [RecordID];

参考:

编辑:
使用.nodes()功能代替的新要求OPENXML,以下将起作用:

DECLARE @ImportData XML;

SET @ImportData = N'
<Records>
  <Record>
    <Name>Best of Pop</Name>
    <Studio>ABC studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>John</ArtistName>
        <Age>36</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Jessica</ArtistName>
        <Age>20</Age>            
      </Artist>
    </Artists>
  </Record>
  <Record>
    <Name>Nursery rhymes</Name>
    <Studio>XYZ studio</Studio>
    <Artists>
      <Artist>
        <ArtistName>Judy</ArtistName>
        <Age>10</Age>            
      </Artist> 
      <Artist>
        <ArtistName>Rachel</ArtistName>
        <Age>15</Age>            
      </Artist>
    </Artists>
  </Record>
</Records>';

IF (OBJECT_ID('tempdb..#Record') IS NOT NULL)
BEGIN
   DROP TABLE #Record;
END;
IF (OBJECT_ID('tempdb..#Artist') IS NOT NULL)
BEGIN
   DROP TABLE #Artist;
END;

CREATE TABLE #Record (RecordId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                      Name NVARCHAR(400) UNIQUE,
                      Studio NVARCHAR(400));
CREATE TABLE #Artist (ArtistId INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
                      RecordId INT NOT NULL,
                      ArtistName NVARCHAR(400),
                      Age INT);


-- First pass: extract "Record" rows
INSERT INTO #Record (Name, Studio)
   SELECT col.value(N'(./Name/text())[1]', N'NVARCHAR(400)') AS [Name],
          col.value(N'(./Studio/text())[1]', N'NVARCHAR(400)') AS [Studio]
   FROM   @ImportData.nodes(N'/Records/Record') tab(col);


-- Second pass: extract "Artist" rows
;WITH artists AS
(
   SELECT col.value(N'(../../Name/text())[1]', N'NVARCHAR(400)') AS [RecordName],
          col.value(N'(./ArtistName/text())[1]', N'NVARCHAR(400)') AS [ArtistName],
          col.value(N'(./Age/text())[1]', N'INT') AS [Age]
   FROM   @ImportData.nodes(N'/Records/Record/Artists/Artist') tab(col)
)
INSERT INTO #Artist (RecordId, ArtistName, Age)
   SELECT rec.RecordId, art.ArtistName, art.Age
   FROM artists art
   INNER JOIN #Record rec
           ON rec.[Name] = art.RecordName;

-- OR --
-- INSERT INTO #Artist (RecordId, ArtistName, Age)
   SELECT rec.RecordId,
          col.value(N'(./ArtistName/text())[1]', N'NVARCHAR(400)') AS [ArtistName],
          col.value(N'(./Age/text())[1]', N'INT') AS [Age]
   FROM   @ImportData.nodes(N'/Records/Record/Artists/Artist') tab(col)
   INNER JOIN #Record rec
           ON rec.Name = col.value(N'(../../Name/text())[1]', N'NVARCHAR(400)');

-------------------

SELECT * FROM #Record ORDER BY [RecordID];
SELECT * FROM #Artist ORDER BY [RecordID];

#Artist上面显示了两个插入选项第一个使用CTE从INSERT / SELECT查询中提取XML提取。另一个是简化版本,类似于问题UPDATE 2中的查询。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何将一个表中的主键作为外键插入到辅助表中

来自分类Dev

SQL Server:从第一个表链接到第二个表的主键的多个外键

来自分类Dev

按第一个外键过滤

来自分类Dev

连接表并选择所有行均满足第一个表中条件的外键

来自分类Dev

删除一个指针作为std :: map的第一个键

来自分类Dev

删除EF代码第一个外键表列中的表名前缀

来自分类Dev

在PostgreSQL中使用executemany()在另一个表中插入外键

来自分类Dev

一次将两个记录插入两个表,其中一个表使用另一个作为外键?

来自分类Dev

如何使用XSLT 2.0在XML文档中查找第一个表

来自分类Dev

EF代码第一个外键相同的表

来自分类Dev

如何将一个表中的主键作为外键插入到辅助表中

来自分类Dev

SQL Server:使用第一个插入的输出插入第二个表

来自分类Dev

EF代码第一个带有多个键的外键

来自分类Dev

SQL Server:从第一个表链接到第二个表的主键的多个外键

来自分类Dev

按第一个外键过滤

来自分类Dev

删除一个指针作为std :: map的第一个键

来自分类Dev

在两个实体之间编码第一个外键

来自分类Dev

Mysql在两个表中插入其中第一个表具有外键并更新第一个表

来自分类Dev

获取最近添加的记录的主键,并作为外键插入到另一个表中

来自分类Dev

根据第一个表中的列在第二个表中插入新记录

来自分类Dev

C#代码第一个外键。外键引用带有引用的表中的无效列

来自分类Dev

使用php lastInsertId()将一个表的主键作为外键插入到另一个表

来自分类Dev

使用第一个列表作为标题从表创建字典

来自分类Dev

PostgreSQL - 从三个表中多选主键并作为外键插入到一个表中

来自分类Dev

将批量注入 KairosDB 的 Python 脚本 - 仅插入第一个批量,其余忽略

来自分类Dev

第一个表中的外键

来自分类Dev

当键为第一个时,如何使用映射作为键值?

来自分类Dev

是否可以将表链接到使用外键链接回第一个表的表?

来自分类Dev

使用第一个 SELECT 的结果嵌套 SELECT

Related 相关文章

  1. 1

    如何将一个表中的主键作为外键插入到辅助表中

  2. 2

    SQL Server:从第一个表链接到第二个表的主键的多个外键

  3. 3

    按第一个外键过滤

  4. 4

    连接表并选择所有行均满足第一个表中条件的外键

  5. 5

    删除一个指针作为std :: map的第一个键

  6. 6

    删除EF代码第一个外键表列中的表名前缀

  7. 7

    在PostgreSQL中使用executemany()在另一个表中插入外键

  8. 8

    一次将两个记录插入两个表,其中一个表使用另一个作为外键?

  9. 9

    如何使用XSLT 2.0在XML文档中查找第一个表

  10. 10

    EF代码第一个外键相同的表

  11. 11

    如何将一个表中的主键作为外键插入到辅助表中

  12. 12

    SQL Server:使用第一个插入的输出插入第二个表

  13. 13

    EF代码第一个带有多个键的外键

  14. 14

    SQL Server:从第一个表链接到第二个表的主键的多个外键

  15. 15

    按第一个外键过滤

  16. 16

    删除一个指针作为std :: map的第一个键

  17. 17

    在两个实体之间编码第一个外键

  18. 18

    Mysql在两个表中插入其中第一个表具有外键并更新第一个表

  19. 19

    获取最近添加的记录的主键,并作为外键插入到另一个表中

  20. 20

    根据第一个表中的列在第二个表中插入新记录

  21. 21

    C#代码第一个外键。外键引用带有引用的表中的无效列

  22. 22

    使用php lastInsertId()将一个表的主键作为外键插入到另一个表

  23. 23

    使用第一个列表作为标题从表创建字典

  24. 24

    PostgreSQL - 从三个表中多选主键并作为外键插入到一个表中

  25. 25

    将批量注入 KairosDB 的 Python 脚本 - 仅插入第一个批量,其余忽略

  26. 26

    第一个表中的外键

  27. 27

    当键为第一个时,如何使用映射作为键值?

  28. 28

    是否可以将表链接到使用外键链接回第一个表的表?

  29. 29

    使用第一个 SELECT 的结果嵌套 SELECT

热门标签

归档