【问题标题】:I Need Help in Parsing an XML in T-SQL在 T-SQL 中解析 XML 时需要帮助
【发布时间】:2012-01-29 12:51:39
【问题描述】:

有人可以帮我在 T-SQL (SQL Server 2005) 中解析以下 XML 吗?

<Tx>
    <T>1</T>
    <C>1</C>
    <T>2</T>
    <C>1</C>
    <T>3</T>
    <C>1</C>
    <T>4</T>
    <C>1</C>
</Tx>

我尝试了以下方法:

SELECT
        Tx.query('T').value('.', 'varchar(10)') AS [Column 1],
        Tx.query('C').value('.', 'varchar(10)') AS [Column 2]
    FROM @MyXml.nodes('Tx') x(Tx)

但它不起作用,因为我得到以下结果:

Column 1    Column 2
--------    --------
1234        1111

同时期待(我想要实现的目标):

Column 1    Column 2
--------    --------
1           1
2           1
3           1
4           1

字符串显然是一个有效的 XML,但它对 T-SQL 有效吗?

如果有人能提供解释 XML 如何在 T-SQL 中工作的参考资料,我将不胜感激。

提前致谢。

【问题讨论】:

  • 显然您想将CT 匹配。哪些值相互配对是否重要,还是可以选择任何值?
  • @MikaelEriksson,对不起,我不明白这个问题。我将 XML 发送到存储过程,然后从那里我正在做类似的事情(在临时表中插入 XML 之后):基于 [Column 2] 将 XML“表”与 MyTable1 连接并插入 [Column 1]在 MyTable2... 或 INSERT INTO MyTable2 (SomeColumn, SomeOtherColumn) SELECT A.[Column 2], B.AnotherColumn FROM @xmlTable A JOIN MyTable1 B ON A.[Column 1] = B.Id
  • 在您的示例数据中,C 的值始终为 1。一个 XML 中的所有 C 是否都只有一个不同的值?
  • @MikaelEriksson,如果它总是一个不同的值,我没问题,对吧? :) 我给出的示例 XML 只是一个示例,但有点说 CT 相比几乎没有不同的值。
  • 你想让T的第一个值与C的第一个值配对,T的第二个值与C的第二个值配对,等等, ETC ....?使用数字表可以有更多的 T 然后 Cs or more C´s than T´s? Can there be two or more consecutive T´s without a C in between or the other way around? It is possible to do a XQuery on position`。我可以提供一个答案,但我想知道配对 Ts and Cs 的规则。顺便说一句,@marc-s 说如果可以的话你应该重新设计你的 XML 是正确的。

标签: xml tsql xpath xml-parsing xquery


【解决方案1】:

这是一种按位置配对值的方法。第一个 T 与第一个 C 匹配,依此类推。

declare @XML xml = 
'<Tx>
    <T>1</T>
    <C>4</C>
    <T>2</T>
    <C>3</C>
    <T>3</T>
    <C>2</C>
    <T>4</T>
    <C>1</C>
</Tx>'

select @XML.value('(/Tx/T[position() = sql:column("N.number")])[1]', 'int') as Column1,
       @XML.value('(/Tx/C[position() = sql:column("N.number")])[1]', 'int') as Column2
from master..spt_values as N
where N.type = 'P' and
      N.number between 1 and @XML.value('max((count(/Tx/T), count(/Tx/C)))', 'int')

使用不同的 XML 结构,例如 marc_ssuggested,查询要简单得多

declare @XML xml = 
'<Tx>
  <row>
    <T>1</T>
    <C>4</C>
  </row>
  <row>
    <T>2</T>
    <C>3</C>
  </row>
  <row>
    <T>3</T>
    <C>2</C>
  </row>
  <row>
    <T>4</T>
    <C>1</C>
  </row>
</Tx>'

select T.R.value('T[1]', 'int') as Column1,
       T.R.value('C[1]', 'int') as Column2
from @XML.nodes('/Tx/row') as T(R)

【讨论】:

  • 尝试运行第一个时出现以下错误(第二个给出类似的错误,但行号不同)消息 139,级别 15,状态 1,行 0 无法将默认值分配给本地多变的。 Msg 137, Level 15, State 2, Line 13 必须声明标量变量“@XML”。
  • @JamesJiao - 这是一种适用于 SQL Server 2008 的语法。如果您使用的是 SQL Server 2005,则必须将变量 @XML 的声明和赋值拆分为两个语句,如 @987654326 @
  • 我选择了这个作为我的问题的答案,虽然我不喜欢代码解析简单的东西有多难看......但如果是这样,那么它就是这样。我的建议是像 Mikael Eriksson 和 marc_s 建议的那样格式化 XML……如果可能的话。
【解决方案2】:

你想达到什么目的??

您当前的选择语句将列出&lt;Tx&gt; 下方的所有&lt;T&gt; 元素-但仅列出那些-也不是&lt;C&gt; 节点-而且您似乎想从两种子节点中获取值-对??

这将为您提供来自 &lt;T&gt; 节点的所有元素 - 这就是您要查找的内容吗?

SELECT
    Tx.x.value('.', 'varchar(10)') AS [Column 1]
FROM @MyXml.nodes('Tx/T') Tx(x)

这将为您提供&lt;Tx&gt; 节点内的所有元素,包括它们的“类型”(C 或 T)——这就是您要找的吗?

SELECT
    ColumType = Tx.x.value('local-name(.)', 'varchar(10)'),
    ColumnValue = Tx.x.value('.', 'varchar(10)')
FROM @MyXml.nodes('Tx/*') Tx(x)

更新:作为学习如何在 SQL Server 中使用 XQuery 的资源,我会推荐

您的 XML 不太适合做您想做的事情 - 没有什么可以“保持”显然属于一起的 T 和 C 元素 - 所以 XQuery 确实无法按照您想要的方式解析它它到。

如果你有这样的 XML:

<Tx>
   <Pair>
      <T>....</T>
      <C>....</C>
   </Pair>
   <Pair>
      <T>....</T>
      <C>....</C>
   </Pair>
   ....
</Tx>

然后您可以获取Tx/Pair 节点的列表并从该XML 片段中获取T 和C 元素。但是现在 - 你所能做的就是解析出&lt;Tx&gt; 的所有 8 个子节点并显示它们的值 - 这就是你能做的所有事情

【讨论】:

  • @TheBlueSky:你不能按照你想要的方式解析它——没有将相应的&lt;T&gt;&lt;C&gt; 元素保持在一起的“结构”——所以在 XPath 中你无法解析这些元素元素为“column1”和“column2”。
  • 是的,如果我有像您的示例那样的 XML,我将是一项简单的任务,但是,唉,事实并非如此。无论如何,感谢您的参考。
  • 我会为这篇文章投赞成票,因为它部分回答了我的问题。
【解决方案3】:

使用这个:

select a.t, b.c
from
(
    select t.c.value('.[1]', 'int') [t]
        , ROW_NUMBER() OVER(ORDER BY t.c) [rn]
    from @MyXml.nodes('Tx/T') t(c)
)a
join
(
    select t.c.value('.[1]', 'int') [c]
        , ROW_NUMBER() OVER(ORDER BY t.c) [rn]
    from @MyXml.nodes('Tx/C') t(c)
)b on b.rn = a.rn

或其他方式:

select t.t [T]
    , @MyXml.value('(Tx/C[position() = sql:column("rn")])[1]', 'int') [C]
from
(
    select t.c.value('.[1]', 'int') [t]
        , ROW_NUMBER() OVER(ORDER BY t.c) [rn]
    from @MyXml.nodes('Tx/T') t(c)
)t

【讨论】:

  • 这只是一种解决方法;你的意思是 T-SQL 不能解析给定的 XML?
  • @TheBlueSky,据我所知 - 不。
  • 看起来 bitnodes 实例的排序很可疑,但显然它使用了来自 XML Reader &lt;ColumnReference Table="[XML Reader with XPath filter]" Column="id" /&gt; 的 id。 Adam Machanic 在Uniquely Identifying XML Nodes with DENSE_RANK 中写了一篇关于此的博客。 +1
猜你喜欢
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多