【问题标题】:Bulk load XML referring to auto-increment parent-id引用自动递增父 ID 的批量加载 XML
【发布时间】:2012-11-04 01:47:01
【问题描述】:

简而言之:我想对 SQL Server (2008) 数据库进行 XML 批量加载,并为父级生成可在子级中使用的自动增量 ID。这似乎受到范围的限制:父节点尚未完成,因此尚未插入。有人知道解决这个问题的方法吗?

较长的描述(对不起,真的很长,但我尽量完整):

我从一位客户那里得到了很多具有相似结构的 XML 文档,用于生成测试数据库。它们被导出以供其他工具使用,我的客户没有权限也没有联系来影响结构或内容。 (这些工具是由另一方为母公司编写的。)他也没有对 XML 或导出它们的数据库的正式描述。

事实证明,“顶级”XML 节点 <Registration> 确实有 ID,但它们在文档中并不是唯一的。 (顶部节点是相对的,它们确实有一个根节点和一个列表节点,但在 XML 中它们是进入数据库的最高元素。)ID 可以在其他 XML 文档中使用,因为它们引用到不在导出中的另一个对象<Case>。所以我需要生成自动增量 ID,以保持所有 <Registration>-elements 的唯一性,即使跨文件也是如此。

我的<Registration>-node 有很多女儿,例如<Activity>-节点。这些节点需要引用它们的父节点,因此它们应该使用生成的 auto-increment-id。但是,由于它们是未完成父节点的一部分,父节点仍在范围内,并且尚未插入表中,如msdn 和 @ 的“记录子集和键排序规则”中所述987654322@。但是,这些站点上的示例具有明确的唯一 CustomerId,而不是自动生成的 Id。

虽然这个关于“密钥排序规则”的文档看起来无法做到这一点,但我无法相信对于缺少(唯一)ID 的 XML 文件没有办法解决这个问题。更奇怪的是:它确实在 child 中插入了一个 parent-id,但数字低了一个。所以我假设这是来自前一个范围的自动增量 ID(其中 0 是默认值,尚未插入任何内容,我确实希望为 NULL)。所以我确实看到了一种解决方法:之后在我的子表中增加父键(UPDATE Activity SET RegistrationId = RegistrationId + 1)。但是,这确实需要保持一个限制 (WHERE TimeStamp > ...) 并且没有其他(手动或脚本)干预。

我已经尝试了很多不同的关系船和 VB 脚本(例如,我更喜欢自动生成我的表格),但我只会发布我的最新尝试。这也将用于说明从前一个范围插入自动增量 ID。

我的主要问题是:

  • 是否可以获得正确的自动递增父 ID?

但也欢迎其他提示,例如:

  • 在 SQL 中没有显式 CREATE TABLE-statement 的情况下,使用什么设置自动生成自动增量标识?

生成表格:

CREATE TABLE [dbo].[Registration](
  [Id] INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Registration PRIMARY KEY,
  [XmlId] [nvarchar](40) NULL,
)
CREATE TABLE [dbo].[Activity](
  [Id] INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Activity PRIMARY KEY,
  [RegistrationId] INT CONSTRAINT FK_Activity_Registration FOREIGN KEY (RegistrationId) REFERENCES Registration (Id),
  [XmlId] [nvarchar](1000) NULL,
)

要导入的 XML 文件:

<Updates>
  <Registrations>
    <Registration ID="NonUniqCaseId-123">
      <Activities>
        <Activity ID="UniqActId-1234" />
        <Activity ID="UniqActId-1235" />
      </Activities>
    </Registration>
    <Registration ID="NonUniqCaseId-124">
      <Activities>
        <Activity ID="UniqActId-1241" />
        <Activity ID="UniqActId-1242" />
      </Activities>
    </Registration>
  </Registrations>
</Updates>

测试上传的VB脚本(我想稍后在程序中包含一个循环,以处理多个文件):

    Dim objBL 
Set objBL = CreateObject("SQLXMLBulkLoad.SQLXMLBulkload.4.0")
objBL.ConnectionString = "provider=SQLOLEDB;data source=localhost;database=Test;integrated security=SSPI"
objBL.ErrorLogFile = "error.log"

objBL.CheckConstraints = False
objBL.XMLFragment = False
objBL.SchemaGen = True
objBL.SGDropTables = False
objBL.KeepIdentity = False

objBL.Execute "BulkTestMapping.xsd", "BulkTestContents.xml"
Set objBL = Nothing

XSD:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
    attributeFormDefault="qualified"
    elementFormDefault="qualified"
    xmlns:sql="urn:schemas-microsoft-com:mapping-schema">

  <xs:annotation>
    <xs:appinfo>
      <sql:relationship name="Registration_Activity"
            parent="Registration"
            parent-key="Id"
            child="Activity"
            child-key="RegistrationId"
            inverse="true"
            />
    </xs:appinfo>
  </xs:annotation>

  <xs:element name="Registration"
              sql:relation="Registration"
              sql:key-fields="Id" 
            >
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Activities" minOccurs="0" maxOccurs="unbounded" sql:is-constant="true">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Activity" minOccurs="0" maxOccurs="unbounded"
                     sql:relation="Activity" 
                     sql:key-fields="RegistrationId"
                     sql:relationship="Registration_Activity"
              >
                <xs:complexType>
                  <xs:attribute name="ID" sql:field="XmlId" form="unqualified" type="xs:string" />
                  <xs:attribute name="DbId" sql:identity="ignore" sql:field="Id" msdata:AutoIncrement="true" msdata:ReadOnly="true" type="xs:int" /> 
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="ID" form="unqualified" sql:field="XmlId" />
      <xs:attribute name="DbId" sql:identity="ignore" sql:field="Id" msdata:AutoIncrement="true" type="xs:int" /> 
    </xs:complexType>
  </xs:element>
