Requirements (clarify): ingest ~100k rows/day CSVs from multiple vendors, validate and transform to a canonical schema, allow optional new columns (schema drift), idempotent loads, easy failure recovery, monitoring/alerting, use Snowflake.High-level flow:1. Ingest files to an object store (S3/GCS) or upload to Snowflake internal stage.2. COPY INTO a raw staging table (raw_csv_stage) storing each file as VARIANT/JSON or all columns as STRING plus metadata (file_name, load_id, file_hash, received_at).3. Validate & transform: run validation SQL / Great Expectations checks against raw data; normalize to canonical schema using SQL/Stored Proc; capture unknown columns into a JSON column to support schema drift.4. Merge into production analytic table using MERGE with business key + checksum for idempotency.5. Monitoring: load audit table, TASKs, alerts via cloud functions -> Slack/email.Staging + COPY pattern:- Create FILE FORMAT matching CSV options.- COPY INTO raw_table from @stage FILE_FORMAT=(...) ON_ERROR='CONTINUE' to capture rejects in load_errors table.Example COPY + MERGE snippets:sql
-- load raw CSV into staging table (all cols as VARIANT or flattened)
COPY INTO raw_stage(file_variant, file_name, file_hash, load_id)
FROM @vendor_stage/prefix/
FILE_FORMAT = (TYPE = 'CSV' SKIP_HEADER = 1 FIELD_OPTIONALLY_ENCLOSED_BY='"');
-- merge into canonical table using business key and checksum for idempotency
MERGE INTO analytics.canonical t
USING (
SELECT
data:vendor_id::STRING as vendor_id,
data:order_id::STRING as order_id,
MD5(TO_VARCHAR(data)) as row_hash,
...
FROM transformed_stage
) s
ON t.vendor_id = s.vendor_id AND t.order_id = s.order_id
WHEN MATCHED AND t.row_hash <> s.row_hash THEN
UPDATE SET ..., row_hash = s.row_hash, updated_at = CURRENT_TIMESTAMP()
WHEN NOT MATCHED THEN
INSERT (..., row_hash, created_at) VALUES (..., s.row_hash, CURRENT_TIMESTAMP());
Validation patterns:- Structural: required columns exist — use INFORMATION_SCHEMA or VARIANT keys plus CHECK queries; reject files missing required columns and record to load_errors.- Content: type, domain, business rules checked via SQL or Great Expectations; invalid rows routed to reject table with reason.- Drift handling: store unknown/extra columns in a VARIANT column (payload_extra) so queries can still access them; maintain a drift registry table listing new columns per vendor and date for review.Idempotency & provenance:- Compute file_hash and store (file_name, load_id, file_hash). Skip processing if file_hash already loaded.- Use row-level hash (MD5 of canonicalized row) stored in row_hash to detect changes and make MERGE idempotent.- Include load_id and source_file columns for traceability.Failure recovery:- On partial failure: keep raw files until success; use transactional MERGE; use staging/transient tables so retries don't duplicate.- For rejected rows, provide interface to fix and re-ingest; keep file-level audit and reprocess by load_id.- Implement a manual "replay" procedure referencing file_hash/load_id.Monitoring & alerting:- Audit table: one row per file with status (received, loaded, validated, merged, failed), row counts, error_count, start/end timestamps.- Scheduled health checks (Snowflake TASK) and Lambda/Cloud Function polls S3 + Snowflake audit; send alerts to Slack/email on failures, large error rates, or schema drift detection.- Expose dashboards (Grafana/Tableau) on load metrics and recent errors.Trade-offs:- Storing extras in VARIANT keeps pipeline flexible but requires analysts to handle JSON fields.- Strong upfront validation reduces bad data but may delay loads; prefer fail-fast for missing required fields, relax for optional fields.- For this volume (~100k/day), Snowflake warehousing + COPY + MERGE is cost-effective and scalable.This design gives clear auditability, idempotency via file & row hashes, handles schema drift by capturing extras, and provides simple failure recovery and alerting suitable for analytics consumption.