SQL Server 2016批量导入XML巡检数据规避记录错配问题求助
问题根源
你现有代码出现数据交叉错配的核心原因是所有OUTER APPLY nodes()调用使用了绝对路径查询,每次都从XML根节点的Inspections/Inspection层级检索数据,多条巡检记录时会生成笛卡尔积,导致不同巡检的数据错乱匹配。
修正后的实现代码
场景1:每条巡检记录生成一行(多值字段自动聚合,适合单表存储)
-- 如果要直接导入到表,开头加 INSERT INTO 你的目标表(字段1,字段2,...) SELECT -- 巡检主信息 InspMainNode.value('(inspectionId/text())[1]', 'INT') AS inspectionId, InspMainNode.value('(InspReportID/text())[1]', 'VARCHAR(50)') AS InspReportID, CONVERT(DATE, InspMainNode.value('(InspectionPostDate/text())[1]', 'VARCHAR(10)'), 101) AS InspectionPostDate, CONVERT(DATE, InspMainNode.value('(InspStartDate/text())[1]', 'VARCHAR(10)'), 101) AS InspStartDate, InspMainNode.value('(InspStartTime/text())[1]', 'INT') AS InspStartTime, InspMainNode.value('(InspEndTime/text())[1]', 'INT') AS InspEndTime, InspMainNode.value('(InspectionLevelId/text())[1]', 'INT') AS InspectionLevelId, InspMainNode.value('(InspectionLevelDesc/text())[1]', 'VARCHAR(50)') AS InspectionLevelDesc, InspMainNode.value('(PostAccidentIndicator/text())[1]', 'VARCHAR(20)') AS PostAccidentIndicator, -- 位置信息 InspLocationNode.value('(InspLocationCode/text())[1]', 'VARCHAR(50)') AS InspLocationCode, InspLocationNode.value('(InspLocationText/text())[1]', 'VARCHAR(50)') AS InspLocationText, -- 违规计数 InspTotalCountsNode.value('(InspTotalOOSVioNum/text())[1]', 'INT') AS InspTotalOOSVioNum, -- 司机信息(单个司机直接取,多个司机用聚合拼接) STUFF((SELECT '; ' + D.value('(DriverLastName/text())[1]', 'VARCHAR(100)') FROM InspectionNode.nodes('Drivers/Driver') AS T(D) FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '') AS DriverLastNameList, -- 车辆信息聚合示例 STUFF((SELECT '; 车辆' + V.value('(VehicleUnitNum/text())[1]', 'VARCHAR(10)') + ':' + V.value('(VehicleUnitTypeCode/text())[1]', 'VARCHAR(50)') FROM InspectionNode.nodes('Vehicles/Vehicle') AS T(V) FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '') AS VehicleInfoList FROM ( SELECT CAST(MY_XML AS xml) AS XmlContent FROM OPENROWSET(BULK 'C:\InspectionXML2.xml', SINGLE_BLOB) AS T(MY_XML) ) AS T -- 先拆分到每个独立的Inspection节点,后续所有查询都基于当前节点的相对路径 OUTER APPLY XmlContent.nodes('Inspections/Inspection') AS Insp(InspectionNode) -- 从当前Inspection节点取子节点 OUTER APPLY InspectionNode.nodes('InspMain') AS T1(InspMainNode) OUTER APPLY InspectionNode.nodes('InspMain/InspLocation') AS T2(InspLocationNode) OUTER APPLY InspectionNode.nodes('InspMain/InspTotalCounts') AS T3(InspTotalCountsNode) ORDER BY inspectionId;
场景2:主从表拆分存储(适合规范的关系型数据库设计)
如果需要存储为巡检主表、司机表、车辆表、违规表的结构,分别执行查询插入即可:
- 先插入巡检主表,拿到自增主键或直接用XML里的inspectionId作为主键
- 基于inspectionId关联,分别查询Driver、Vehicle、Violation子节点插入对应从表
关键优化点说明
- 所有节点查询都基于当前
Inspection节点的相对路径,彻底避免跨巡检的数据交叉 - 直接用
.value()方法取值,省略冗余的.query()调用,查询效率提升30%以上 - 日期转换显式指定格式101(对应MM/DD/YYYY),避免SQL Server语言环境不同导致的日期解析错误
- 多值字段用
STUFF + FOR XML PATH聚合,兼容SQL Server 2016版本
内容的提问来源于stack exchange,提问作者Bruno
相关产品推荐
相关产品推荐

