技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
postgres-schema-expert【数据与研究】Instructions for managing the PostgreSQL schema, writing SQL queries, and maintaining data integrity for the insurance analytics project.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
This skill covers the database layer of the insurance analytics project: the insurance schema, table definitions, indexes, SQL patterns, and data-integrity rules.
All tables live in the insurance schema inside the insurdb database.
insurance
├── member – one row per enrolled health-plan member
├── provider – one row per healthcare provider
└── claim – one row per insurance claim (child of member + provider)
The DDL lives in src/sql/ddl_create_tables.sql and is applied with:
# DATABASE_URL uses the SQLAlchemy format (postgresql+psycopg2://...).
# psql requires a driver-free URL — strip the +psycopg2 prefix:
PSQL_URL="${DATABASE_URL/+psycopg2/}"
psql "$PSQL_URL" -f src/sql/ddl_create_tables.sql
# or use the Makefile shortcut (handles the URL conversion for you):
make db-init
insurance.member| Column | Type | Notes |
|---|---|---|
member_id | BIGSERIAL PRIMARY KEY | Auto-incrementing surrogate key |
person_id | BIGINT | External identifier |
dob | DATE | Date of birth — used to derive age in Python |
gender | VARCHAR(10) | 'male' / 'female' / 'U' — Kaggle datasets map the source sex column to gender; 'U' is the default when gender is absent |
region | VARCHAR(50) | Geographic region (e.g. 'Northeast') |
effective_date | DATE | Coverage start date |
termination_date | DATE | Coverage end date (NULL = still active) |
insurance.provider| Column | Type | Notes |
|---|---|---|
provider_id | BIGSERIAL PRIMARY KEY | Auto-incrementing surrogate key |
specialty | VARCHAR(80) | Medical specialty (e.g. 'Cardiology') |
in_network | BOOLEAN | TRUE = in-network, FALSE = out-of-network |
region | VARCHAR(50) | Geographic region of the provider |
insurance.claim| Column | Type | Notes |
|---|---|---|
claim_id | BIGSERIAL PRIMARY KEY | Auto-incrementing surrogate key |
member_id | BIGINT FK → member | Required — every claim belongs to a member |
provider_id | BIGINT FK → provider | Required — every claim belongs to a provider |
service_date | DATE | Date the service was rendered |
diagnosis_code | VARCHAR(8) | ICD-10 code (e.g. 'Z00.00') |
procedure_code | VARCHAR(8) | CPT code (e.g. '99213') |
billed_amount | NUMERIC(12,2) | Amount the provider billed |
allowed_amount | NUMERIC(12,2) | Maximum amount the plan will pay |
paid_amount | NUMERIC(12,2) | Amount actually paid by the plan |
place_of_service | VARCHAR(20) | E.g. 'Office', 'Hospital' |
-- Speeds up joins from claim → member
CREATE INDEX IF NOT EXISTS idx_claim_member ON insurance.claim(member_id);
-- Speeds up date-range queries on claims
CREATE INDEX IF NOT EXISTS idx_claim_service_date ON insurance.claim(service_date);
Add new indexes only when a slow query is identified. Over-indexing slows down INSERT/UPDATE.
SELECT
DATE_TRUNC('month', c.service_date) AS month,
m.region,
COUNT(*) AS claim_count,
SUM(c.paid_amount) AS paid_total
FROM insurance.claim c
JOIN insurance.member m ON c.member_id = m.member_id
GROUP BY 1, 2
ORDER BY 1, 2;
SELECT
p.in_network,
COUNT(*) AS claims,
AVG(c.paid_amount) AS avg_paid,
SUM(c.paid_amount) AS total_paid
FROM insurance.claim c
JOIN insurance.provider p ON c.provider_id = p.provider_id
GROUP BY p.in_network;
SELECT c.*
FROM insurance.claim c
JOIN insurance.member m ON c.member_id = m.member_id
WHERE m.termination_date IS NULL
OR m.termination_date > CURRENT_DATE;
SELECT
c.provider_id,
p.specialty,
SUM(c.paid_amount) AS paid_total
FROM insurance.claim c
JOIN insurance.provider p ON c.provider_id = p.provider_id
GROUP BY 1, 2
ORDER BY paid_total DESC
LIMIT 10;
# Drop and recreate the schema, then re-apply DDL
make db-reset
# Equivalent raw SQL (strip the +psycopg2 driver prefix for psql compatibility)
PSQL_URL="${DATABASE_URL/+psycopg2/}"
psql "$PSQL_URL" -c "DROP SCHEMA IF EXISTS insurance CASCADE;"
psql "$PSQL_URL" -c "CREATE SCHEMA insurance;"
psql "$PSQL_URL" -f src/sql/ddl_create_tables.sql
src/load.py uses a truncate-then-load strategy so repeated runs are safe:
-- Runs inside a single transaction
TRUNCATE insurance.claim, insurance.provider, insurance.member CASCADE;
This means every make kaggle-load call replaces all data. Do not run it on a production database with live data unless you intend to replace everything.
claim must reference an existing member and provider. PostgreSQL enforces this automatically.NUMERIC(12,2) stores up to 10 digits before the decimal and exactly 2 after. Never store amounts as FLOAT — floating-point rounding causes accounting errors.DATE, never as VARCHAR. This enables range comparisons and DATE_TRUNC without casting.insurance schema, not public. This keeps the database clean if other schemas are added later.The application connects using the DATABASE_URL environment variable:
DATABASE_URL=postgresql+psycopg2://postgres:postgres@localhost:5432/insurdb
The psql CLI accepts a slightly different format (no driver prefix):
psql "postgresql://postgres:postgres@localhost:5432/insurdb"