Semantic Query Fingerprinting for Snowflake — collapse syntactically different but logically identical SQL queries to a canonical fingerprint
Project description
sqf-py — Semantic Query Fingerprinting for Snowflake
sqf-py assigns a stable, content-addressed fingerprint to any SQL query by normalizing away syntactic noise. Queries that are logically identical but written differently collapse to the same fingerprint — enabling deduplication analysis, cost attribution, and query-cache optimization on Snowflake.
Accompanies the white paper: Semantic Query Deduplication in Cloud Data Warehouses.
The Problem
Modern data warehouses are bombarded with semantically identical queries that look different:
-- BI tool A (Looker)
SELECT o.user_id AS uid, SUM(o.amount) AS revenue
FROM orders AS o WHERE o.status = 'complete' AND o.created_at > '2024-01-01'
GROUP BY 1
-- BI tool B (Tableau)
SELECT SUM(amount) AS revenue, user_id AS uid
FROM orders WHERE created_at > '2023-06-01' AND status = 'active'
GROUP BY uid
These are logically the same query template. Snowflake's text-keyed result cache treats them as distinct — burning compute on every re-execution. sqf-py proves they're duplicates: both fingerprint to 3c1a8c600789df69….
On a synthetic 10,000-query BI-style workload, sqf-py identifies 99.7% of executions as semantic duplicates, at ~440 queries/second analyzed client-side. See the white paper for methodology and caveats.
Installation
pip install sqf-py # core library (sqlglot only)
pip install "sqf-py[snowflake]" # + Snowflake connector
pip install "sqf-py[bench]" # + matplotlib for benchmark charts
pip install "sqf-py[dev]" # + pytest
Quick Start
from sqf import fingerprint, are_equivalent, canonical_form, SQFAnalyzer
# Single fingerprint
fp = fingerprint("SELECT a, b FROM t WHERE id = 1")
# → "3f4a1b9c..." (64-char hex, stable)
# Equivalence check — these two queries are semantically identical
q1 = "SELECT a AS col1, b AS col2 FROM t WHERE id = 99"
q2 = "SELECT b, a FROM t WHERE id = 1"
are_equivalent(q1, q2) # → True
# See the canonical form
canonical_form("SELECT a AS x, b AS y FROM t WHERE id = 42")
# → "SELECT A, B FROM T WHERE ID = ?"
# Bulk workload analysis
analyzer = SQFAnalyzer()
analyzer.ingest_sql(my_query_list, credits_per_query=0.05)
print(analyzer.report().summary())
Normalization Pipeline
The SQF algorithm applies these passes in order:
| Pass | What it does | Example |
|---|---|---|
| 1. GROUP BY reference resolution | GROUP BY 1 / GROUP BY alias → actual expression |
GROUP BY user_id |
| 2. Alias stripping | Remove all AS aliases and table qualifiers |
SELECT o.a AS x → SELECT a |
| 3. Column sort | Sort SELECT list alphabetically | SELECT b, a → SELECT a, b |
| 4. GROUP BY sort | Sort GROUP BY keys | GROUP BY b, a → GROUP BY a, b |
| 5. Predicate canonicalization | Sort AND/OR operands recursively | WHERE b=2 AND a=1 → WHERE a=1 AND b=2 |
| 6. CTE inlining | Inline single-reference CTEs | WITH x AS (...) SELECT ... FROM x → subquery |
| 7. Literal abstraction | Replace all values with ? |
WHERE id = 42 → WHERE id = ? |
| 8. Whitespace collapse + uppercase | Canonical string form | |
| Hash | SHA-256 of canonical string | 64-char hex fingerprint |
The precise equivalence class (and its deliberate trade-offs) is defined in §2 of the white paper.
Analyzing a Snowflake Workload
from sqf import SnowflakeIngestor, ClusterStore, SQFAnalyzer
import snowflake.connector
conn = snowflake.connector.connect(...) # your credentials
# 1. Pull the last 30 days of QUERY_HISTORY
records = SnowflakeIngestor(conn, lookback_days=30, row_limit=50_000).fetch_records()
# 2. Fingerprint + cluster
report = SQFAnalyzer().ingest(records).report()
print(report.summary())
# ═══════════════════════════════════════════════════════════
# Semantic Query Fingerprint (SQF) Analysis Report
# ═══════════════════════════════════════════════════════════
# Total query executions : 12,847
# Unique SQF fingerprints : 4,203
# Dedup hit rate : 67.3%
# Credits wasted : 86.4800
# ...
# 3. Persist results back to Snowflake (idempotent MERGEs)
store = ClusterStore(conn, database="SQF", schema="ANALYTICS")
store.bootstrap() # creates tables + 6 analytical views
store.persist(report)
# 4. Query the views
store.overall_metrics() # headline KPIs
store.daily_hit_rate() # time series for charts
store.top_waste(10) # the 10 most expensive duplicate clusters
store.multi_variant_offenders(10) # same logic, many SQL spellings
The bundled SQL (DDL, views, QUERY_HISTORY export) lives in sqf/sql/ and is also usable standalone.
Synthetic Workloads & Benchmarks
No Snowflake account needed to try the library:
from sqf import SyntheticWorkloadGenerator, SQFAnalyzer
gen = SyntheticWorkloadGenerator(n_queries=1000, duplication_rate=0.7, seed=42)
report = SQFAnalyzer().ingest(gen.generate()).report()
print(report.summary()) # → 96.9% dedup hit rate
The generator models 12 logical query families (BI aggregates, joins, window functions, funnels, MRR rollups, …) with 8 syntactic variant dimensions each, plus realistic per-family credit cost distributions.
Reproduce the white paper's full benchmark grid (36 configurations, ~5 min):
python -m sqf.benchmark --out benchmarks --full
Outputs benchmarks/results.json plus five charts:
Development
git clone https://github.com/vermapragya/sqf-py
cd sqf-py
python3 -m venv .venv
.venv/bin/pip install -e ".[dev,bench]"
.venv/bin/python -m pytest # 68 tests
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqf_py-0.1.0.tar.gz.
File metadata
- Download URL: sqf_py-0.1.0.tar.gz
- Upload date:
- Size: 31.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d72e7674d47fe70080330757d371192dc6148b0b2570184505a0b779657db72f
|
|
| MD5 |
7165f368ea52b1cafef6df6bffcd14bb
|
|
| BLAKE2b-256 |
4ac0e6c962590b65bfc543858058e2fbfe5ca971bcc1ac18ee0e07ac0c8f1be0
|
Provenance
The following attestation bundles were made for sqf_py-0.1.0.tar.gz:
Publisher:
publish.yml on vermapragya/sqf-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqf_py-0.1.0.tar.gz -
Subject digest:
d72e7674d47fe70080330757d371192dc6148b0b2570184505a0b779657db72f - Sigstore transparency entry: 1787779386
- Sigstore integration time:
-
Permalink:
vermapragya/sqf-py@44ff3efe41c1ed3948341753d72ab805054bb1b3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vermapragya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44ff3efe41c1ed3948341753d72ab805054bb1b3 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sqf_py-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sqf_py-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe3769e4d4de04e26dbf7ffe59538cc297d50a46187812e99b59b11a2c282709
|
|
| MD5 |
847b08c33eb05d4e3e819431082b7449
|
|
| BLAKE2b-256 |
508a6f46f3907c16ded6fd17bc3371affcae942c9f33d45ff054ebe27f37f519
|
Provenance
The following attestation bundles were made for sqf_py-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on vermapragya/sqf-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqf_py-0.1.0-py3-none-any.whl -
Subject digest:
fe3769e4d4de04e26dbf7ffe59538cc297d50a46187812e99b59b11a2c282709 - Sigstore transparency entry: 1787779454
- Sigstore integration time:
-
Permalink:
vermapragya/sqf-py@44ff3efe41c1ed3948341753d72ab805054bb1b3 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vermapragya
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@44ff3efe41c1ed3948341753d72ab805054bb1b3 -
Trigger Event:
push
-
Statement type: