【问题标题】:Using XQuery in SQL Server returning duplicates在 SQL Server 中使用 XQuery 返回重复项
【发布时间】:2019-10-02 01:58:24
【问题描述】:

我希望这不是一个重复的问题。我在这个网站上搜索了我的问题,但我没有找到任何东西,所以这是我的问题......

我正在使用 XQuery 将 XML 文档解析为 SQL 服务器结果集。由于一些重复的节点名称/值对,我得到了重复。为什么我会收到这些重复项?

非常感谢任何帮助或帮助。

XML:

<root>
  <record>
    <recordid>1</recordid>
    <tests>
      <test name="Food" value="Apple" />
      <test name="Drink" value="Water" />
    </tests>
  </record>
  <record>
    <recordid>2</recordid>
    <tests>
      <test name="Food" value="Banana" />
      <test name="Drink" value="Orange Juice" />
    </tests>
    <tests>
      <test name="Food" value="Steak" />
      <test name="Drink" value="Beer" />
    </tests>
  </record>
</root>

SQL:

DECLARE
    @XML XML = '<root><record><recordid>1</recordid><tests><test name="Food" value="Apple" /><test name="Drink" value="Water" /></tests></record><record><recordid>2</recordid><tests><test name="Food" value="Banana" /><test name="Drink" value="Orange Juice" /></tests><tests><test name="Food" value="Steak" /><test name="Drink" value="Beer" /></tests></record></root>'

DECLARE @XMLTable AS TABLE (
    MyXML XML
)   
INSERT INTO @XMLTable(MyXML)
VALUES(@XML)

SELECT
    rec.c.value('recordid[1]', 'INT') AS RecordId,
    t.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
    t.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
FROM @XMLTable AS x
CROSS APPLY MyXML.nodes('/root/record') rec(c)
OUTER APPLY MyXML.nodes('/root/record/tests') t(c)

结果:

RecordId    Food            Drink
----------- --------------- ---------------
1           Apple           Water
1           Banana          Orange Juice
1           Steak           Beer
2           Apple           Water
2           Banana          Orange Juice
2           Steak           Beer

预期结果:

RecordId    Food            Drink
----------- --------------- ---------------
1           Apple           Water
2           Banana          Orange Juice
2           Steak           Beer

【问题讨论】:

    标签: sql-server xml xpath duplicates xquery


    【解决方案1】:

    这样就可以了:

    SELECT
        rec.c.value('../recordid[1]', 'INT') AS RecordId,
        rec.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
        rec.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
    FROM @XMLTable 
    CROSS APPLY MyXML.nodes('/root/record/tests') rec(c)
    

    【讨论】:

    • 啊,谢谢!如果 节点为空,这也有效,这很好。我还通过在我的原始脚本中添加 WHERE 子句找到了另一个解决方案,我将在下面发布它作为另一个答案,因为它为您的查询提供相同的结果。我很想讨论哪种解决方案更好。我认为根据性能,您的解决方案看起来会更好,因为它只有 1 个 CROSS APPLY。
    • @如果您使用“包括实际执行计划”运行这些查询,您将看到我的查询相对于批处理是 1%,而您的查询是 99%
    • Kirill,谈论性能:../ 的向后导航是众所周知的性能杀手。你可能会read this for details。我的回答提供了一种可以避免这种情况的方法......
    【解决方案2】:

    通过在我的原始脚本中添加 WHERE 子句,我能够达到预期的效果。

    SQL:

    SELECT
        rec.c.value('recordid[1]', 'INT') AS RecordId,
        t.c.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
        t.c.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
    FROM @XMLTable AS x
    CROSS APPLY MyXML.nodes('/root/record') rec(c)
    CROSS APPLY MyXML.nodes('/root/record/tests') t(c)
    WHERE rec.c.value('recordid[1]', 'INT') = t.c.value('../recordid[1]', 'INT')
    

    结果:

    RecordId    Food            Drink
    ----------- --------------- ---------------
    1           Apple           Water
    2           Banana          Orange Juice
    2           Steak           Beer
    

    【讨论】:

      【解决方案3】:

      解决这个问题的最好方法,实际上是这样的:

      SELECT
          rec.value('recordid[1]', 'INT') AS RecordId,
          t.value('test[@name="Food"][1]/@value[1]', 'VARCHAR(15)') AS Food,
          t.value('test[@name="Drink"][1]/@value[1]', 'VARCHAR(15)') AS Drink
      FROM @XMLTable AS x
      CROSS APPLY MyXML.nodes('/root/record') A(rec)
      OUTER APPLY rec.nodes('tests') b(t);
      

      与您自己的解决方案的不同之处在于:第二个 APPLY 使用第一个 APPLY 的输出来深入研究返回的 XML 片段。

      你可以这样理解:

      • 深入研究 XML 并在一行中返回每个 &lt;record&gt;
      • 现在(认为它行明智)获取返回的&lt;record&gt; 并打开&lt;tests&gt;。这是一条相对路径,因此没有斜线。
      • OUTER 将允许空的&lt;tests&gt;

      还有一个一般提示:向后导航(Xpath../)是已知的性能杀手。有一个great answer by Mikael Eriksson explaining the background

      【讨论】:

      • 我不知道这个...带有 ../ 的 Xpath ...我有一些 UDF 需要调整
      • @JohnCappelletti 父轴 会导致性能非常差,尤其是对于较大的 XML。我不知道您是否在其他答案下方的评论中找到了我的链接。值得一读。我现在把它放在我的答案中......
      • 哇@Shnugo,很棒的信息!明天我在办公室时会测试你的答案,但我想让你知道我真的很感激这次讨论。性能是重中之重,因此再次感谢您提供链接、答案和更多详细信息。
      • @Shnugo,去办公室测试一下。它工作得很好并且很有意义。我很高兴创建一个大型 XML 文件并进行一些性能测试以创建基准。再次感谢!
      猜你喜欢
      • 2023-03-26
      • 1970-01-01
      • 2010-12-24
      • 2014-09-09
      • 1970-01-01
      • 2016-05-25
      • 1970-01-01
      • 2021-03-07
      • 1970-01-01
      相关资源
      最近更新 更多