2016-12-07 8 views
2

나는 VARCHAR(MAX) 열에 레코드 변경에 대한 감사 정보를 저장하는 SQL 서버 2014 데이터베이스의 테이블이 (가난한 사람의 CDC를.)튜닝 SQL Server에서 XML 데이터를 구문 분석하는 쿼리 2014

이 데이터는 다음에 형식 :이 보이도록 내가 전치 데이터를 검색하기 위해 해당 정보를 분석 할 필요가

<span class="fieldname">Assigned To</span> 
    changed from <span class="oldvalue">user1</span> 
    to <span class="newvalue">user2</span><br /> 
<span class="fieldname">Status</span> 
    changed from <span class="oldvalue">QA</span> 
    to <span class="newvalue">Development</span><br /> 
<span class="fieldname">Progress</span> 
    changed from <span class="oldvalue">Yes</span> 
    to <span class="newvalue">No</span><br /> 
... 

과 같이 :

Record FieldName  OldValue NewValue 
------ ---------  -------- -------- 
1234  Assigned To user1  user2 
1234  Status   QA   Development 
1234  Progress  Yes  No 

저장 프로 시저에서 https://www.brentozar.com/pastetheplan/?id=rJF2GRB7g

대응 IO 통계 :

Table 'Trans'. Scan count 9, logical reads 27429, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 2964994, physical reads 0, read-ahead reads 0, lob logical reads 2991628, lob physical reads 0, lob read-ahead reads 0. 

이 여기

;WITH TT AS (
    SELECT TransId, 
     CAST('<root><rec>' + REPLACE(REPLACE(TransDescription, 'Ticket reopened... Status', 'Status'), '<br />', '</rec><rec>') + '</rec></root>' AS XML) TransXml 
    FROM dbo.Trans 
    WHERE TransDate >= '11/1/2016' 
     AND (TransDescription LIKE '%Ticket reopened... Status%' OR TransDescription LIKE '%Status%')) 
SELECT TransId, 
    TransXml, 
    FieldName = T.V.value('span[@class="fieldname"][1]', 'varchar(255)'), 
    OldValue = NULLIF(T.V.value('span[@class="oldvalue"][1]', 'varchar(255)'), 'nothing'), 
    NewValue = NULLIF(T.V.value('span[@class="newvalue"][1]', 'varchar(255)'), 'nothing') 
INTO #tmp 
FROM TT 
    CROSS APPLY TT.TransXml.nodes('root/rec') T(V); 

실행 계획이다 XML로 데이터를 변환 한 후 필요한 부분을 검색 XPath를 이용하여이 작업을 유혹 쿼리는 엄청나게 느리다. (예제는 단지 10 일분의 데이터에 불과했다.) 더 많은 데이터로 점진적으로 느려진다.

이 쿼리를 조정하기위한 나의 옵션에는 어떤 것이 있습니까?

+1

'% Status %'와 같은 TransDescription 만 필요하며 LIKE '% Ticket Reopened ... Status %'세트가 포함되어 있습니다. – HoneyBadger

+0

@HoneyBadger 그렇습니다. 선도적 인 '%'가 이글 거리지 않으므로 성능이 이미 저하되어 있습니다. 입출력 통계에 따르면, "작업 테이블"이 망치질입니다. – Chahk

+0

실제로 속도를 높이려면 xml 색인 생성이 필요합니다. 그러나 XML을 즉석에서 작성하기 때문에 이러한 일이 발생하지 않습니다. 효과적으로, 이것은 CROSS JOIN과 동등하며, 시간이 지남에 따라 기하 급수적으로 느려질 것입니다. –

답변

2

실제로 속도를 높이려면 xml 색인 생성이 필요합니다. 그러나 XML을 즉석에서 작성하기 때문에 이러한 일이 발생하지 않습니다. 효과적으로, 이것은 CROSS JOIN과 동등하며, 시간이 지남에 따라 기하 급수적으로 느려질 것입니다.

자세한 내용과 색인 생성에 도움이되는 방법은 cross apply xml query performs exponentially worse as xml document grows을 참조하십시오. XML을 통해이 작업을 수행하려면 실제로 XML을 저장해야 XML을 색인 할 수 있습니다.

1

CROSS JOIN은 테이블이 커질수록 중첩 루프의 "실행 횟수"가 기하 급수적으로 커짐에 따라 잘 확장되지 않는 것 중 하나입니다. 제출 한 실행 계획에서 각 수치는 0.6M을 초과했습니다. 논리적 인 읽기는 진정으로 낮지 만, 페이지는 계속해서 프로세스를 반복합니다. 은 (쿼리 크기가 어느 디스크에 당신의 버퍼 크기와 스풀을 넘쳐 경우, 당신이 진짜 상처를 경험하게 될 것입니다.를)

