Une partie de ma charge de travail utilise une fonction CLR qui implémente l'algorithme de hachage fantasmagorique pour comparer les lignes pour voir si des valeurs de colonne ont changé. La fonction CLR prend une chaîne binaire en entrée, j'ai donc besoin d'un moyen rapide pour convertir des lignes en chaîne binaire. Je m'attends à hacher environ 10 milliards de lignes pendant toute la charge de travail, je voudrais donc que ce code soit aussi rapide que possible.
J'ai environ 300 tables avec différents schémas. Pour les besoins de cette question, supposons une structure de table simple de 32 INT
colonnes nullables . J'ai fourni des exemples de données ainsi qu'un moyen de comparer les résultats au bas de cette question.
Les lignes doivent être converties dans la même chaîne binaire si toutes les valeurs de colonne sont identiques. Les lignes doivent être converties en différentes chaînes binaires si une valeur de colonne est différente. Par exemple, un code aussi simple que le suivant ne fonctionnera pas:
CAST(COL1 AS BINARY(4)) + CAST(COL2 AS BINARY(4)) + ..
Il ne gère pas correctement les valeurs NULL. Si COL1
est NULL pour la ligne 1 et COL2
NULL pour la ligne 2, les deux lignes seront converties en une chaîne NULL. Je crois que la gestion correcte des valeurs NULL est la partie la plus difficile de convertir correctement la ligne entière. Toutes les valeurs autorisées pour les colonnes INT sont possibles.
Pour anticiper certaines questions:
- Si cela est important, la majorité du temps (90% +) les colonnes ne seront pas NULL.
- Je dois utiliser le CLR.
- Je dois hacher autant de lignes. Je ne peux pas persister les hachages.
- Je pense que je ne peux pas utiliser le mode batch pour la conversion en raison de la présence de la fonction CLR.
Quel est le moyen le plus rapide pour convertir 32 INT
colonnes nullables en une chaîne BINARY(X)
ou VARBINARY(X)
?
Exemples de données et de code comme promis:
-- 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;
(J'utiliserai toujours le hachage fantasmagorique sur ce résultat binaire. La charge de travail utilise des jointures de hachage et la valeur hachée est utilisée pour l'une des générations de hachage. Je ne veux pas d'une longue valeur binaire dans la construction de hachage car elle nécessite trop Mémoire.)