Portfolio Case Study

Municipal Finance Lab

A fictional municipal revenue environment built to practice PostgreSQL, SQL reporting, relational data modeling, business analysis, financial analysis, transaction tracking, audit-style thinking, and Tableau dashboard work.

Overview

The goal is analyst practice, not just SQL syntax.

Municipal Finance Lab simulates a local government finance operation that manages taxpayer accounts, tax categories, assessments, statuses, payments, transactions, reporting, and dashboards.

The project started as SQL practice, then became a structured environment for learning how database design supports business questions, financial reporting, operational review, and audit evidence.

Interactive Study Companion

The case study now has a game layer for repeatable analyst practice.

The Municipal Finance Lab Companion turns the project into a case-based learning tool. Instead of only reading about tables, joins, balances, and exceptions, the companion asks the learner to build the analyst path from business question to SQL-ready reporting view.

This keeps the project connected to the larger site idea: learn the concept, apply it in a working system, reflect on the process, then turn the result into public proof.

Professional Relevance

The project connects technical learning to the work analysts are asked to do.

Municipal Finance Lab serves as a practical environment for developing skills in SQL, PostgreSQL, relational data modeling, reporting, business analysis, financial systems, audit review, and dashboard development.

The project bridges technical practice with real operational questions that analysts, ERP teams, finance departments, and compliance-focused organizations need to answer with reliable data.

Lab & Live Environments

The project now mirrors a small lab and a live-style finance environment.

Live taxpayers 500
Live assessments 500
Live payments 166
Transactions 170

Business Questions

The model is useful because it can answer operational finance questions.

The live-style database supports questions about assessment volume, revenue categories, payment activity, outstanding balances, delinquency, and audit exceptions.

Each question becomes a way to practice the analyst workflow: define the business need, identify the grain of the data, join the right tables, reconcile the totals, and explain the result.

Progress Metrics

The build has enough structure to show progress across database, reporting, and dashboard work.

Core tables 9
Report sections 12
Dashboards 1
Environment versions 2

Environment Strategy

The original lab stays simple while the live database carries the operational story.

The project now uses two database environments. The original municipal_finance_lab database remains a small reference model for basic SQL, joins, and schema learning.

The richer municipal_finance_live database is the main working environment. It contains fuller operating data, payment activity, transaction history, status exceptions, and the reporting examples used for portfolio work.

Data Modeling

The model turns municipal finance concepts into related tables and business rules.

Lookup taxpayer_types

Reference data that classifies taxpayers as individual, business, nonprofit, or government.

feeds taxpayer records
Master table taxpayers

The taxpayer entity. Stores account numbers, names, addresses, ZIP codes, and taxpayer type IDs.

feeds assessments
Assessment table assessments

The assessment entity. Stores tax year, period, amount, due date, taxpayer ID, tax type ID, and status ID.

Lookup tax_types

Reference data for personal property, real estate, business license, meals, and admissions tax.

Lookup assessment_status

Reference data for open, paid, delinquent, adjusted, and cancelled assessment states.

Payment detail payments

A payment entity tied to an assessment, with method, dates, amount, and receipt number.

Ledger activity assessment_transactions

A broader transaction entity for payments, adjustments, cancellations, refunds, and write-offs.

Modeling Decisions

The project practices the questions a business analyst asks before the dashboard exists.

Data modeling in this lab means deciding what the real-world business objects are, how they relate, which values deserve lookup tables, and which rules should be visible through SQL.

A key modeling decision was separating payments from broader assessment transactions. Payments can have receipt numbers and payment methods, while adjustments, cancellations, refunds, and write-offs still need transaction history even when they are not payments.

Build Progress

The project is moving from database construction into live-style reporting.

Part One

Database Design & Data Modeling

Built the schema from scratch, practiced primary keys, foreign keys, parent and child tables, lookup tables, data types, constraints, environment naming, and database rebuild drills.

Part Two

SQL Analysis, Payments & Transactions

Practicing reporting queries that combine taxpayer, assessment, tax type, status, payment, and transaction information into analyst-ready outputs.

Dashboard Layer

Tableau Assessment Dashboard

Built an early assessment dashboard showing assessment count, total assessed amount, assessment status, and tax type views before expanding into live operations reporting.

Reporting Practice

The useful question is: what does the data help a team decide?

The reporting work is shifting from "how do I write SQL?" to "what business question am I answering?" That is the practical difference between syntax practice and analyst work.

SELECT
  tt.tax_type_name,
  COUNT(*) AS assessment_count,
  SUM(a.assessment_amount) AS total_assessment_amount
FROM assessments AS a
JOIN tax_types AS tt
  ON a.tax_type_id = tt.tax_type_id
GROUP BY tt.tax_type_name
ORDER BY total_assessment_amount DESC;

Lessons

Mistakes became part of the learning system.

  • Always verify the active database before creating or changing tables.
  • Account numbers and ZIP codes should be VARCHAR, not INTEGER.
  • Dates should use DATE and be inserted with quotes.
  • JOIN conditions connect keys to keys; SELECT chooses the readable output.
  • GROUP BY turns detail rows into management summaries.
  • WHERE filters rows before grouping; HAVING filters grouped results.
  • A payment is one kind of transaction, but not every transaction is a payment.
  • Status labels and payment totals need reconciliation because they answer different questions.
  • Small lab and live-style environments make it easier to separate learning from operating-data practice.

Dashboard Iteration

The first Tableau view validates the assessment model before the live dashboard expands.

The first Tableau dashboard focuses on assessment count, total assessed amount, assessment amount by status, and assessment amount by tax type. It is intentionally an early lab version, not the final live operations dashboard.

The next dashboard iteration will use municipal_finance_live to add payments received, outstanding balances, collection rates, delinquency, transaction activity, and audit exceptions.

Next

The next milestone is a live dashboard-ready reporting dataset.

The next Tableau phase will start with one clean query that combines taxpayer information, taxpayer type, tax type, assessment status, tax year, due date, assessment amount, payment totals, balance due, and transaction activity.

That dataset will become the foundation for dashboards focused on assessment totals, status monitoring, delinquency, tax category performance, collections, exceptions, and management review.