SQL Server Query Plan XML: QueryPlanHash Length

11

AKTUALIZACJA: To zdecydowanie błąd. Aby uzyskać szczegółowe informacje, zobacz ten przedmiot Connect .

Podczas testowania niektórych zmian w sp_BlitzCache (pełne ujawnienie, jestem jednym z autorów), natknąłem się na coś, co moim zdaniem było błędem w naszym kodzie.

W pewnym momencie dopasowujemy skrót planu zapytania, aby uzyskać koszt zapytania. Robimy to tak:

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

O ile widziałem, to zadziałało. Jednak w jednym dziwnym przypadku podciąg w pliku XML generował NULLwartość, a plan wykazywał koszt 0, mimo że był dość wysoki.

Zagłębiając się w plan wykonania (pełne ujawnienie, pracuję dla firmy, która jest gospodarzem Wklej plan), zauważyłem, że skrót planu zapytań dla jednego problemowego skrótu miał 17 znaków, a pozostałe 18. Oto przykłady:

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

Hash planu zapytań jest wymieniony jako BINARY 8- przypuszczalnie powinna zawsze mieć tę samą długość, ale co taki facet jak ja wie o wartościach binarnych?

Grając trochę z XQuery, odkryłem, że zmieniając podłańcuch, aby rozpocząć od drugiej pozycji, uzyskałby prawidłową (choć niepoprawną) wartość skrótu.

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);

Orzechy

Korzystam z programu SQL Server 2016, SP1 (13.0.4001).

Czy ktoś już na to wpadł?

Czy 17 znaków jest prawidłową długością BINARY 8wartości?

Czy to wygląda na błąd, który powinien otrzymać element Connect?

Erik Darling
źródło

Odpowiedzi:

11

Myślę, że tak się dzieje, ponieważ ten jeden hash to nieparzysta liczba znaków. Ważny VARBINARYbędzie musiał mieć parzystą liczbę „par”, aby poprawnie reprezentować dane. Więc ... powinieneś być w stanie rozwiązać ten problem, usuwając 0x, umieszczając „0” na początku, chwytając odpowiednie 18 znaków, a następnie rzucając je VARBINARY.

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

Jeśli chcesz czegoś bardziej niezawodnego, powodzenia, ponieważ musisz podzielić przez 2 jako liczbę całkowitą i uzyskać moduł 2, a następnie „zrobić właściwą rzecz”, aby dowiedzieć się, jak duże powinny być twoje dane.

Jeremiasz Peschka
źródło