【问题标题】:Modifying date field in XML document stored in SQL Server table修改存储在 SQL Server 表中的 XML 文档中的日期字段
【发布时间】:2013-05-04 14:30:30
【问题描述】:

我有一个存储在 SQL Server 表 xml 列中的 xml 文档,其中日期字段中的时间不准确

我想将文档中的所有日期和时间(SaleDateTime、LineStartTime、LineEndTime)值更新 15 秒,例如,2012-02-01T00:07:50 变为 2012-02-01T00:08 :05(关于为什么它需要这样的长篇大论;它不在我的手中)。可以有 1 到多笔交易,每笔交易可以有 1 个或多个行条目。

我已经尝试使用 DATEADD 进行 OPENXML、修改方法等,但我无法正确处理。我已经黔驴技穷了。任何帮助表示赞赏。提前致谢!!

示例如下

CREATE TABLE XMLTable (doc xml);

INSERT INTO XMLTable (doc)
VALUES
(
'<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Transaction>
  <SaleID>1</SaleID>
  <Sale>
    <SaleDateTime>2012-02-01T00:07:00</SaleDateTime>
    <LineItem>
      <Line>1</Line>
      <LineStartTime>2012-02-01T00:07:00</LineStartTime>
      <LineEndTime>2012-02-01T00:07:00</LineEndTime>
          <Amount>13.50</Amount>
    </LineItem>
  </Sale>
</Transaction>
<Transaction>
  <SaleID>2</SaleID>
  <Sale>
    <SaleDateTime>2012-02-01T00:11:00</SaleDateTime>
    <LineItem>
      <Line>1</Line>
      <LineStartTime>2012-02-01T00:11:00</LineStartTime>
      <LineEndTime>2012-02-01T00:11:00</LineEndTime>
          <Amount>13.50</Amount>
    </LineItem>
    <LineItem>
      <Line>2</Line>
      <LineStartTime>2012-02-01T00:11:00</LineStartTime>
      <LineEndTime>2012-02-01T00:11:00</LineEndTime>
          <Amount>5.22</Amount>
    </LineItem>
  </Sale>
</Transaction>
</Root>')

SELECT * FROM XMLTable 

【问题讨论】:

    标签: sql-server xml xml-dml


    【解决方案1】:

    您可以使用modify 方法。例如替换SaleDateTime的第一次出现:

    declare @now datetime = getdate()
    
    update  XMLTable
    set     doc.modify('replace value of (/Root/Transaction/Sale/SaleDateTime/text())[1] 
                        with sql:variable("@now")') 
    

    【讨论】:

    • 谢谢。我尝试了修改方法,但我的问题是如何为每个日期时间字段添加 15 秒?
    • 您必须读取旧值然后设置它。您必须遍历每一行和每笔销售。在 C# 或 VB 中可能更容易解决客户端问题。
    • 谢谢!你能举个例子吗?
    【解决方案2】:

    如果您知道 XML 文档的结构,最快的选择可能是使用 for xml 将文档分解并重建它。还可以轻松修改此解决方案,以一次性跨表中的多行进行转换,而不是一次处理一个文档。

    对于问题中给出的结构,针对 XML 变量的查询将如下所示。

    select T.X.value('(SaleID/text())[1]', 'int') as SaleID,
           (
           select dateadd(second, 15, S.X.value('(SaleDateTime/text())[1]', 'datetime')) as SaleDateTime,
                  (
                  select L.X.value('(Line/text())[1]', 'int') as Line,
                         dateadd(second, 15, L.X.value('(LineStartTime/text())[1]', 'datetime')) as LineStartTime,
                         dateadd(second, 15, L.X.value('(LineEndTime/text())[1]', 'datetime')) as LineEndTime,
                         L.X.value('(Amount/text())[1]', 'varchar(20)') as Amount
                  from S.X.nodes('LineItem') as L(X)
                  for xml path('LineItem'), type
                  )
           from T.X.nodes('Sale') as S(X)
           for xml path('Sale'), type
           )
    from @doc.nodes('/Root/Transaction') as T(X)
    for xml path('Transaction'), root('Root'), type
    

    正如我所说,这可以修改为针对表工作,甚至可以更改为更新 XML 列。

    【讨论】:

      【解决方案3】:

      如果这些 XML 文档不是很大,或者这是一次性的事情并且性能不是优先考虑的,您可以将文档转换为 varchar 并对其执行REPLACE 以增加日期。

      下面是一个示例(您可以将其包装在一个函数中):

      declare @doc xml = 
      '<?xml version="1.0" encoding="UTF-8"?>
      <Root>
      <Transaction>
        <SaleID>1</SaleID>
        <Sale>
          <SaleDateTime>2012-02-01T00:07:00</SaleDateTime>
          <LineItem>
            <Line>1</Line>
            <LineStartTime>2012-02-01T00:07:00</LineStartTime>
            <LineEndTime>2012-02-01T00:07:00</LineEndTime>
                <Amount>13.50</Amount>
          </LineItem>
        </Sale>
      </Transaction>
      <Transaction>
        <SaleID>2</SaleID>
        <Sale>
          <SaleDateTime>2012-02-01T00:11:00</SaleDateTime>
          <LineItem>
            <Line>1</Line>
            <LineStartTime>2012-02-01T00:11:00</LineStartTime>
            <LineEndTime>2012-02-01T00:11:00</LineEndTime>
                <Amount>13.50</Amount>
          </LineItem>
          <LineItem>
            <Line>2</Line>
            <LineStartTime>2012-02-01T00:11:00</LineStartTime>
            <LineEndTime>2012-02-01T00:11:00</LineEndTime>
                <Amount>5.22</Amount>
          </LineItem>
        </Sale>
      </Transaction>
      </Root>'
      
      declare @New xml = @doc;
      
      ;with 
      dates (LineStartTime, LineEndTime) as
          (   -- get the start/end dates in any LineItem
              select  p.n.value('(LineStartTime)[1]', 'datetime'),
                      p.n.value('(LineEndTime)[1]', 'datetime')
              from    @doc.nodes('Root/Transaction/Sale/LineItem')p(n)
          ),
      upd (OldValue, NewValue) as
          (   -- add 15 min to each, and cast as varchar
              select  '<LineStartTime>' + convert(varchar, LineStartTime, 126) + '</LineStartTime>', 
                      '<LineStartTime>' + convert(varchar, dateadd(mi, 15, LineStartTime), 126) + '</LineStartTime>'
              from    dates 
              union 
              select  '<LineEndTime>' + convert(varchar, LineEndTime, 126) + '</LineEndTime>', 
                      '<LineEndTime>' + convert(varchar, dateadd(mi, 15, LineEndTime), 126) + '</LineEndTime>' 
              from    dates
          )
      -- cast @doc as varchar, and replace each occurrence of start/end elements with NewValue
      select  @new = cast(replace(cast(@new as varchar(max)), OldValue, NewValue) as xml)
      from    upd;
      
      select [Old]=@doc, [New]=@new;
      

      【讨论】:

      • 哇,真酷!这可能对我有用!非常感谢,内森,谢谢阿多马尔!
      • 我对一个大型文档(24k 事务)进行了尝试,它运行了大约一个小时。对于小型文档(如您所说),它看起来是一个很好的解决方案,但对于大型文档来说需要很长时间。
      • 尝试将 xpath 更改为更具体,例如:'Root/Transaction/Sale/LineItem'。这可能有助于提高性能。我做了上面的编辑。此外,文档是否有大量的唯一日期?还是它们大多与正在增加的开始和结束日期值相同?
      • 这是代表交易的销售数据,因此大部分日期会有所不同。我会试试你的修改和修补。非常感谢您的帮助!
      猜你喜欢
      • 1970-01-01
      • 2019-03-23
      • 1970-01-01
      • 1970-01-01
      • 2021-06-28
      • 1970-01-01
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多