</xs:schema>

结果表(注意RegistrationId 减一):

[Registration]
Id  XmlId
1   NonUniqCaseId-123
2   NonUniqCaseId-124

[Activity]
Id  RegistrationId  XmlId
1   0   UniqActId-1234
2   0   UniqActId-1235
3   1   UniqActId-1241
4   1   UniqActId-1242

编辑:比我想象的还要糟糕。如果我再次添加记录,外键(子键)再次从 0 开始!所以很难甚至不可能确定更正(每张桌子)应该是什么:

[Registration]
Id  XmlId
1   NonUniqCaseId-123
2   NonUniqCaseId-124
3   NonUniqCaseId-123
4   NonUniqCaseId-124

[Activity]
Id  RegistrationId  XmlId
1   0   UniqActId-1234
2   0   UniqActId-1235
3   1   UniqActId-1241
4   1   UniqActId-1242
5   0   UniqActId-1234
6   0   UniqActId-1235
7   1   UniqActId-1241
8   1   UniqActId-1242

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?
  • SELECT @@version 返回Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64) Jun 17 2011 00:54:03 Copyright (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows NT 6.1 &lt;X64&gt; (Build 7601: Service Pack 1)

标签: sql-server xsd sqlxml bulk-load


【解决方案1】:

我不知道使用 XML 进行批量加载,所以这里是使用 TSQL 执行此操作的答案。

在 SQL Server 2008 中,您可以将合并与输出结合使用,在源数据和目标自动生成的 id 之间创建映射。

Using merge..output to get mapping between source.id and target.id

Dr. OUTPUT or: How I Learned to Stop Worrying and Love the MERGE

在这种情况下,您可以合并到 Registration 并将具有生成的 id 的子 XML 节点输出到临时表或表变量,然后使用该表插入到 Activity

SQL Fiddle

MS SQL Server 2008 架构设置

CREATE TABLE [dbo].[Registration](
  [Id] INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Registration PRIMARY KEY,
  [XmlId] [nvarchar](40) NULL,
);

CREATE TABLE [dbo].[Activity](
  [Id] INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_Activity PRIMARY KEY,
  [RegistrationId] INT CONSTRAINT FK_Activity_Registration FOREIGN KEY (RegistrationId) REFERENCES Registration (Id),
  [XmlId] [nvarchar](1000) NULL,
);

查询 1

declare @XML xml = '
<Updates>
  <Registrations>
    <Registration ID="NonUniqCaseId-123">
      <Activities>
        <Activity ID="UniqActId-1234" />
        <Activity ID="UniqActId-1235" />
      </Activities>
    </Registration>
    <Registration ID="NonUniqCaseId-124">
      <Activities>
        <Activity ID="UniqActId-1241" />
        <Activity ID="UniqActId-1242" />
      </Activities>
    </Registration>
  </Registrations>
</Updates>';

declare @T table
(
  RegistrationId nvarchar(40),
  Activities xml
);

merge Registration as T
using
  (
  select R.N.value('@ID', 'nvarchar(40)') as XmlId,
         R.N.query('Activities') as Activities
  from @XML.nodes('/Updates/Registrations/Registration')  as R(N)
  ) as S
on 0 = 1
when not matched then
  insert(XmlId) values (S.XmlId)
output inserted.Id, S.Activities into  @T(RegistrationId, Activities);

insert into Activity(RegistrationId, XmlId)
select T.RegistrationId,
       A.N.value('@ID', 'nvarchar(1000)')
from @T as T
  cross apply T.Activities.nodes('Activities/Activity') as A(N);

查询 2

select *
from Registration;

Results

| ID |             XMLID |
--------------------------
|  1 | NonUniqCaseId-123 |
|  2 | NonUniqCaseId-124 |

查询 3

select *
from Activity;

Results

| ID | REGISTRATIONID |          XMLID |
----------------------------------------
|  5 |              1 | UniqActId-1234 |
|  6 |              1 | UniqActId-1235 |
|  7 |              2 | UniqActId-1241 |
|  8 |              2 | UniqActId-1242 |

【讨论】:

  • 感谢您提供的替代解决方案。因为我的示例是对我的真实 XSD 的非常简短的摘录(只是为了重现问题),这意味着要编写 很多 的查询和路径,而我宁愿不这样做。幸运的是,我确实找到了替代解决方案!
【解决方案2】:

答案很简单:在XSD 中省略inverse,所以删除这一行:

inverse="true"

我介绍这个是因为我有很多多对多的关系。 (我的例子是一个非常简短的摘录来重现问题。)但似乎我在太多地方介绍了它。

推测:(很遗憾,我没有时间调查/确认下一个假设。)

我假设现在inverse 应该只用于关系女儿的一面,而不是母亲的一面。例如。当 A 和 B 具有多对多关系 A_B 时,XML 看起来像这样:

<ListOfA>
  <A ID="Uniq_A123">
    <A_B>
      <B ID="NonUniq_B234" />
    </A_B>
    <A_B>
      <B ID="NonUniq_B235" />
    </A_B>
  </A>
  <A ID="Uniq_A124">
    <A_B>
      <B ID="NonUniq_B234" />
    </A_B>
  </A>
</ListOfA>

A 是 A_B 中关系的隐式“父”,因为它是 XML 母体,然后 B 应该通过指定 inverse 显式地从子级反转到父级。 但是,由于我正在为 A 和 B 生成我自己的 Id,我怀疑这是否对我有用,我将在之后运行修复查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-12
    • 2013-04-25
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多