Minha pergunta é baseada nisso: https://stackoverflow.com/q/35575990/5089204
Para dar uma resposta lá, fiz o seguinte cenário de teste.
Cenário de teste
Primeiro, crio uma tabela de teste e a preenche com 100.000 linhas. Um número aleatório (0 a 1000) deve levar a ~ 100 linhas para cada número aleatório. Esse número é colocado em uma coluna varchar e como um valor em seu XML.
Então eu faço uma chamada como o OP que precisa com .exist () e .nodes () com uma pequena vantagem para o segundo, mas ambos levam de 5 a 6 segundos. Na verdade, eu faço as chamadas duas vezes: uma segunda vez em ordem trocada e com parâmetros de pesquisa ligeiramente alterados e com "// item" em vez do caminho completo para evitar falsos positivos por meio de resultados ou planos em cache.
Então eu crio um índice XML e faço as mesmas chamadas
Agora - o que realmente me surpreendeu! - o .nodes
com caminho completo é muito mais lento do que antes (9 segundos), mas o .exist()
é para baixo a metade de um segundo, com caminho completo mesmo para baixo a cerca de 0,10 segundos. (enquanto .nodes()
com caminho curto é melhor, mas ainda muito atrás .exist()
)
Questões:
Meus próprios testes são resumidos: índices XML podem explodir extremamente um banco de dados. Eles podem acelerar as coisas extremamente (s. Ed. 2), mas também podem atrasar suas consultas. Gostaria de entender como eles funcionam ... Quando alguém deve criar um índice XML? Por que .nodes()
com um índice pode ser pior do que sem? Como alguém poderia evitar o impacto negativo?
CREATE TABLE #testTbl(ID INT IDENTITY PRIMARY KEY, SomeData VARCHAR(100),XmlColumn XML);
GO
DECLARE @RndNumber VARCHAR(100)=(SELECT CAST(CAST(RAND()*1000 AS INT) AS VARCHAR(100)));
INSERT INTO #testTbl VALUES('Data_' + @RndNumber,
'<error application="application" host="host" type="exception" message="message" >
<serverVariables>
<item name="name1">
<value string="text" />
</item>
<item name="name2">
<value string="text2" />
</item>
<item name="name3">
<value string="text3" />
</item>
<item name="name4">
<value string="text4" />
</item>
<item name="name5">
<value string="My test ' + @RndNumber + '" />
</item>
<item name="name6">
<value string="text6" />
</item>
<item name="name7">
<value string="text7" />
</item>
</serverVariables>
</error>');
GO 100000
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_no_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_no_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_no_index;
GO
CREATE PRIMARY XML INDEX PXML_test_XmlColum1 ON #testTbl(XmlColumn);
CREATE XML INDEX IXML_test_XmlColumn2 ON #testTbl(XmlColumn) USING XML INDEX PXML_test_XmlColum1 FOR PATH;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_with_index;
GO
DECLARE @d DATETIME=GETDATE();
SELECT *
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_with_index;
GO
DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_with_index;
GO
DROP TABLE #testTbl;
EDIT 1 - Resultados
Este é um resultado com o SQL Server 2012 instalado localmente em um laptop médio. Neste teste, não pude reproduzir o impacto extremamente negativo NodesFullPath_with_index
, embora seja mais lento do que sem o índice ...
NodesFullPath_no_index 6.067
ExistFullPath_no_index 6.223
ExistShortPath_no_index 8.373
NodesShortPath_no_index 6.733
NodesFullPath_with_index 7.247
ExistFullPath_with_index 0.217
ExistShortPath_with_index 0.500
NodesShortPath_with_index 2.410
EDIT 2 Teste com XML maior
De acordo com a sugestão do TT, usei o XML acima, mas copiei os item
-nodes para alcançar cerca de 450 itens. Eu deixei o nó da ocorrência estar muito alto no XML (porque acho que isso .exist()
iria parar na primeira ocorrência, enquanto .nodes()
continuaria)
A criação do índice XML expandiu o arquivo mdf para ~ 21GB, ~ 18GB parecem pertencer ao índice (!!!)
NodesFullPath_no_index 3min44
ExistFullPath_no_index 3min39
ExistShortPath_no_index 3min49
NodesShortPath_no_index 4min00
NodesFullPath_with_index 8min20
ExistFullPath_with_index 8,5 seconds !!!
ExistShortPath_with_index 1min21
NodesShortPath_with_index 13min41 !!!
.nodes()
e.exist()
são convincentes. Tambémfull path search
é fácil entender o fato de que o índice com é mais rápido. Isso significaria: Se você criar um índice XML, sempre deve estar ciente da influência negativa de qualquer XPath genérico (//
ou*
ou..
ou[filter]
ou qualquer coisa que não seja simplesmente o Xpath simples ...). Na verdade, você deve usar o caminho completo única - muito grande de volta desenhar ...