7

我继承了一个 1000 行的存储过程,它使用 FOR XML EXPLICIT 生成 XML。我的问题是它大部分时间都有效。在某些情况下,我收到错误:

父标签 ID 2 不在打开标签中。FOR XML EXPLICIT 要求先打开父标签。检查结果集的顺序。编号:6833 严重性:16 状态:1

我需要有关如何解决此问题的想法。我需要找出嵌套失败的地方。这可能是没有发出父行但有子行的情况。更糟糕的是,这个问题只发生在我们的测试系统上,它可能缺少一些生产数据。问题是如何从数千行中找到它?

我确信不存在一个疯狂的想法:SQL Server 有一种算法用于确定行的顺序是否正确。如果有一个工具可以查看我的结果集(没有 FOR XML EXPLICIT)并找出问题所在,那就太好了(如果不太可能),然后告诉我。

在没有这样的工具的情况下,我欢迎任何关于如何调试它的建议。XML(当它工作时)有四个层次!


更新:感谢到目前为止的所有答案。看起来这是一个错误编辑的存储过程的问题。大的部分用“/* /”注释注释掉了——当代码中已经有“/*/”注释时,这不能很好地工作……当我确定答案时,我会再次更新。

4

4 回答 4

10

一种可能的方法是实际删除 FOR XML EXPLICIT 部分并查看由您的 sql 语句生成的结果集。它将指示生成 xml 的嵌套,并希望将您引导至该问题。请参阅以下图片,该图片取自 MSDN 文档:http: //msdn.microsoft.com/en-us/library/ms189068.aspx

替代文字


编辑

发布示例输出可能值得,但在图像中的示例中,如果任何带有 tag=3 的行的 Order!2!Id 为空,您将得到相同的错误。此列实际上是标记 = 2 的父行和标记 = 3 的子行之间的连接。如果您的数据如上所示,我认为您可以通过识别 parent=2 且 Order!2!Id 为空的行来有效地找到您的问题。

或者,它可能是订购。在这种情况下,您可以以某种方式构建一个查询,该查询标识在结果集中出现在 Tag = 2 行之前的任何 Parent = 2 行。


编辑 2

CREATE TABLE MyTable(
    Tag int,
    Parent int,
    SomeIdentifier int
)   

INSERT INTO MyTable VALUES (2, 1, 1) -- this row defined before parent
INSERT INTO MyTable VALUES (1, null, 1)
INSERT INTO MyTable VALUES (3, 2, 1)
INSERT INTO MyTable VALUES (3, 2, 1)
INSERT INTO MyTable VALUES (1, null, 2)
INSERT INTO MyTable VALUES (2, 1, 2)
INSERT INTO MyTable VALUES (3, 2, 2)
INSERT INTO MyTable VALUES (3, 2, 2)
INSERT INTO MyTable VALUES (1, null, 3)
INSERT INTO MyTable VALUES (3, 2, 3) -- this is orphaned
INSERT INTO MyTable VALUES (3, 2, 3) -- this is orphaned

;WITH myCte AS(
SELECT   Tag
        ,Parent
        ,SomeIdentifier
        ,ROW_NUMBER() OVER (PARTITION BY SomeIdentifier ORDER BY(SELECT 0)) AS RowOrder
FROM    MyTable   
) SELECT c1.Tag
        ,c1.Parent
        ,c1.SomeIdentifier
FROM myCte c1 
LEFT OUTER JOIN myCte c2 ON c2.SomeIdentifier = c1.SomeIdentifier AND c1.Parent = c2.Tag
WHERE c1.Parent IS NOT NULL     --ignore root rows for now
AND   (c1.RowOrder < c2.RowOrder    --out of order rows
        OR    
       c2.Tag IS NULL)      --orphaned rows
于 2010-12-17T00:56:25.537 回答
4

使用 FOR XML 时,结果集的顺序必须是父 xml 节点在其子节点之前(通常,XML 文件不应依赖于排序;这应使用 XSL 转换执行)

感兴趣的:XSD 的艺术(免费电子书)

你可能已经知道了:如果你有 XSD,你可以使用一个工具来验证 XML 与 XSD(或编写大约 10 行 C# 来完成):

如何在 Visual C# .NET 中使用 DTD、XDR 或 XSD 验证 XML 文档

如果您有格式良好的 XML 示例,则可以使用XSD.exe.

于 2010-12-17T00:28:50.150 回答
2

我会将行集(没有 FOR XML 子句)转储到临时表中。然后,您应该能够在此表中执行孤立的搜索。

如果您找不到任何孤儿,这往往表明您的排序存在问题(父在行集中,但出现在子之后)。但至少我们会将该问题的搜索空间减半 :-)

于 2010-12-17T08:39:47.493 回答
2

我找到了其中一个问题的答案,并想分享我学到的一些经验教训。

我采用了存储过程的内容,并对其进行了更改,以便将结果集插入到表变量中。第 1 课:确保您在表变量中获得正确的列类型 - 我花了几个小时研究一个由于无意中将列类型从 varchar 更改为 int 引起的问题,这导致排序顺序发生变化,从而转移了问题。

一旦我修复了我的表变量,我就可以做一些有用的查询,比如:

SELECT TOP n *
FROM @result
ORDER BY <same order as original query>
FOR XML EXPLICIT

我想我必须做一个“二分搜索”来确定哪一行有问题。事实证明,问题出在前几行。

2 级数据是由一个查询形成的,该查询包括一个查找表的内部连接。这会导致在查找列未映射时忽略整行。这并没有阻止发出相应的级别 3 和 4 行,因此这导致了错误。

使用 LEFT JOIN 进行查找解决了这个问题。

于 2010-12-23T17:46:59.280 回答