가 여기에 XML 인덱스를 활용할 수있는 솔루션입니다, 그것은 상황에 도움이 될 수 있습니다.

--PREPARE SAMPLE DATA 
DROP TABLE #Trans 
CREATE TABLE #Trans(TransID INT 
        ,TransDate DATE 
        ,TransDescription VARBINARY(MAX) 
        ) 
INSERT INTO #Trans VALUES 
(
1, '20160101' 
,CAST('<span class="fieldname">Assigned To</span> 
    changed from <span class="oldvalue">user1</span> 
    to <span class="newvalue">user2</span><br /> 
<span class="fieldname">Status</span> 
    changed from <span class="oldvalue">QA</span> 
    to <span class="newvalue">Development</span><br /> 
<span class="fieldname">Progress</span> 
    changed from <span class="oldvalue">Yes</span> 
    to <span class="newvalue">No</span><br />' AS varbinary(MAX))) 
,(2, '20160101' 
,CAST('<span class="fieldname">Assigned To</span> 
    changed from <span class="oldvalue">user1</span> 
    to <span class="newvalue">user2</span><br /> 
<span class="fieldname">Status</span> 
    changed from <span class="oldvalue">QA</span> 
    to <span class="newvalue">Development</span><br /> 
<span class="fieldname">Progress</span> 
    changed from <span class="oldvalue">Yes</span> 
    to <span class="newvalue">No</span><br />' AS varbinary(MAX))) 
,(3, '20160101' 
,CAST('<span class="fieldname">Assigned To</span> 
    changed from <span class="oldvalue">user1</span> 
    to <span class="newvalue">user2</span><br /> 
<span class="fieldname">Status</span> 
    changed from <span class="oldvalue">QA</span> 
    to <span class="newvalue">Development</span><br /> 
<span class="fieldname">Progress</span> 
    changed from <span class="oldvalue">Yes</span> 
    to <span class="newvalue">No</span><br />' AS varbinary(MAX))) 

--------------------------------------------------------------------------------------------------- 
--RUN BELOW THIS LINE COLLECTIVELY, THE ORIGINAL QUERY IS SHOWING UP WITH APPROX 93% OR OVERALL COST 

--BUILD A TEMP TABLE TO RECIEVE XML FORMATTED DATA 
DROP TABLE #XmlData 
CREATE TABLE #XmlData (
    TransId INT NOT NULL, 
    TransXml xml NOT NULL, 
CONSTRAINT [PK_XmlData] PRIMARY KEY CLUSTERED (TransId) 
) 

--INSERT DATA INTO XML TABLE 
INSERT INTO #XmlData 
SELECT TransId, 
    CAST('<root><rec>' + REPLACE(REPLACE(TransDescription, 'Ticket reopened... Status', 'Status'), '<br />', '</rec><rec>') + '</rec></root>' AS XML) TransXml 
FROM #Trans 
WHERE TransDate >= '11/1/2015' 
    AND (TransDescription LIKE '%Ticket reopened... Status%' OR TransDescription LIKE '%Status%') 

--CREATE AN XML INDEX 
CREATE PRIMARY XML INDEX PXML_TransXml 
ON #XmlData(TransXml) 

--APPLY NODES QUERY AGAINST XML INDEX 
SELECT TransId, 
    TransXml, 
    FieldName = T.V.value('span[@class="fieldname"][1]', 'varchar(255)'), 
    OldValue = NULLIF(T.V.value('span[@class="oldvalue"][1]', 'varchar(255)'), 'nothing'), 
    NewValue = NULLIF(T.V.value('span[@class="newvalue"][1]', 'varchar(255)'), 'nothing') 
FROM #XmlData TT 
    CROSS APPLY TT.TransXml.nodes('root/rec') T(V); 

--------------------------------- 
--Original Query 
;WITH TT AS (
    SELECT TransId, 
     CAST('<root><rec>' + REPLACE(REPLACE(TransDescription, 'Ticket reopened... Status', 'Status'), '<br />', '</rec><rec>') + '</rec></root>' AS XML) TransXml 
    FROM #Trans--dbo.Trans 
    WHERE TransDate >= '11/1/2015' 
     AND (TransDescription LIKE '%Ticket reopened... Status%' OR TransDescription LIKE '%Status%')) 

SELECT TransId, 
    TransXml, 
    FieldName = T.V.value('span[@class="fieldname"][1]', 'varchar(255)'), 
    OldValue = NULLIF(T.V.value('span[@class="oldvalue"][1]', 'varchar(255)'), 'nothing'), 
    NewValue = NULLIF(T.V.value('span[@class="newvalue"][1]', 'varchar(255)'), 'nothing') 
--INTO #tmp 
FROM TT 
    CROSS APPLY TT.TransXml.nodes('root/rec') T(V);