简而言之:我想对SQL Server(2008)数据库执行XML批量加载,并为父级生成auto-increment-id,可以在子级中使用。这似乎受范围限制:父节点未完成,因此尚未插入。有人知道解决这个问题吗?
时间越长(抱歉,它真的很长,但我尝试完成):
从客户那里我获得了大量具有类似结构的XML文档,从中可以生成测试数据库。它们被导出供其他工具使用,我的客户没有权限也没有联系人来影响结构和内容。 (这些工具是由另一方为母公司编写的。)他也没有正式描述XML或从中导出的数据库。
事实证明,“顶级”XML节点<Registration>
确实有ID,但这些在文档中并不是唯一的。 (顶级节点是相对的,它们确实有一个根节点和一个列表节点,但在XML中它们是进入数据库的最高元素。)ID可以在其他XML文档中使用,因为它们引用到另一个不在导出中的对象<Case>
。所以我需要生成auto-increment-id来保持所有<Registration>
- 元素在文件中是唯一的。
我的<Registration>
- 节点有很多女儿,例如<Activity>
- 节点。这些节点需要引用它们的父节点,因此它们应该使用生成的auto-increment-id。但是,由于它们是未完成的父节点的一部分,因此父节点仍然在范围内,并且它尚未插入到表中,如msdn中的“记录子集和密钥排序规则”中所述和technet。但是,这些网站上的示例具有明确的唯一CustomerId
,而不是自动生成的ID。
尽管有关“密钥排序规则”的文档使得它看起来无法完成,但我无法相信没有办法解决缺少(唯一)ID的XML文件。更奇怪的是:它确实在孩子中插入了父ID,但数字却低了一个。所以我假设这是来自前一个范围的auto-increment-id(其中0是默认值,没有插入任何内容,我确实期望NULL)。所以我确实看到了一个解决方法:之后在我的子表中递增父键(UPDATE Activity SET RegistrationId = RegistrationId + 1
)。但是,这确实需要保持限制(WHERE TimeStamp > ...
)而不需要其他(手动或脚本)干预。
我尝试了很多不同的关系和VB脚本(例如我更喜欢自动生成我的表格),但我会发布我的最新尝试。这也将用于说明从前一个范围插入auto-increment-id。
我的主要问题是:
但其他提示非常受欢迎,例如:
CREATE TABLE
语句的情况下使用什么设置来自动生成自动增量标识?生成表格:
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
答案 0 :(得分:0)
我不知道使用XML进行批量加载,所以这里是使用TSQL来做到这一点的答案。
在SQL Server 2008中,您可以将merge与输出结合使用,以在源数据和目标自动生成的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节点输出到临时表或表variabl,然后将该表用于插入Activity
。
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 |
答案 1 :(得分:0)
答案结果很简单:只需忽略inverse
中的XSD
,请删除此行:
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通过作为XML-mother隐含地表示A_B中关系的“父”,然后通过指定inverse
来明确地将B从子转换为父。
但是,由于我为A和B生成自己的Id,我怀疑这对我是否有用,之后我会运行修复查询。