2

我有一个存储在 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 
4

3 回答 3

1

如果您知道 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 列。

于 2013-05-05T16:46:30.803 回答
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")') 
于 2013-05-04T14:47:05.340 回答
0

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

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

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;
于 2013-05-04T15:48:18.597 回答