This document provides a quick reference for all primary key and foreign key constraints in the FRED data pipeline.
The FRED pipeline uses Delta Live Tables with Streaming Tables and Materialized Views. Constraints are defined inline during table creation to ensure data quality and referential integrity.
┌─────────────────────────────────┐
│ silver_metadata │
│ PK: series_id (NOT NULL) │
└─────────────┬───────────────────┘
│
│ FK: series_id
│
┌─────────────▼───────────────────┐ ┌─────────────────────────────────┐
│ silver_observations │ │ common.reference.dim_calendar │
│ FK: series_id → silver_metadata│◄────┤ PK: calendar_date (optional) │
│ FK: date → dim_calendar (opt) │ └─────────────────────────────────┘
│ NOT NULL: series_id, date, value
└─────────────────────────────────┘
│
│ Auto-join
│
┌─────────────▼───────────────────┐
│ gold_observations │
│ (Materialized View) │
│ Denormalized: obs + metadata │
└─────────────────────────────────┘
Primary Key:
series_id(STRING NOT NULL)
Purpose: Unique identifier for each FRED economic series (e.g., "DGS10", "DFF")
Definition:
CREATE OR REPLACE STREAMING TABLE investments.fred.silver_metadata (
series_id STRING NOT NULL,
...
CONSTRAINT pk_series PRIMARY KEY(series_id)
)NOT NULL Constraints:
series_id(STRING NOT NULL)date(DATE NOT NULL)value(DOUBLE NOT NULL)
Foreign Keys:
-
series_id→silver_metadata.series_id- Purpose: Ensures every observation has corresponding metadata
- Enforcement: Applied during streaming ingestion
-
date→common.reference.dim_calendar.calendar_date(OPTIONAL)- Purpose: Ensures all dates are valid calendar dates
- Status: Commented out by default
- To enable: Uncomment in notebook 03_Silver_Setup.py
Definition:
CREATE OR REPLACE STREAMING TABLE investments.fred.silver_observations (
series_id STRING NOT NULL,
series_name STRING,
date DATE NOT NULL,
value DOUBLE NOT NULL,
run_timestamp TIMESTAMP,
updated_at TIMESTAMP,
CONSTRAINT fk_series FOREIGN KEY(series_id) REFERENCES investments.fred.silver_metadata(series_id)
)The Bronze layer has no constraints as it stores raw data:
bronze_observations- Raw observation databronze_metadata- Raw metadata
Data quality is enforced at the Silver layer during transformation.
The Gold layer is a Materialized View with no explicit constraints:
gold_observations- Denormalized view joining silver_observations + silver_metadata
The view automatically maintains referential integrity through the JOIN operation.
-
Primary Keys:
- Documented in table properties and comments
- Enforced via DLT expectations (
@dlt.expect_all_or_drop) - Example:
@dlt.expect_all_or_drop({"pk_series_id_not_null": "series_id IS NOT NULL"}) - Informational for documentation and optimization
- Application-level enforcement during streaming
-
Foreign Keys:
- Documented in table properties and comments
- Enforced via DLT expectations and filtering
- Example: Application-level filtering with
.filter(col("series_id").isNotNull()) - Informational for lineage and documentation
- Application-level enforcement during streaming
-
NOT NULL:
- Enforced via DLT expectations (
@dlt.expect_all_or_drop) - Example:
@dlt.expect_all_or_drop({"value_not_null": "value IS NOT NULL"}) - Streaming queries filter out NULL values before insert
- Rows violating expectations are dropped
- Enforced via DLT expectations (
- Document constraints in table properties and comments for documentation and tooling support
- Use DLT expectations for constraint enforcement (e.g.,
@dlt.expect_all_or_drop({"value_not_null": "value IS NOT NULL"})) - Filter invalid data in PySpark transformations (e.g.,
.filter(col("value").isNotNull())) - Use type conversions to enforce data types (e.g.,
.cast("double"),to_date(),to_timestamp()) - Monitor data quality metrics in DLT pipeline UI (expectations tracked automatically)
- Review constraint violations in event logs and expectations metrics
-- View metadata table constraints
DESCRIBE EXTENDED investments.fred.silver_metadata;
-- View observations table constraints
DESCRIBE EXTENDED investments.fred.silver_observations;-- Find observations without metadata (should be empty)
SELECT o.series_id, COUNT(*) as orphaned_records
FROM investments.fred.silver_observations o
LEFT JOIN investments.fred.silver_metadata m ON o.series_id = m.series_id
WHERE m.series_id IS NULL
GROUP BY o.series_id;
-- Find metadata without observations (valid - new series not yet observed)
SELECT m.series_id, m.title
FROM investments.fred.silver_metadata m
LEFT JOIN investments.fred.silver_observations o ON m.series_id = o.series_id
WHERE o.series_id IS NULL;-- Should return 0 - NOT NULL constraints enforced
SELECT COUNT(*) as null_series_id
FROM investments.fred.silver_observations
WHERE series_id IS NULL;
SELECT COUNT(*) as null_dates
FROM investments.fred.silver_observations
WHERE date IS NULL;
SELECT COUNT(*) as null_values
FROM investments.fred.silver_observations
WHERE value IS NULL;If you have a calendar dimension table, you can enable the date foreign key:
-
Ensure the calendar table exists:
SELECT COUNT(*) FROM common.reference.dim_calendar;
-
Ensure it has a primary key:
ALTER TABLE common.reference.dim_calendar ADD CONSTRAINT pk_date PRIMARY KEY(calendar_date);
-
Uncomment in Silver Setup: Edit
notebooks/03_Silver_Setup.pyand uncomment:ALTER TABLE investments.fred.silver_observations ADD CONSTRAINT fk_date FOREIGN KEY(date) REFERENCES common.reference.dim_calendar(calendar_date);
Solution: In DLT, constraints are informational. Monitor:
- Data quality expectations in pipeline UI
- Event logs for parsing/type conversion errors
- Row counts and data validation queries
Cause: Parent table (silver_metadata) doesn't exist yet
Solution: Ensure notebooks run in order:
02_Bronze_Setup.py03_Silver_Setup.py(metadata before observations)04_Gold_Setup.py
Cause: NOT NULL constraint not enforced at source
Solution: Add explicit filtering in streaming query:
WHERE series_id IS NOT NULL
AND date IS NOT NULL
AND value IS NOT NULL