2012-09-28 91 views
1

我有兩個表,XMLtablefilterTableT-SQL查詢中的XPath

我需要所有從XMLtable其中Col_X數據包含MyElementXMLtable.ID值,其中在filterTable匹配filterColumn內容。

Col_X的XML的每一行可以包含多個MyElement的,我想的是在ID情況下,任何這些元素的匹配任何值的filterColumn

問題是這些列實際上是varchar(max)數據類型,而表本身是巨大的(如50GB巨大)。所以這個查詢需要儘可能優化。

下面是我現在在哪裏的示例,它僅返回第一個匹配元素等於我正在查找的行中的行。由於過多的不同錯誤消息,我無法將其更改爲與我想要的所有相同命名元素進行比較。

SELECT ID, 
    CAST(Col_X AS XML).value('(//*[local-name()=''MyElement''])', N'varchar(25)') 
FROM XMLtable 

...然後將結果與filterTable進行比較。這已經花了5分鐘以上。

我想要實現的是一樣的東西:

SELECT ID 
FROM XMLtable 
WHERE CAST(Col_X AS XML).query('(//*[local-name()=''MyElement''])') 
    IN (SELECT filterColumn FROM filterTable) 

我目前能做到這一點的唯一方法是使用LIKE操作,這需要像長一千倍。

現在,顯然它不是開始更改列或其他任何數據類型的選項。這是我必須與之合作的。 :)

+1

你能告訴我們一個示例XML並解釋你想從中提取什麼嗎? –

+0

我不得不做一個,因爲顯然我不能在這裏給真正的XML。它有幾十到幾百行的各種元素,其中MyElement可能會在0到X次之間出現。我想爲它出現的行找到ID,任何一個出現的實例都包含另一個表中的任何值。 – Kahn

回答

3

試試這個:

SELECT 
    ID, 
    MyElementValue 
FROM 
    (
    SELECT ID, myE.value('(./text())[1]', N'VARCHAR(25)') AS 'MyElementValue' 
    FROM XMLTable 
     CROSS APPLY (SELECT CAST(Col_X AS XML)) as X(Col_X) 
     CROSS APPLY X.Col_X.nodes('(//*[local-name()="MyElement"])') as T2(myE) 
) T1 
WHERE MyElementValue IN (SELECT filterColumn FROM filterTable) 

這:

SELECT 
    ID, 
    MyElementValue 
FROM 
    (
    SELECT ID, myE.value('(./text())[1]', N'VARCHAR(25)') AS 'MyElementValue' 
    FROM XMLTable 
     CROSS APPLY (SELECT CAST(Col_X AS XML)) as X(Col_X) 
     CROSS APPLY X.Col_X.nodes('//MyElement') as T2(myE) 
) T1 
WHERE MyElementValue IN (SELECT filterColumn FROM filterTable) 

更新

我想在這裏說明Compute Scalars, Expressions and Execution Plan Performance您遇到。對XML的強制轉換推遲到每次調用value函數。您應該進行的測試是將Col_X的數據類型更改爲XML

如果不是,你可以查詢你的XMLTable需要到具有XML列的臨時表中的行,然後做上述對臨時表的查詢,而不需要轉換爲XML的選項。

CREATE TABLE #XMLTable 
(
    ID int, 
    Col_X xml 
) 

INSERT INTO #XMLTable(ID, Col_X) 
SELECT ID, Col_X 
FROM XMLTable 

SELECT 
     ID, 
     MyElementValue 
    FROM 
     (
     SELECT ID, myE.value('(./text())[1]', N'varchar(25)') AS 'MyElementValue' 
     FROM #XMLTable 
      CROSS APPLY Col_X.nodes('//MyElement') as T2(myE) 
    ) T1 
    WHERE MyElementValue IN (SELECT filterColumn FROM filterTable) 


DROP TABLE #XMLTable 
+0

嘿,非常感謝!下次我們回到這個腳本或者我有時間時,我一定會檢查一下。較低的一個將不起作用,因爲每個元素上的所有XML使用名稱空間,以及它們可能會因基本文檔而異。這就是爲什麼我在那裏使用本地名稱的原因。但除此之外,我總是對進一步優化感興趣,所以謝謝! :) – Kahn

+0

@Kahn - 的重要的變化是,我使用'(./text())[1]'來獲取價值,而不是僅僅'.'。它對查詢計劃中的估計成本有巨大的影響。如果它在執行時間上具有相同的效果,則必須對其進行測試。查詢的重構可能會有所幫助,因爲這隻會爲「MyElementValue」取一次而不是兩次。 –

+0

哦對,不知道。我是xpath新手,所以我不知道它如何工作的複雜性。所以解釋是讚賞。 :) – Kahn

1

你可以嘗試這樣的事情。我相信它至少在功能上做你想做的事。您必須憑經驗確定您的數據集來探索其性能。

SELECT ID 
FROM 
( 
    SELECT xt.ID, CAST(xt.Col_X AS XML) [content] FROM XMLTable AS xt 
) AS src 
INNER JOIN FilterTable AS f 
ON f.filterColumn IN 
(
    SELECT 
     elt.value('.', 'varchar(25)')  
    FROM src.content.nodes('//MyElement') AS T(elt) 
) 
+0

嘿,謝謝你。但是當我得到它的工作時,它的表現是我運行了2.5個小時並且不得不取消。它確實給了我如何進行的想法,儘管如此多的感謝! – Kahn

+0

我發現,添加基於使用CHARINDEX做的過濾器值的簡單字符串搜索(原VARCHAR列)附加JOIN條件有助於優化限制XML解析到行的一個更小的羣體。這在我的測試中導致查詢速度加快。 –

+0

好的,我得看看,謝謝! – Kahn

0

我終於得到了這個工作,性能比我想象的要好得多。以下是最終在5分鐘到6分鐘內產生正確結果的腳本。

SELECT ID, myE.value('.', N'VARCHAR(25)') AS 'MyElementValue' 
FROM (SELECT ID, CAST(Col_X AS XML) AS Col_X 
    FROM XMLTable) T1 
CROSS APPLY Col_X.nodes('(//*[local-name()=''MyElement''])' T2(myE) 
WHERE myE.value('.', N'varchar(25)') IN (SELECT filterColumn FROM filterTable) 

謝謝你們的幫助!

+0

這將是有趣的知道,如果添加CHARINDEX爲基礎的連接(看到我對我自己的答案評論)給你任何進一步的顯著改善。 –