Convertendo um VARCHAR em VARBINARY

17

Eu tenho mantido um registro de consultas em execução caras, juntamente com seus planos de consulta, em uma tabela para nos permitir monitorar tendências de desempenho e identificar áreas que precisam ser otimizadas.

No entanto, chegou ao ponto em que os planos de consulta estão ocupando muito espaço (pois estamos armazenando o plano inteiro em cada consulta).

Portanto, estou tentando normalizar os dados existentes extraindo o QueryPlanHash e QueryPlan para outra tabela.

CREATE TABLE QueryPlans
(
    QueryPlanHash VARBINARY(25),
    QueryPlan XML,
    CONSTRAINT PK_QueryPlans PRIMARY KEY
    (
      QueryPlanHash
    )
);

Como a definição do query_plan_hash in sys.dm_exec_query_statsé um campo binário (e eu regularmente inserirei novos dados), eu estava usando VARBINARYo tipo de dados em minha nova tabela.

No entanto, a inserção abaixo falha ...

INSERT INTO QueryPlans
    ( QueryPlanHash, QueryPlan )
SELECT queryplanhash, queryplan
FROM
(
    SELECT 
      p.value('(./@QueryPlanHash)[1]', 'varchar(20)') queryplanhash,
      QueryPlan,
      ROW_NUMBER() OVER (PARTITION BY p.value('(./@QueryPlanHash)[1]', 'varchar(20)') ORDER BY DateRecorded) rownum
    FROM table
    CROSS APPLY QueryPlan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple[@QueryPlanHash]') t(p)
) data
WHERE rownum = 1

.... com o erro

Implicit conversion from data type varchar to varbinary is not allowed. Use the CONVERT function to run this query.

O problema é que os hashes do plano de consulta já estão em formato binário, no entanto, armazenados como VARCHAR no plano de consulta XML, por exemplo

0x9473FBCCBC01AFE

e CONVERT para BINARY fornece um valor completamente diferente

0x3078393437334642434342433031414645

Tentei alterar a definição de valor no XQuery select para binário, mas ele não retornou valores.

Como extrair o valor de 0x9473FBCCBC01AFEum plano de consulta XML como um VARBINARY, e não como VARCHAR?

Mark Sinkinson
fonte

Respostas:

28

Você precisa usar um estilo específico quando espera manter o mesmo valor binário ao converter de uma string. Caso contrário, o SQL Server tentará codificar a string da mesma maneira que codificaria 'bob'ou 'frank'.

Dito isto, sua string de entrada não parece correta - há um byte ausente ou um byte em excesso. Isso funciona bem se eu largar o final E:

SELECT CONVERT(VARBINARY(25), '0x9473FBCCBC01AF', 1);
------------ the ,1 is important ---------------^^^

O resultado é binário:

----------------
0x9473FBCCBC01AF
Aaron Bertrand
fonte
1
Ah, ,1era o que eu estava perdendo. Isso foi mais fácil do que eu esperava! Obrigado!
Mark Sinkinson
Não tenho certeza sobre o byte ausente / extra. Nos 2666 registros que tenho, há 183 que falham noTRY_CONVERT
Mark Sinkinson
Pode ser necessário anexar um caractere (digamos 0) a qualquer sequência com uma contagem de caracteres ímpares. Isso muda o valor, mas sempre deve mudar o mesmo valor da mesma maneira (e não suspeito que você tenha colisões com ou sem o 0).
Aaron Bertrand
Isso não é um bug? O queryplanhash no xml é explicitamente definido para esse valor ... Certamente um TRY_CONVERTa um BINARYnão deve voltarNULL
Mark Sinkinson
Ao comparar os valores salvos na minha tabela com o xml, na verdade é um 0 inicial que está faltando. Portanto, o valor deve ser 0x09473FBCCBC01AF. Posso corrigir aqueles com um simples substituir, mas eu tenho certeza que é um erro ...
Mark Sinkinson
0

Como eu extrairia o valor 0x9473FBCCBC01AFE de um plano de consulta XML como um VARBINARY, em vez de um VARCHAR?

Enfrentei algo assim usando o HeidiSQL para consultar tabelas CASD e resolvi com fn_varbintohexstr () , assim:

SELECT master.dbo.fn_varbintohexstr(table.hexfield) FROM table;

Com o HeidiSQL, o valor estava errado como '0x3F3F3F3F3F3F3F3F' e se tornou correto como '0x158B1DB75616484695684007CE98E21C'.

OBS: Funciona desde o MSSQL 2008! Espero que ajude!

MMJ
fonte
2
Observe as advertências de uso fn_varbintohexstr() mencionadas aqui .
Erik