Query Optimization for OpenSAFELY
This page contains practical SQL-level guidance for teams building or reviewing OpenSAFELY queries on Trino.
Use native column types in predicates and joins
Section titled “Use native column types in predicates and joins”Columns such as snomed_concept_id, code_id, and hashed identifiers are
stored as native numeric/binary types. Casting them to VARCHAR in predicates
or join conditions can prevent file pruning.
-- Prefer native type comparisonWHERE obs.snomed_concept_id IN (BIGINT '123456789', BIGINT '987654321')Prefer EXISTS for membership checks
Section titled “Prefer EXISTS for membership checks”When you only need to confirm that related rows exist, EXISTS usually performs
better than LEFT JOIN ... IS NOT NULL.
SELECT DISTINCT pop.patient_idFROM hive."your_username"."tmp_population" AS popWHERE EXISTS ( SELECT 1 FROM explorer_open_safely.observation AS obs WHERE obs.patient_id = pop.patient_id AND obs.snomed_concept_id = BIGINT '37687008')Use explicit date ranges
Section titled “Use explicit date ranges”Prefer bounded date predicates over expressions like YEAR(column) = ....
WHERE effective_datetime >= DATE '2022-01-01' AND effective_datetime < DATE '2023-01-01'Use aggregates for latest-record logic where possible
Section titled “Use aggregates for latest-record logic where possible”For “latest per patient” patterns, aggregate functions can avoid heavy window materialization.
SELECT patient_id, MAX_BY(observation_id, effective_datetime) AS latest_observation_id, MAX(effective_datetime) AS latest_dateFROM explorer_open_safely.observationGROUP BY patient_idInvestigating slow queries
Section titled “Investigating slow queries”Use execution plans during query tuning:
- Run
EXPLAINfor plan shape and pushdown checks. - Use
EXPLAIN ANALYZEin controlled environments for runtime statistics. - Review data scanned, row counts, join strategy, and memory-heavy operators.