sql-server - 使用 T-SQL 粉碎 XML - 从组内提取行值的正确语法是什么?

标签 sql-server xml t-sql

我正在尝试使用以 XML 形式接收的一些日志记录数据。

我在 SQL Server 中获得了 XML 数据包,经过大量工作清理数据以使其成为有效的 XML。 (并从 JSON 包装器中获取其他属性等) 但现在我被困在尝试读取 XML 以检索各行中的值

我的示例 XML 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<general>
   <group id="0" comment="Application">
      <N1 comment="Start Date">2020-11-03T00:05:48Z</N1>
      <N2 comment="Name/Description">ProgramName</N2>
      <N3 comment="Version Number">ReleaseNumber</N3>
      <N5 comment="Compilation Date">2020-10-01T01:05:01Z</N5>
      <N6 comment="Up Time">1899-12-30T00:00:56Z</N6>
   </group>
   <group id="1" comment="Exception">
      <N1 comment="Date">Tue, 3 Nov 2020 11:06:45 +1100</N1>
      <N2 comment="Address">MemoryAddress</N2>
      <N3 comment="Module Name">ModuleName</N3>
      <N4 comment="Module Version">ModuleVersionNumber</N4>
      <N5 comment="Type">ExceptionType</N5>
      <N6 comment="Message">Insufficient memory for this operation.</N6>
      <N7 comment="ID">ExceptionID</N7>
      <N8 comment="Count">1</N8>
      <N9 comment="Status">New</N9>
      <N11 comment="Sent">0</N11>
   </group>
</general>

问题是,我不知道如何查询 Group 0 N6,我一直在使用:

DECLARE @x XML
select @X
,LEFT(@X.value('(/log/@version)[1]','VARCHAR(10)') ,10)

但是我无法理解必要的 XQuery/XPath 来将子行的值拉入特定编号的组内。

@X.value('(/log/group[1]/N2)[1]','VARCHAR(50)') ,10)

任何人都可以分享可以查询 N2 值的魔法吗?我怀疑答案就在“包含”中,但我在找到编写代码教程以使说明在我的脑海中快速到位时遇到了问题。 (这很复杂,因为我想从 XML 中的 3 个不同组中提取 10 个值。为了查询我收到的日志的不同版本(我将其分解为平面文件,以便我可以提取属性),我最终得到了运行:

    ,ExceptionAddress=LEFT(@X.value('(/Doc/Log/General/Line_2.2/@Value)[1]','VARCHAR(10)') ,10)
    ,ExceptionType=LEFT(@X.value('(/Doc/Log/General/Line_2.5/@Value)[1]','VARCHAR(50)') ,50)
    ,ExceptionMessage=LEFT(@X.value('(/Doc/Log/General/Line_2.6/@Value)[1]','NVARCHAR(200)') ,200)
    ,FormClass=LEFT(@X.value('(/Doc/Log/General/Line_4.1/@Value)[1]','VARCHAR(50)') ,50)
    ,FormText=LEFT(@X.value('(/Doc/Log/General/Line_4.2/@Value)[1]','NVARCHAR(50)') ,50)
    ,ControlClass=LEFT(@X.value('(/Doc/Log/General/Line_4.3/@Value)[1]','VARCHAR(50)') ,50)
    ,ControlText=LEFT(@X.value('(/Doc/Log/General/Line_4.4/@Value)[1]','NVARCHAR(50)') ,50)
    ,OSType=LEFT(@X.value('(/Doc/Log/General/Line_6.1/@Value)[1]','VARCHAR(50)') ,50)
    ,OSBuild=LEFT(@X.value('(/Doc/Log/General/Line_6.2/@Value)[1]','VARCHAR(50)') ,50)
    ,OSUpdate=LEFT(@X.value('(/Doc/Log/General/Line_6.3/@Value)[1]','VARCHAR(50)') ,50)

最佳答案

您的 XML 在很多方面都有缺陷,这就是为什么没有简单俗气答案的原因:

DECLARE @xml XML=
'<general>
   <group id="0" comment="Application">
      <N1 comment="Start Date">2020-11-03T00:05:48Z</N1>
      <N2 comment="Name/Description">ProgramName</N2>
      <N3 comment="Version Number">ReleaseNumber</N3>
      <N5 comment="Compilation Date">2020-10-01T01:05:01Z</N5>
      <N6 comment="Up Time">1899-12-30T00:00:56Z</N6>
   </group>
   <group id="1" comment="Exception">
      <N1 comment="Date">Tue, 3 Nov 2020 11:06:45 +1100</N1>
      <N2 comment="Address">MemoryAddress</N2>
      <N3 comment="Module Name">ModuleName</N3>
      <N4 comment="Module Version">ModuleVersionNumber</N4>
      <N5 comment="Type">ExceptionType</N5>
      <N6 comment="Message">Insufficient memory for this operation.</N6>
      <N7 comment="ID">ExceptionID</N7>
      <N8 comment="Count">1</N8>
      <N9 comment="Status">New</N9>
      <N11 comment="Sent">0</N11>
   </group>
</general>';

--这会获取一个列表,您可以将其写入临时表并从那里继续

SELECT A.gr.value('@id','int') groupId
      ,A.gr.value('@comment','nvarchar(max)') groupComment
      ,B.nd.value('@comment','nvarchar(max)') NComment
      ,B.nd.value('text()[1]','nvarchar(max)') NContent
FROM @xml.nodes('/general/group') A(gr)
OUTER APPLY A.gr.nodes('*') B(nd);

--这会尝试以表格格式获取您的 EAV 数据

SELECT A.gr.value('@id','int') groupId
      ,A.gr.value('@comment','nvarchar(max)') groupComment
      ,A.gr.value('(*[@comment="Compilation Date"])[1]','datetime') NCompilationDate
      ,A.gr.value('(*[@comment="Date"])[1]','nvarchar(max)') NDate
      ,A.gr.value('(*[@comment="Count"])[1]','int') NCount
FROM @xml.nodes('/general/group') A(gr);

为什么你的 XML 有缺陷:

  • 您不应该命名多个元素(N1、N2、N3...)。他们都应该有相同的名字。如果确实需要该号码,请添加一个属性 (nmbr="1")。
  • 您正在混合日期-时间格式。在 XML 中,您应该仅使用 ISO8601(因为它位于您的第一组中)。最坏的情况是依赖于文化和语言的内容。在我的(德语)系统中,星期二的“Tue”会打破这一点。

我的建议是:

使用我的第二种方法,但为每种类型的组创建一个查询,并将它们分别读入具有给定类型列集的临时表中,然后继续执行此操作。

关于sql-server - 使用 T-SQL 粉碎 XML - 从组内提取行值的正确语法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65015962/

相关文章:

SQL:展开集合

sql-server - 查询帮助 - 根据列值连接两个表的子集

sql - 在插入语句中使用 OUTPUT 时,如果您在 select 中指定 order by ,它是否遵循 select 中指定的顺序?

sql - 使用子查询更新 - 更新超过所需的记录

java - 在Java中将xml数据从一个xml解析为一个新的xml

xml - XSLT - 为一些 child 添加命名空间声明

sql-server - 如何在 IIF 或另一个子查询中引用我的子查询值,而不必再次将其全部写出?

sql - 存储过程sql中的日期转换

sql-server - Azure 数据工厂复制事件失败。用户登录失败

android - 按钮 - 单击时更改背景颜色