initial commit
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
Baseline vs rolling evaluation template
|
||||
=======================================
|
||||
|
||||
Purpose:
|
||||
Compare a fixed baseline cohort with rolling evaluation periods.
|
||||
|
||||
Typical use:
|
||||
- A medicines optimisation search where the baseline is fixed.
|
||||
- Later periods check whether the same patients still meet criteria.
|
||||
- Output is practice x period, preserving the baseline count.
|
||||
|
||||
Replace medicine_products and the evaluation criteria before use.
|
||||
*/
|
||||
|
||||
SET BASELINE_START = '2025-04-01';
|
||||
SET BASELINE_END = '2025-06-30';
|
||||
SET FIRST_EVALUATION_START = '2025-04-01';
|
||||
SET EVALUATION_MONTHS = 3;
|
||||
SET BNF_PREFIX = 'REPLACE_WITH_BNF_PREFIX';
|
||||
|
||||
WITH latest_prescribing_date AS (
|
||||
-- Caps generated periods to data already present in the prescribing table.
|
||||
SELECT MAX("DateMedicationStart")::DATE AS "MaxDate"
|
||||
FROM REPORTING_DATASETS_ICB.SCRATCHPAD."MEDS__UnifiedPrescribingTable"
|
||||
WHERE "DateMedicationStart" <= CURRENT_DATE()
|
||||
),
|
||||
periods AS (
|
||||
-- Generates candidate month starts; increase ROWCOUNT for longer projects.
|
||||
SELECT
|
||||
ROW_NUMBER() OVER (ORDER BY SEQ4()) AS "PeriodNumber",
|
||||
DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY SEQ4()) - 1, $FIRST_EVALUATION_START::DATE)::DATE AS "PeriodStartDate"
|
||||
FROM TABLE(GENERATOR(ROWCOUNT => 48))
|
||||
),
|
||||
evaluation_periods AS (
|
||||
SELECT
|
||||
"PeriodNumber",
|
||||
"PeriodStartDate",
|
||||
LAST_DAY(DATEADD(MONTH, $EVALUATION_MONTHS - 1, "PeriodStartDate"))::DATE AS "PeriodEndDate"
|
||||
FROM periods
|
||||
CROSS JOIN latest_prescribing_date
|
||||
WHERE "PeriodStartDate" <= "MaxDate"
|
||||
),
|
||||
date_bounds AS (
|
||||
SELECT
|
||||
$BASELINE_START::DATE AS "MinEventDate",
|
||||
GREATEST($BASELINE_END::DATE, COALESCE(MAX("PeriodEndDate"), $BASELINE_END::DATE)) AS "MaxEventDate"
|
||||
FROM evaluation_periods
|
||||
),
|
||||
practices AS (
|
||||
-- Default to active Norfolk and Suffolk parent GP practices.
|
||||
SELECT DISTINCT
|
||||
"OrganisationCode" AS "PracticeCode",
|
||||
"OrganisationName" AS "PracticeName"
|
||||
FROM DATA_HUB.DWH."DimOrganisationAndSite"
|
||||
WHERE "OrganisationSubType" = 'GP Practice'
|
||||
AND "IsSiteActive" = 'Yes'
|
||||
AND "IsSiteNorfolkAndSuffolk" = 'Yes'
|
||||
AND "SiteCode" = "OrganisationCode"
|
||||
),
|
||||
medicine_products AS (
|
||||
-- Replace this CTE for VTM, VMP, explicit SNOMED, or cluster-based definitions.
|
||||
SELECT DISTINCT "ProductSnomedCode"
|
||||
FROM DATA_HUB.DWH."DimMedicineAndDevice"
|
||||
WHERE "BNFCode" LIKE $BNF_PREFIX || '%'
|
||||
),
|
||||
medicine_events AS (
|
||||
-- Pull the smallest event window needed for both baseline and evaluation.
|
||||
SELECT DISTINCT
|
||||
rx."PersonKey",
|
||||
rx."CurrentGeneralPractice" AS "PracticeCode",
|
||||
rx."DateMedicationStart"
|
||||
FROM REPORTING_DATASETS_ICB.SCRATCHPAD."MEDS__UnifiedPrescribingTable" rx
|
||||
CROSS JOIN date_bounds db
|
||||
INNER JOIN medicine_products mp
|
||||
ON rx."SNOMEDCode" = mp."ProductSnomedCode"
|
||||
WHERE rx."PersonKey" IS NOT NULL
|
||||
AND rx."CurrentGeneralPractice" IS NOT NULL
|
||||
AND rx."DateMedicationStart" BETWEEN db."MinEventDate" AND db."MaxEventDate"
|
||||
),
|
||||
baseline_cohort AS (
|
||||
-- Fixes each patient to the practice recorded during the baseline window.
|
||||
SELECT DISTINCT
|
||||
me."PersonKey",
|
||||
me."PracticeCode"
|
||||
FROM medicine_events me
|
||||
WHERE me."DateMedicationStart" BETWEEN $BASELINE_START AND $BASELINE_END
|
||||
),
|
||||
baseline_counts AS (
|
||||
SELECT
|
||||
"PracticeCode",
|
||||
COUNT(DISTINCT "PersonKey") AS "BaselineCount"
|
||||
FROM baseline_cohort
|
||||
GROUP BY "PracticeCode"
|
||||
),
|
||||
practice_periods AS (
|
||||
-- Ensures every active practice returns one row per evaluation period.
|
||||
SELECT
|
||||
p."PracticeCode",
|
||||
p."PracticeName",
|
||||
ep."PeriodNumber",
|
||||
ep."PeriodStartDate",
|
||||
ep."PeriodEndDate"
|
||||
FROM practices p
|
||||
CROSS JOIN evaluation_periods ep
|
||||
),
|
||||
evaluation_counts AS (
|
||||
SELECT
|
||||
bc."PracticeCode",
|
||||
ep."PeriodNumber",
|
||||
ep."PeriodStartDate",
|
||||
ep."PeriodEndDate",
|
||||
COUNT(DISTINCT me."PersonKey") AS "EvaluationCount"
|
||||
FROM baseline_cohort bc
|
||||
CROSS JOIN evaluation_periods ep
|
||||
LEFT JOIN medicine_events me
|
||||
ON me."PersonKey" = bc."PersonKey"
|
||||
AND me."DateMedicationStart" BETWEEN ep."PeriodStartDate" AND ep."PeriodEndDate"
|
||||
GROUP BY bc."PracticeCode", ep."PeriodNumber", ep."PeriodStartDate", ep."PeriodEndDate"
|
||||
)
|
||||
SELECT
|
||||
pp."PracticeCode",
|
||||
pp."PracticeName",
|
||||
pp."PeriodNumber",
|
||||
pp."PeriodStartDate",
|
||||
pp."PeriodEndDate",
|
||||
COALESCE(bc."BaselineCount", 0) AS "BaselineCount",
|
||||
COALESCE(ec."EvaluationCount", 0) AS "EvaluationCount",
|
||||
COALESCE(bc."BaselineCount", 0) - COALESCE(ec."EvaluationCount", 0) AS "ReductionFromBaseline"
|
||||
FROM practice_periods pp
|
||||
LEFT JOIN baseline_counts bc
|
||||
ON pp."PracticeCode" = bc."PracticeCode"
|
||||
LEFT JOIN evaluation_counts ec
|
||||
ON pp."PracticeCode" = ec."PracticeCode"
|
||||
AND pp."PeriodNumber" = ec."PeriodNumber"
|
||||
ORDER BY
|
||||
pp."PracticeName",
|
||||
pp."PeriodNumber";
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Dual-source long-format measure template
|
||||
========================================
|
||||
|
||||
Purpose:
|
||||
Produce the same measure from dispensing and prescribing data, then return
|
||||
a tidy long output:
|
||||
|
||||
OrganisationName, PeriodStartDate, PeriodEndDate, DataSource,
|
||||
Measure, Indicator, Value
|
||||
|
||||
Why this shape:
|
||||
- Easy to append multiple measures.
|
||||
- Easy to chart in Excel or Power BI.
|
||||
- Avoids changing columns every time a new indicator is added.
|
||||
|
||||
Replace the BNF filter and measure-specific calculations before use.
|
||||
*/
|
||||
|
||||
SET FIRST_PERIOD_END_DATE = '2025-06-30';
|
||||
SET LAST_PERIOD_END_DATE = LAST_DAY(DATEADD('MONTH', -1, CURRENT_DATE()));
|
||||
SET LOOKBACK_MONTHS = 12;
|
||||
SET BNF_PREFIX = '0501';
|
||||
SET MEASURE_ID = 'M_REPLACE';
|
||||
|
||||
WITH RECURSIVE date_periods AS (
|
||||
-- Month-end series; cap LAST_PERIOD_END_DATE before sharing a final report.
|
||||
SELECT $FIRST_PERIOD_END_DATE::DATE AS "PeriodEndDate"
|
||||
UNION ALL
|
||||
SELECT LAST_DAY(DATEADD(MONTH, 1, "PeriodEndDate"))::DATE AS "PeriodEndDate"
|
||||
FROM date_periods
|
||||
WHERE "PeriodEndDate" < $LAST_PERIOD_END_DATE::DATE
|
||||
),
|
||||
rolling_periods AS (
|
||||
-- Converts each period end into a fixed lookback window.
|
||||
SELECT
|
||||
DATEADD(MONTH, 1 - $LOOKBACK_MONTHS, DATE_TRUNC('MONTH', "PeriodEndDate"))::DATE AS "PeriodStartDate",
|
||||
"PeriodEndDate"
|
||||
FROM date_periods
|
||||
),
|
||||
date_bounds AS (
|
||||
SELECT
|
||||
MIN("PeriodStartDate") AS "MinPeriodStartDate",
|
||||
MAX("PeriodEndDate") AS "MaxPeriodEndDate"
|
||||
FROM rolling_periods
|
||||
),
|
||||
practices AS (
|
||||
-- Default reporting geography: active Norfolk and Suffolk parent GP practices.
|
||||
SELECT DISTINCT
|
||||
"OrganisationCode" AS "PracticeCode",
|
||||
"OrganisationName" AS "OrganisationName"
|
||||
FROM DATA_HUB.DWH."DimOrganisationAndSite"
|
||||
WHERE "OrganisationSubType" = 'GP Practice'
|
||||
AND "IsSiteActive" = 'Yes'
|
||||
AND "IsSiteNorfolkAndSuffolk" = 'Yes'
|
||||
AND "SiteCode" = "OrganisationCode"
|
||||
),
|
||||
practice_periods AS (
|
||||
SELECT
|
||||
p."PracticeCode",
|
||||
p."OrganisationName",
|
||||
rp."PeriodStartDate",
|
||||
rp."PeriodEndDate"
|
||||
FROM practices p
|
||||
CROSS JOIN rolling_periods rp
|
||||
),
|
||||
prescribing_events AS (
|
||||
-- Pre-filter prescribing once to keep later rolling joins smaller.
|
||||
SELECT
|
||||
rx."PersonKey",
|
||||
rx."CurrentGeneralPractice",
|
||||
rx."DateMedicationStart",
|
||||
rx."Quantity",
|
||||
rx."EstPrice"
|
||||
FROM REPORTING_DATASETS_ICB.SCRATCHPAD."MEDS__UnifiedPrescribingTable" rx
|
||||
CROSS JOIN date_bounds db
|
||||
INNER JOIN DATA_HUB.DWH."DimMedicineAndDevice" med
|
||||
ON rx."SNOMEDCode" = med."ProductSnomedCode"
|
||||
AND med."BNFCode" LIKE $BNF_PREFIX || '%'
|
||||
WHERE rx."PersonKey" IS NOT NULL
|
||||
AND rx."DateMedicationStart" BETWEEN db."MinPeriodStartDate" AND db."MaxPeriodEndDate"
|
||||
),
|
||||
dispensing_agg AS (
|
||||
-- Dispensing is official paid activity; use ProcessingPeriodDate for periods.
|
||||
SELECT
|
||||
pp."OrganisationName",
|
||||
pp."PeriodStartDate",
|
||||
pp."PeriodEndDate",
|
||||
COUNT(DISTINCT gpm."PatientPseudonym") AS "Patients",
|
||||
COALESCE(SUM(gpm."ItemCount"), 0) AS "Items",
|
||||
COALESCE(SUM(gpm."PaidQuantity"), 0) AS "Quantity"
|
||||
FROM practice_periods pp
|
||||
LEFT JOIN NATIONAL.GPMED."MedicinesDispensedInPrimarycare" gpm
|
||||
ON pp."PracticeCode" = gpm."CostCentreODSCode"
|
||||
AND gpm."ProcessingPeriodDate" BETWEEN pp."PeriodStartDate" AND pp."PeriodEndDate"
|
||||
AND gpm."PaidBNFCode" LIKE $BNF_PREFIX || '%'
|
||||
AND gpm."PatientPseudonym" IS NOT NULL
|
||||
GROUP BY pp."OrganisationName", pp."PeriodStartDate", pp."PeriodEndDate"
|
||||
),
|
||||
prescribing_agg AS (
|
||||
-- Prescribing is current clinical-system activity from the unified table.
|
||||
SELECT
|
||||
pp."OrganisationName",
|
||||
pp."PeriodStartDate",
|
||||
pp."PeriodEndDate",
|
||||
COUNT(DISTINCT rx."PersonKey") AS "Patients",
|
||||
COUNT(rx."PersonKey") AS "Items",
|
||||
COALESCE(SUM(TRY_CAST(rx."Quantity" AS FLOAT)), 0) AS "Quantity",
|
||||
COALESCE(SUM(rx."EstPrice"), 0) AS "EstimatedCost"
|
||||
FROM practice_periods pp
|
||||
LEFT JOIN prescribing_events rx
|
||||
ON pp."PracticeCode" = rx."CurrentGeneralPractice"
|
||||
AND rx."DateMedicationStart" BETWEEN pp."PeriodStartDate" AND pp."PeriodEndDate"
|
||||
GROUP BY pp."OrganisationName", pp."PeriodStartDate", pp."PeriodEndDate"
|
||||
)
|
||||
SELECT
|
||||
"OrganisationName",
|
||||
"PeriodStartDate",
|
||||
"PeriodEndDate",
|
||||
'Dispensing' AS "DataSource",
|
||||
$MEASURE_ID AS "Measure",
|
||||
'Patients' AS "Indicator",
|
||||
CAST("Patients" AS FLOAT) AS "Value"
|
||||
FROM dispensing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Dispensing', $MEASURE_ID, 'Items', CAST("Items" AS FLOAT)
|
||||
FROM dispensing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Dispensing', $MEASURE_ID, 'Quantity', CAST("Quantity" AS FLOAT)
|
||||
FROM dispensing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Prescribing', $MEASURE_ID, 'Patients', CAST("Patients" AS FLOAT)
|
||||
FROM prescribing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Prescribing', $MEASURE_ID, 'Items', CAST("Items" AS FLOAT)
|
||||
FROM prescribing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Prescribing', $MEASURE_ID, 'Quantity', CAST("Quantity" AS FLOAT)
|
||||
FROM prescribing_agg
|
||||
UNION ALL
|
||||
SELECT "OrganisationName", "PeriodStartDate", "PeriodEndDate", 'Prescribing', $MEASURE_ID, 'EstimatedCost', CAST("EstimatedCost" AS FLOAT)
|
||||
FROM prescribing_agg
|
||||
ORDER BY
|
||||
"PeriodEndDate",
|
||||
"DataSource",
|
||||
"OrganisationName",
|
||||
"Indicator";
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Latest data dates
|
||||
=================
|
||||
|
||||
Purpose:
|
||||
Check freshness anchors before deciding reporting periods.
|
||||
|
||||
Notes:
|
||||
- Dispensing uses ProcessingPeriodDate and usually lags.
|
||||
- Unified prescribing DateMedicationStart can include future starts, so the
|
||||
TPP DateEventRecorded probe is included as a more conservative source
|
||||
event freshness marker.
|
||||
*/
|
||||
|
||||
SELECT
|
||||
'NATIONAL.GPMED.MedicinesDispensedInPrimarycare' AS "Source",
|
||||
MAX("ProcessingPeriodDate")::DATE AS "LatestDate"
|
||||
FROM NATIONAL.GPMED."MedicinesDispensedInPrimarycare"
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'REPORTING_DATASETS_ICB.SCRATCHPAD.MEDS__UnifiedPrescribingTable DateMedicationStart' AS "Source",
|
||||
MAX("DateMedicationStart")::DATE AS "LatestDate"
|
||||
FROM REPORTING_DATASETS_ICB.SCRATCHPAD."MEDS__UnifiedPrescribingTable"
|
||||
WHERE "DateMedicationStart" <= CURRENT_DATE()
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'PRIMARY_CARE.TPP.SRPrimaryCareMedication DateEventRecorded' AS "Source",
|
||||
MAX(CAST("DateEventRecorded" AS DATE)) AS "LatestDate"
|
||||
FROM PRIMARY_CARE.TPP."SRPrimaryCareMedication"
|
||||
WHERE "DateEventRecorded" >= DATEADD('MONTH', -3, CURRENT_DATE())
|
||||
ORDER BY "Source";
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Rolling period generator
|
||||
========================
|
||||
|
||||
Purpose:
|
||||
Generate reusable rolling reporting windows for monthly outputs.
|
||||
|
||||
Use this first when a measure needs:
|
||||
- one row per practice per period;
|
||||
- a rolling lookback window;
|
||||
- stable PeriodStartDate and PeriodEndDate columns for Excel or Power BI.
|
||||
*/
|
||||
|
||||
SET FIRST_PERIOD_END_DATE = '2025-06-30';
|
||||
SET LAST_PERIOD_END_DATE = LAST_DAY(DATEADD('MONTH', -1, CURRENT_DATE()));
|
||||
SET LOOKBACK_MONTHS = 12;
|
||||
|
||||
WITH RECURSIVE date_periods AS (
|
||||
-- Month-end series; cap LAST_PERIOD_END_DATE before sharing a final report.
|
||||
SELECT $FIRST_PERIOD_END_DATE::DATE AS "PeriodEndDate"
|
||||
UNION ALL
|
||||
SELECT LAST_DAY(DATEADD(MONTH, 1, "PeriodEndDate"))::DATE AS "PeriodEndDate"
|
||||
FROM date_periods
|
||||
WHERE "PeriodEndDate" < $LAST_PERIOD_END_DATE::DATE
|
||||
),
|
||||
rolling_periods AS (
|
||||
-- Converts each period end into a fixed lookback window.
|
||||
SELECT
|
||||
DATEADD(MONTH, 1 - $LOOKBACK_MONTHS, DATE_TRUNC('MONTH', "PeriodEndDate"))::DATE AS "PeriodStartDate",
|
||||
"PeriodEndDate",
|
||||
$LOOKBACK_MONTHS AS "LookbackMonths"
|
||||
FROM date_periods
|
||||
)
|
||||
SELECT
|
||||
"PeriodStartDate",
|
||||
"PeriodEndDate",
|
||||
"LookbackMonths"
|
||||
FROM rolling_periods
|
||||
ORDER BY "PeriodEndDate";
|
||||
Reference in New Issue
Block a user