Parte da minha carga de trabalho usa uma função CLR que implementa o algoritmo de hash assustador para comparar linhas e verificar se algum valor de coluna foi alterado. A função CLR usa uma string binária como entrada, portanto, preciso de uma maneira rápida de converter linhas em uma string binária. Eu espero que o hash fique em torno de 10 bilhões de linhas durante toda a carga de trabalho, então eu gostaria que esse código fosse o mais rápido possível.
Eu tenho cerca de 300 tabelas com esquemas diferentes. Para os fins desta pergunta, assuma uma estrutura de tabela simples de 32 INT
colunas anuláveis . Forneci dados de amostra e também uma maneira de comparar os resultados na parte inferior desta pergunta.
As linhas devem ser convertidas na mesma cadeia binária se todos os valores da coluna forem iguais. As linhas devem ser convertidas em cadeias binárias diferentes se qualquer valor da coluna for diferente. Por exemplo, código simples como o seguinte não funcionará:
CAST(COL1 AS BINARY(4)) + CAST(COL2 AS BINARY(4)) + ..
Ele não manipula NULLs corretamente. Se COL1
for NULL para a linha 1 e COL2
NULL para a linha 2, as duas linhas serão convertidas em uma seqüência de caracteres NULL. Eu acredito que o manuseio correto de NULLs é a parte mais difícil de converter a linha inteira corretamente. Todos os valores permitidos para as colunas INT são possíveis.
Para antecipar algumas perguntas:
- Se isso importa, na maioria das vezes (90% +), as colunas não serão NULL.
- Eu tenho que usar o CLR.
- Eu tenho que hash tantas linhas. Não consigo persistir com os hashes.
- Acredito que não posso usar o modo em lote para a conversão devido à presença da função CLR.
Qual é a maneira mais rápida de converter 32 INT
colunas anuláveis em uma BINARY(X)
ou VARBINARY(X)
sequência?
Amostra de dados e código conforme prometido:
-- create sample data
DROP TABLE IF EXISTS dbo.TABLE_OF_32_INTS;
CREATE TABLE dbo.TABLE_OF_32_INTS (
COL1 INT NULL,
COL2 INT NULL,
COL3 INT NULL,
COL4 INT NULL,
COL5 INT NULL,
COL6 INT NULL,
COL7 INT NULL,
COL8 INT NULL,
COL9 INT NULL,
COL10 INT NULL,
COL11 INT NULL,
COL12 INT NULL,
COL13 INT NULL,
COL14 INT NULL,
COL15 INT NULL,
COL16 INT NULL,
COL17 INT NULL,
COL18 INT NULL,
COL19 INT NULL,
COL20 INT NULL,
COL21 INT NULL,
COL22 INT NULL,
COL23 INT NULL,
COL24 INT NULL,
COL25 INT NULL,
COL26 INT NULL,
COL27 INT NULL,
COL28 INT NULL,
COL29 INT NULL,
COL30 INT NULL,
COL31 INT NULL,
COL32 INT NULL
);
INSERT INTO dbo.TABLE_OF_32_INTS WITH (TABLOCK)
SELECT 0, 123, 12345, 1234567, 123456789
, 0, 123, 12345, 1234567, 123456789
, 0, 123, 12345, 1234567, 123456789
, 0, 123, 12345, 1234567, 123456789
, 0, 123, 12345, 1234567, 123456789
, 0, 123, 12345, 1234567, 123456789
, NULL, -876545321
FROM
(
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
) q
OPTION (MAXDOP 1);
GO
-- procedure to test performance
CREATE OR ALTER PROCEDURE #p AS
BEGIN
SET NOCOUNT ON;
DECLARE
@counter INT = 0,
@dummy VARBINARY(8000);
WHILE @counter < 10
BEGIN
SELECT @dummy = -- this code is clearly incomplete as it does not handle NULLs
CAST(COL1 AS BINARY(4)) +
CAST(COL2 AS BINARY(4)) +
CAST(COL3 AS BINARY(4)) +
CAST(COL4 AS BINARY(4)) +
CAST(COL5 AS BINARY(4)) +
CAST(COL6 AS BINARY(4)) +
CAST(COL7 AS BINARY(4)) +
CAST(COL8 AS BINARY(4)) +
CAST(COL9 AS BINARY(4)) +
CAST(COL10 AS BINARY(4)) +
CAST(COL11 AS BINARY(4)) +
CAST(COL12 AS BINARY(4)) +
CAST(COL13 AS BINARY(4)) +
CAST(COL14 AS BINARY(4)) +
CAST(COL15 AS BINARY(4)) +
CAST(COL16 AS BINARY(4)) +
CAST(COL17 AS BINARY(4)) +
CAST(COL18 AS BINARY(4)) +
CAST(COL19 AS BINARY(4)) +
CAST(COL20 AS BINARY(4)) +
CAST(COL21 AS BINARY(4)) +
CAST(COL22 AS BINARY(4)) +
CAST(COL23 AS BINARY(4)) +
CAST(COL24 AS BINARY(4)) +
CAST(COL25 AS BINARY(4)) +
CAST(COL26 AS BINARY(4)) +
CAST(COL27 AS BINARY(4)) +
CAST(COL28 AS BINARY(4)) +
CAST(COL29 AS BINARY(4)) +
CAST(COL30 AS BINARY(4)) +
CAST(COL31 AS BINARY(4)) +
CAST(COL32 AS BINARY(4))
FROM dbo.TABLE_OF_32_INTS
OPTION (MAXDOP 1);
SET @counter = @counter + 1;
END;
SELECT cpu_time
FROM sys.dm_exec_requests
WHERE session_id = @@SPID;
END;
GO
-- run procedure
EXEC #p;
(Continuarei usando o hash assustador nesse resultado binário. A carga de trabalho usa junções de hash e o valor do hash é usado para uma das compilações de hash. Não quero um valor binário longo na compilação de hash, pois exige muito memória.)
fonte
Se você pode garantir antecipadamente que não armazena algum int em particular, por exemplo
-2,147,483,648
, pode fazer algo como:fonte