Hash Surrogate Key Benchmark

This benchmark compares the join in Snowflake performance of surrogate keys generated via md5 versus surrogate keys generated via md5_number_lower64. Run this in a schema you have write permissions on and on a dedicated warehouse to avoid interference with the results.

create or replace transient table benchmark_dim as
select
    row_number() over (order by null) as natural_key
    , md5(natural_key::varchar) as sk_md5
    , md5_number_lower64(natural_key::varchar) as sk_int64
    , 'Attribute_' || natural_key as attr_1
    , current_date - mod(natural_key, 365) as attr_date
from table(generator(rowcount => 100000));

create or replace transient table benchmark_fact as
select
    row_number() over (order by null) as fact_id
    , mod(fact_id - 1, 100000) + 1 as dim_natural_key
    , md5(dim_natural_key::varchar) as fk_md5
    , md5_number_lower64(dim_natural_key::varchar) as fk_int64
    , uniform(1, 1000, random()) as measure_1
    , uniform(1::float, 100::float, random()) as measure_2
    , current_date - mod(fact_id, 730) as event_date
from table(generator(rowcount => 10000000));

create or replace transient table benchmark_results (
    run_id int
    , join_type varchar(20)
    , execution_time_ms int
    , compilation_time_ms int
    , bytes_scanned bigint
    , query_id varchar
    , ts timestamp_ntz default current_timestamp()
);

-- disable caching to avoid skewing results
alter session set use_cached_result = false;

create or replace procedure run_benchmark()
returns varchar
language sql
execute as caller
as
declare
    num_runs int := 10;
    run int;
    join_types array := ['md5', 'int64'];
    jt varchar;
    i int;
    qid varchar;
begin
    for run in 1 to num_runs do
        -- shuffle order each run by alternating start point
        for i in 0 to 1 do
            jt := join_types[mod(run + i, 2)];
 
            if (jt = 'md5') then
                select d.attr_1, sum(f.measure_1), avg(f.measure_2)
                from benchmark_fact f
                join benchmark_dim d on f.fk_md5 = d.sk_md5
                where f.event_date >= '2024-01-01'
                group by 1 order by 2 desc limit 100;
            else
                select d.attr_1, sum(f.measure_1), avg(f.measure_2)
                from benchmark_fact f
                join benchmark_dim d on f.fk_int64 = d.sk_int64
                where f.event_date >= '2024-01-01'
                group by 1 order by 2 desc limit 100;
            end if;

            qid := last_query_id();
 
            insert into benchmark_results (run_id, join_type, execution_time_ms, compilation_time_ms, bytes_scanned, query_id)
            select
                :run,
                :jt,
                execution_time,
                compilation_time,
                bytes_scanned,
                query_id
            from table(information_schema.query_history())
            where query_id = :qid;

        end for
    end for;
 
    return 'completed ' || num_runs || ' runs';
end;

call run_benchmark();

select * from benchmark_result order by run_id asc;
***