Suponha que eu tenha os seguintes dados XML, onde
<root>
pode ter um número arbitrário de<child>
filhos<child>
pode ter um número arbitrário de<grandchild>
filhos<grandchild>
pode ter um número arbitrário de<greatgrandchild>
filhos<child>
e<grandchild>
pode ter um número arbitrário de<parameter>
filhos- cada
<parameter>
um tem um<name>
e um<value>
filho - a profundidade pára no bisneto (não há necessidade de profundidades arbitrárias)
Por exemplo,
<root name="root">
<child name="a">
<grandchild name="a1">
<parameter>
<name>param1</name>
<value>ABC123</value>
</parameter>
<parameter>
<name>param2</name>
<value>CBC</value>
</parameter>
<greatgrandchild name="a1a">
<parameter>
<name>paramA</name>
<value>ABC</value>
</parameter>
<parameter>
<name>paramB</name>
<value>DBC</value>
</parameter>
</greatgrandchild>
</grandchild>
</child>
</root>
O que preciso fazer é selecionar o parâmetro com um valor contendo "_BC", onde _ é um caractere que não é A.
tenho a seguinte consulta
SELECT
TestId,
[root],
child,
grandchild,
NULL AS greatgrandchild,
parameter
FROM (
SELECT
TestId,
[root].a.value('@name', 'varchar(max)') AS [root],
child.b.value('@name', 'varchar(max)') AS child,
grandchild.c.value('@name', 'varchar(max)') AS grandchild,
parameter.d.value('(name)[1]', 'varchar(max)') AS parameter,
parameter.d.value('(value)[1]', 'varchar(max)') AS parameter_value
FROM
dbo.TestTable
CROSS APPLY TestData.nodes('/root') AS [root](a)
CROSS APPLY [root].a.nodes('child') AS child(b)
CROSS APPLY child.b.nodes('grandchild') AS grandchild(c)
CROSS APPLY grandchild.c.nodes('parameter') AS parameter(d)
) t
WHERE
parameter_value LIKE '%[^Aa]BC%'
UNION ALL
SELECT
TestId,
[root],
child,
grandchild,
greatgrandchild,
parameter
FROM (
SELECT
TestId,
[root].a.value('@name', 'varchar(max)') AS [root],
child.b.value('@name', 'varchar(max)') AS child,
grandchild.c.value('@name', 'varchar(max)') AS grandchild,
greatgrandchild.d.value('@name', 'varchar(max)') AS greatgrandchild,
parameter.e.value('(name)[1]', 'varchar(max)') AS parameter,
parameter.e.value('(value)[1]', 'varchar(max)') AS parameter_value
FROM
dbo.TestTable
CROSS APPLY TestData.nodes('/root') AS [root](a)
CROSS APPLY [root].a.nodes('child') AS child(b)
CROSS APPLY child.b.nodes('grandchild') AS grandchild(c)
CROSS APPLY grandchild.c.nodes('greatgrandchild') AS greatgrandchild(d)
CROSS APPLY greatgrandchild.d.nodes('parameter') AS parameter(e)
) t
WHERE
parameter_value LIKE '%[^Aa]BC%'
Por exemplo, para os seguintes dados de teste:
CREATE TABLE TestTable (
TestId INT PRIMARY KEY,
TestData XML NOT NULL
);
INSERT INTO dbo.TestTable (TestId, TestData)
VALUES (1, '<root name="root">
<child name="a">
<grandchild name="a1">
<parameter>
<name>param1</name>
<value>ABC123</value>
</parameter>
<parameter>
<name>param2</name>
<value>CBC</value>
</parameter>
<greatgrandchild name="a1a">
<parameter>
<name>paramA</name>
<value>ABC</value>
</parameter>
<parameter>
<name>paramB</name>
<value>DBC</value>
</parameter>
</greatgrandchild>
</grandchild>
</child>
</root>');
o seguinte conjunto de resultados deve ser retornado:
TestId root child grandchild greatgrandchild parameter
------------------------------------------------------
1 root a a1 NULL param2
1 root a a1 a1a paramB
Estou me perguntando se minha consulta SELECT pode ser otimizada
Em uma tabela de 400 linhas com dados XML de 20 KB cada linha, a consulta SELECT leva 40 minutos. O layout XML não pode ser alterado.
Aqui está uma reescrita do que você já tem.
text()
nó para o nome do parâmetro e o valor do parâmetro.Em meus testes limitados, isso o tornou significativamente mais rápido. Estou ansioso para ver se algum ganho de desempenho você vê do seu lado.
Atualizar:
Tomei a liberdade de executar o test rig fornecido pelo wBob no SQL Server 2014 com nível de compatibilidade 110 (SQL Server 2012) e 120 (SQL Server 2014)
Resultado:
A diferença que você vê no tempo de execução existe porque o SQL Server 2014 usa um novo estimador de cardinalidade. Quando o nível de compatibilidade for 110, o SQL Server usará o estimador antigo. Ativar ou desativar o novo estimador de cardinalidade usando sinalizadores de rastreamento produz exatamente o mesmo resultado.
A diferença de tempos para as consultas que não utilizam os índices XML é porque no nível de Compatibilidade 120 os planos são paralelos com DOP 16 no meu caso.
Também é muito interessante notar que o tempo de execução para usar índices XML é cinco vezes mais lento quando se usa o novo estimador de cardinalidade. A razão para isso é a mesma acima, apenas o contrário. Há um plano paralelo apenas ao usar o antigo estimador de cardinalidade.
Obtenho um bom resultado com uma coleção de esquema XML e um índice XML primário. Não acho que o XSC esteja aumentando o desempenho em particular, mas é uma boa restrição de se ter.
Meu equipamento de teste completo:
Todas as consultas funcionam bem com o índice xml primário. Obviamente há uma penalidade de espaço em disco com o índice XML, no meu rig a tabela passou de ~3MB para ~24MB!
I can't see secondary xml indexes or selective xml indexes adding a lot to this (as you are basically traversing the whole document) but might give them a try if I get time.