2016-06-28 118 views
2

我有一張旨在跟蹤更改的表格。將XML數據轉換爲行和列

CREATE TABLE ChangeTracker 
(
    ChangeId BIGINT NOT NULL identity(1, 1) PRIMARY KEY, 
    ChangeDate DATETIME NOT NULL DEFAULT getdate(), 
    Changes VARCHAR(max) NOT NULL DEFAULT '' 
) 

Changes的數據是以下格式:

<span class="fieldname">AssignedTo</span> 
<span class="oldvalue">user1</span> 
<span class="newvalue">user2</span> 
<br /> 
<span class="fieldname">Attachments</span> 
<br /> 
<span class="fieldname">Status</span> 
<span class="oldvalue">new</span> 
<span class="newvalue">open</span> 
<br /> 
<span class="fieldname">Priority</span> 
<span class="oldvalue">low</span> 
<span class="newvalue">high</span> 
<br /> 
... 

注意,一些變化有字段名,OLDVALUE,NEWVALUE對,而一些具有唯一字段名。所有更改也由<br />標記分隔。

所以,當我想在一個特定的字段名的變化(比如狀態),我可以使用下面的查詢可以這樣做:

SELECT * FROM 
(
    SELECT ChangeId, 
     ChangeDate, 
     TransDesc.value('(/root/span[@class="fieldname"]/text())[1]', 'varchar(255)') as FieldName, 
     TransDesc.value('(/root/span[@class="oldvalue"]/text())[1]', 'varchar(255)') AS OldValue, 
     TransDesc.value('(/root/span[@class="newvalue"]/text())[1]', 'varchar(255)') AS NewValue 
    FROM (
     SELECT *, TransDesc = CAST('<root>' + SUBSTRING(ChangeA, 0, CHARINDEX('<br />', ChangeA)) + '</root>' AS XML) 
     FROM 
     (
      SELECT *, ChangeA = SUBSTRING(Changes, CHARINDEX('<span class="fieldname">Status</span>', Changes), 4000) 
      FROM ChangeTracker 
      WHERE CHARINDEX('<span class="fieldname">Status</span>', Changes) > 0 
      ) TTX 
     ) TT 
) x 

這給了我以下結果:

ChangeId | ChangeDate    | FieldName | OldValue | NewValue 
———————————————————————————————————————————————————————————————————— 
1  | 2016-06-28 18:37:24.403 | Status | new  | open 

現在我想要一個查詢(創建一個視圖)來獲取所有更改。所以輸出看起來是這樣的:

ChangeId | ChangeDate    | FieldName | OldValue | NewValue 
———————————————————————————————————————————————————————————————————— 
1  | 2016-06-28 18:37:24.403 | AssignedTo | user1 | user2 
1  | 2016-06-28 18:37:24.403 | Attachments | NULL  | NULL 
1  | 2016-06-28 18:37:24.403 | Status  | new  | open 
1  | 2016-06-28 18:37:24.403 | Priority | low  | high 

回答

2

這是非常好的,你有<br />作爲分隔符。這樣你就可以得到你想要的東西。

;with tbl as (
select ChangeId,ChangeDate, 
--build xml 
cast('<root><rec>'+replace(Changes,'<br />','</rec><rec>')+'</rec></root>' as xml) x 
from ChangeTracker 
) 
select ChangeId, ChangeDate, 
t.v.value('span[@class="fieldname"][1]','varchar(50)') fieldname, 
t.v.value('span[@class="oldvalue"][1]','varchar(50)') oldvalue, 
t.v.value('span[@class="newvalue"][1]','varchar(50)') newvalue 

from tbl cross apply tbl.x.nodes('root/rec') t(v) --convert to tabular form 
where t.v.value('span[@class="fieldname"][1]','varchar(50)') is not null 
+0

感謝。這真的很有幫助。 –

2

我們可以用逗號分割的方式來做。在這裏,我們只需要將字符串拆分爲「< br/>」。

例如: -

SELECT CAST(item AS XML) 
FROM dbo.SplitString(@str, '<br />') AS [Changes] 
WHERE LEN(item) > 0 

結果: - Result

所以,最終的查詢將是: -

INSERT INTO ChangeTracker 
SELECT GETDATE() 
,tb.xmldata.value('(/span[@class="fieldname"]/text())[1]', 'varchar(255)') AS fieldName 
,tb.xmldata.value('(/span[@class="oldvalue"]/text())[1]', 'varchar(255)') AS oldValue 
,tb.xmldata.value('(/span[@class="newvalue"]/text())[1]', 'varchar(255)') AS newValue 
FROM(
    SELECT CAST(Item AS XML) AS xmldata 
    FROM dbo.SplitString(@str, '<br />') AS [Changes] 
    WHERE LEN(item) > 0 
) AS tb 

最終結果: - Final Result

注: - 您需要創建分割功能,請找below.BEGIN

CREATE FUNCTION SplitString 
    ( 
      @Input NVARCHAR(MAX), 
      @Character NVARCHAR(10) 
    ) 
    RETURNS @Output TABLE (
      Item NVARCHAR(1000) 
    ) 
    AS 
    BEGIN 
      DECLARE @StartIndex INT, @EndIndex INT 

      SET @StartIndex = 1 
      IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character 
      BEGIN 
       SET @Input = @Input + @Character 
      END 

      WHILE CHARINDEX(@Character, @Input) > 0 
      BEGIN 
       SET @EndIndex = CHARINDEX(@Character, @Input) 

       INSERT INTO @Output(Item) 
       SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1) 

       SET @Input = SUBSTRING(@Input, @EndIndex + LEN(@Character), LEN(@Input)) 
      END 

      RETURN 
    END 
+0

謝謝。這真的很有幫助。不幸的是,該網站不允許標記超過1個答覆作爲答案! –