Ein Teil meiner Arbeitslast verwendet eine CLR-Funktion, die den Spooky-Hash-Algorithmus implementiert , um Zeilen zu vergleichen und festzustellen, ob sich Spaltenwerte geändert haben. Die CLR-Funktion verwendet eine Binärzeichenfolge als Eingabe, sodass ich eine schnelle Möglichkeit zum Konvertieren von Zeilen in eine Binärzeichenfolge benötige. Ich gehe davon aus, dass ich während des gesamten Workloads ungefähr 10 Milliarden Zeilen hashe, damit dieser Code so schnell wie möglich ist.
Ich habe ungefähr 300 Tabellen mit verschiedenen Schemata. Für die Zwecke dieser Frage nehmen Sie bitte eine einfache Tabellenstruktur von 32 nullfähigen INT
Spalten an. Am Ende dieser Frage habe ich Beispieldaten sowie eine Möglichkeit zum Benchmarking der Ergebnisse angegeben.
Zeilen müssen in dieselbe Binärzeichenfolge konvertiert werden, wenn alle Spaltenwerte identisch sind. Zeilen müssen in andere Binärzeichenfolgen konvertiert werden, wenn sich ein Spaltenwert unterscheidet. Ein so einfacher Code wie der folgende funktioniert beispielsweise nicht:
CAST(COL1 AS BINARY(4)) + CAST(COL2 AS BINARY(4)) + ..
NULLs werden nicht korrekt verarbeitet. Wenn COL1
für Zeile 1 COL2
NULL und für Zeile 2 NULL ist, werden beide Zeilen in eine NULL-Zeichenfolge konvertiert. Ich glaube, dass der richtige Umgang mit NULL-Werten der schwierigste Teil ist, um die gesamte Zeile richtig zu konvertieren. Alle zulässigen Werte für die INT-Spalten sind möglich.
So vermeiden Sie einige Fragen:
- Wenn es darauf ankommt, sind die Spalten die meiste Zeit (90% +) nicht NULL.
- Ich muss die CLR verwenden.
- Ich muss so viele Zeilen hacken. Ich kann die Hashes nicht durchhalten.
- Ich glaube, dass ich wegen der CLR-Funktion den Batch-Modus nicht für die Konvertierung verwenden kann.
Was ist der schnellste Weg, um 32 nullfähige INT
Spalten in eine BINARY(X)
oder eine VARBINARY(X)
Zeichenkette umzuwandeln ?
Beispieldaten und Code wie versprochen:
-- 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;
(Ich werde weiterhin den spukhaften Hash für dieses Binärergebnis verwenden. Die Workload verwendet Hash-Joins und der Hash-Wert wird für einen der Hash-Builds verwendet. Ich möchte keinen langen Binärwert im Hash-Build, da er zu viel erfordert Erinnerung.)