XML do plano de consulta do SQL Server: comprimento QueryPlanHash

11

ATUALIZAÇÃO: Este é definitivamente um bug. Para detalhes completos, consulte este item de conexão .

Ao testar algumas alterações no sp_BlitzCache (divulgação completa, sou um dos autores), me deparei com o que achava ser um bug em nosso código.

Em um momento, estamos combinando o hash do plano de consulta para obter o custo da consulta. Fazemos isso da seguinte maneira:

statement.value('sum(/p:StmtSimple[xs:hexBinary(substring(@QueryHash, 3)) =
    xs:hexBinary(sql:column("b.QueryHash"))]/@StatementSubTreeCost)', 'float')

Até onde eu vi, isso funcionou. No entanto, em um caso estranho, a substring no XML estava gerando um NULLvalor, e o plano estava mostrando um custo de 0, apesar de bastante alto.

Analisando o plano de execução (divulgação completa, trabalho para a empresa que hospeda Colar o plano), notei que o hash do plano de consulta para o hash de um problema tinha 17 caracteres, enquanto o restante tem 18 anos. Aqui estão alguns exemplos:

QueryPlanHash = "0x4410B0CA640CDA89"
QueryPlanHash = "0x2262FEA4CE645569" 
QueryPlanHash = "0xED4F225CC0E97E5" - Problema!
QueryPlanHash = "0xBF878EEE6DB955EA"
QueryPlanHash = "0x263B53BC8C14A452"
QueryPlanHash = "0x89F5F146CF4B476F"
QueryPlanHash = "0xEF47EA40805C8961"
QueryPlanHash = "0xB7BE27D6E43677A5"
QueryPlanHash = "0x815C54EC43A6A6E9"

O Hash do plano de consulta está listado como um BINARY 8- presumivelmente, esse deve sempre ter o mesmo comprimento, mas o que um cara como eu sabe sobre valores binários?

Brincando com o XQuery um pouco, descobri que, alterando a substring para iniciar na segunda posição, ele apresentava um valor de hash válido (embora incorreto).

WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
SELECT   
        QueryPlanCost = statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float'),
        **q.n.value('substring(@QueryPlanHash, 2)', 'BINARY(8)')**
FROM    #statements s
CROSS APPLY s.statement.nodes('/p:StmtSimple') AS q(n)
OPTION(RECOMPILE);

WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
SELECT   
        QueryPlanCost = statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float'),
        **q.n.value('substring(@QueryPlanHash, 3)', 'BINARY(8)')**
FROM    #statements s
CROSS APPLY s.statement.nodes('/p:StmtSimple') AS q(n)
OPTION(RECOMPILE);

Nozes

Estou executando o SQL Server 2016, SP1 (13.0.4001).

Alguém já se deparou com isso antes?

17 caracteres têm um comprimento válido para um BINARY 8valor?

Parece um bug que deve receber um item do Connect?

Erik Darling
fonte

Respostas:

11

Eu acho que isso está acontecendo porque esse hash é um número ímpar de caracteres. Um válido VARBINARYprecisará ter um número par de "pares" para representar corretamente os dados. Então ... você deve ser capaz de resolver isso removendo o 0x, colocando um '0' no início, pegando os 18 caracteres certos e depois convertendo-o para VARBINARY.

CONVERT(VARBINARY(MAX), RIGHT('0' + SUBSTRING('0xED4F225CC0E97E5', 3, 20), 18), 2)

Se você quiser algo mais robusto, boa sorte, porque precisaria dividir por 2 como um número inteiro, obter o módulo de 2 e, em seguida, "fazer a coisa certa" para descobrir o tamanho dos dados.

Jeremiah Peschka
fonte