Databricks DE ยท Topic 10.2
The everyday Databricks SQL toolkit โ write & share queries, reuse them with typed :parameters, parameterize names with IDENTIFIER(), structure with CTEs, standardize with snippets, and make BI instant with three caching layers.
โธ :param is typed & bound โ not string-concatenated, so injection can't execute
Mechanism: the editor runs statements on a selected SQL Warehouse (10.1). Saved queries are first-class, permissioned (UC GRANT) objects reused by dashboards, alerts, and jobs. Trade-off: the editor is free; the warehouse under it drives cost/latency.
-- A saved query targets a UC object with the 3-level namespace. SELECT region, count(*) AS orders FROM prod.sales.orders -- catalog.schema.table (Unity Catalog) WHERE order_date >= current_date() - INTERVAL 7 DAYS GROUP BY region ORDER BY orders DESC;
:param (typed & injection-safe)Mechanism: the mandatory : separates parameter names from columns; values are bound as typed literals, never spliced into SQL text. Why: that binding is what makes them injection-proof. Trade-off: they replace the legacy mustache {{ }} of the old editor โ old queries must be migrated.
-- :named markers are typed & bound โ safe + reusable across every range. SELECT region, sum(amount) AS revenue FROM prod.sales.orders WHERE order_date BETWEEN :start_date AND :end_date -- bound as typed DATEs AND region = :region -- bound as STRING GROUP BY region;
IDENTIFIER() โ parameterize object names safelyMechanism: a plain :param binds a value, not a name โ you can't put :table after FROM. IDENTIFIER(:x) interprets the bound string as a catalog/schema/table/column name. Trade-off: the object must exist and UC grants still apply โ it doesn't bypass permissions.
-- Dynamic, injection-safe table & column selection. SELECT IDENTIFIER(:metric_col) AS metric -- column name as a parameter FROM IDENTIFIER(:catalog || '.' || :schema || '.orders') -- table name as a parameter WHERE order_date >= :since;
WITH for readable multi-step SQLMechanism: WITH name AS (subquery) names an intermediate result you reference (and chain) later. Trade-off (gotcha): a CTE is not guaranteed to materialize โ referenced many times, it may be recomputed; persist a temp view/table for heavy reuse.
-- Chained CTEs read top-down; each name is a clean, reusable step. WITH recent AS ( SELECT * FROM prod.sales.orders WHERE order_date BETWEEN :start_date AND :end_date ), by_region AS ( SELECT region, sum(amount) AS revenue, count(*) AS orders FROM recent GROUP BY region ) SELECT region, revenue, revenue / orders AS avg_order_value FROM by_region ORDER BY revenue DESC;
Mechanism: save a reusable fragment with a trigger keyword; typing it expands the snippet (placeholders allowed). Why: everyone defines common logic ("active customer") identically. Trade-off: snippets aren't governed โ for logic that must stay consistent, prefer a view or SQL UDF in UC.
-- Snippet "active_cust" expands to a standardized predicate so every -- query defines "active" identically: SELECT customer_id, region FROM prod.sales.customers WHERE status = 'ACTIVE' AND last_seen >= current_date() - INTERVAL 90 DAYS;
Mechanism: three layers, each auto-invalidated on write. Why: a re-run of the same query skips compute (result cache); the disk cache speeds different queries over the same hot data.
| Layer | Scope & lifetime | Stores |
|---|---|---|
| UI cache | per-user, in the DBSQL UI, up to 7 days | recent results + visualizations |
| Result cache โ local | per-warehouse, in-memory, until cluster stop/restart | exact results of a prior run |
| Result cache โ remote | serverless, survives restarts, shared workspace-wide, 24 h | results as workspace system data |
| Disk cache | per-node local SSD, auto-detects data changes | hot data files, optimized format |
-- Force a cold run for benchmarking; re-enable afterward. SET use_cached_result = false; -- bypass the result cache for this session SELECT region, sum(amount) FROM prod.sales.orders GROUP BY region; SET use_cached_result = true; -- restore caching
โธ identical query on unchanged data = cache hit; a write invalidates it
:start_date/:end_date so one query serves every range; a CTE builds the aggregate; the result cache makes re-opening instant; a shared snippet standardizes the "active customer" filter team-wide.:param), env/column-agnostic queries (IDENTIFIER), readable pipelines (CTEs), standard fragments (snippets), millisecond re-opens (caching).:param/IDENTIFIER/snippets are Databricks SQL features; the query profile reflects one run's plan โ re-check after data/size changes.:param โ injection + no type safety.:table after FROM โ bind values with :param, names with IDENTIFIER(:table).{{ mustache }} parameters into the new editor โ migrate them to :named markers.SET use_cached_result = false).