<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Otwoma E.</title>
    <description>The latest articles on DEV Community by Otwoma E. (@otasium).</description>
    <link>https://dev.to/otasium</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F939091%2Fd1f988ea-b3fa-4dca-a7cd-19c445783941.jpg</url>
      <title>DEV Community: Otwoma E.</title>
      <link>https://dev.to/otasium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/otasium"/>
    <language>en</language>
    <item>
      <title>Building a Sales Analytics Data Warehouse with PostgreSQL</title>
      <dc:creator>Otwoma E.</dc:creator>
      <pubDate>Wed, 26 Aug 2026 02:26:39 +0000</pubDate>
      <link>https://dev.to/otasium/building-a-sales-analytics-data-warehouse-with-postgresql-1e29</link>
      <guid>https://dev.to/otasium/building-a-sales-analytics-data-warehouse-with-postgresql-1e29</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Modern businesses generate large volumes of transactional data from systems such as ecommerce platforms, point-of-sale systems, CRM tools, and financial applications. However, raw data alone does not create business value. Organizations need reliable systems that transform operational data into structured, trusted, and accessible information for decision-making.&lt;/p&gt;

&lt;p&gt;In this project, I built an end-to-end sales analytics data warehouse using PostgreSQL, SQL, Python, and Streamlit. The objective was to simulate a real-world analytics engineering workflow: ingest raw sales data, clean and transform it, model it into a dimensional warehouse, create analytical datasets, and prepare the data for business intelligence reporting.&lt;/p&gt;

&lt;p&gt;The project demonstrates key data warehousing concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data warehouse architecture&lt;/li&gt;
&lt;li&gt;ETL/ELT workflows&lt;/li&gt;
&lt;li&gt;Data modeling&lt;/li&gt;
&lt;li&gt;Star schema design&lt;/li&gt;
&lt;li&gt;Fact and dimension tables&lt;/li&gt;
&lt;li&gt;Surrogate keys&lt;/li&gt;
&lt;li&gt;Data quality validation&lt;/li&gt;
&lt;li&gt;Analytics layers&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Data Warehouse Architecture
&lt;/h1&gt;

&lt;p&gt;The project follows a layered architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSV Files
   |
   ↓
Raw Layer
   |
   ↓
Staging Layer
   |
   ↓
Data Mart
   |
   ↓
Analytics Layer
   |
   ↓
Dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer has a specific responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw Layer
&lt;/h2&gt;

&lt;p&gt;The raw layer stores source data exactly as received.&lt;/p&gt;

&lt;p&gt;Tables include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customers&lt;/li&gt;
&lt;li&gt;Products&lt;/li&gt;
&lt;li&gt;Sales transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping raw data unchanged provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auditability&lt;/li&gt;
&lt;li&gt;Reproducibility&lt;/li&gt;
&lt;li&gt;Easier debugging&lt;/li&gt;
&lt;li&gt;Ability to rebuild downstream models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a production environment, this layer could receive data from APIs, SaaS applications, operational databases, or cloud storage.&lt;/p&gt;




&lt;h1&gt;
  
  
  Staging Layer
&lt;/h1&gt;

&lt;p&gt;The staging layer prepares raw data for analytical use.&lt;/p&gt;

&lt;p&gt;Transformations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing duplicates&lt;/li&gt;
&lt;li&gt;Standardizing formats&lt;/li&gt;
&lt;li&gt;Cleaning missing values&lt;/li&gt;
&lt;li&gt;Validating data types&lt;/li&gt;
&lt;li&gt;Applying business rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separating cleaning logic from reporting logic ensures that downstream analytics are consistent and reusable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Dimensional Modeling
&lt;/h1&gt;

&lt;p&gt;The warehouse uses a star schema design.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 dim_customer

                       |

                       |

dim_date -------- fact_sales -------- dim_product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design separates measurable business events from descriptive context.&lt;/p&gt;




&lt;h1&gt;
  
  
  Fact Tables
&lt;/h1&gt;

&lt;p&gt;The central fact table is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fact_sales
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The grain is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One row represents one sales transaction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Metrics stored include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantity sold&lt;/li&gt;
&lt;li&gt;Unit price&lt;/li&gt;
&lt;li&gt;Sales amount&lt;/li&gt;
&lt;li&gt;Profit&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Dimension Tables
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Customer Dimension
&lt;/h2&gt;

&lt;p&gt;Stores customer attributes such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Location&lt;/li&gt;
&lt;li&gt;Customer characteristics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supports analysis such as customer lifetime value and segmentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Dimension
&lt;/h2&gt;

&lt;p&gt;Stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product names&lt;/li&gt;
&lt;li&gt;Categories&lt;/li&gt;
&lt;li&gt;Brands&lt;/li&gt;
&lt;li&gt;Suppliers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supports product and profitability analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Date Dimension
&lt;/h2&gt;

&lt;p&gt;Stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Year&lt;/li&gt;
&lt;li&gt;Month&lt;/li&gt;
&lt;li&gt;Quarter&lt;/li&gt;
&lt;li&gt;Week&lt;/li&gt;
&lt;li&gt;Day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enables efficient time-based analysis.&lt;/p&gt;




&lt;h1&gt;
  
  
  Surrogate Keys
&lt;/h1&gt;

&lt;p&gt;The warehouse uses surrogate keys to create stable internal identifiers.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_key = 1
customer_id = 4318
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source identifier is retained for traceability, while the warehouse manages its own keys.&lt;/p&gt;

&lt;p&gt;This approach supports changing source systems and historical tracking.&lt;/p&gt;




&lt;h1&gt;
  
  
  Analytics Layer
&lt;/h1&gt;

&lt;p&gt;Business-ready views were created to answer important questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monthly Sales Performance
&lt;/h2&gt;

&lt;p&gt;Provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue trends&lt;/li&gt;
&lt;li&gt;Profit trends&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;li&gt;Units sold&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Product Performance
&lt;/h2&gt;

&lt;p&gt;Provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best-selling products&lt;/li&gt;
&lt;li&gt;Revenue by product&lt;/li&gt;
&lt;li&gt;Product profitability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Customer Lifetime Value
&lt;/h2&gt;

&lt;p&gt;Provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customer spend&lt;/li&gt;
&lt;li&gt;Average order value&lt;/li&gt;
&lt;li&gt;Customer profitability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Customer Segmentation
&lt;/h2&gt;

&lt;p&gt;Uses customer behaviour to classify customers into segments such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Champions&lt;/li&gt;
&lt;li&gt;Loyal Customers&lt;/li&gt;
&lt;li&gt;Potential Loyalists&lt;/li&gt;
&lt;li&gt;At Risk&lt;/li&gt;
&lt;li&gt;Lost&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sales Channel Performance
&lt;/h2&gt;

&lt;p&gt;Analyzes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue by channel&lt;/li&gt;
&lt;li&gt;Profit by channel&lt;/li&gt;
&lt;li&gt;Orders by channel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sales Representative Performance
&lt;/h2&gt;

&lt;p&gt;Measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue generated&lt;/li&gt;
&lt;li&gt;Orders handled&lt;/li&gt;
&lt;li&gt;Profit contribution&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Data Quality
&lt;/h1&gt;

&lt;p&gt;Reliable analytics require trusted data.&lt;/p&gt;

&lt;p&gt;Validation checks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary key constraints&lt;/li&gt;
&lt;li&gt;Foreign key constraints&lt;/li&gt;
&lt;li&gt;Row count validation&lt;/li&gt;
&lt;li&gt;Revenue reconciliation&lt;/li&gt;
&lt;li&gt;Referential integrity checks&lt;/li&gt;
&lt;li&gt;Duplicate detection&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Future Improvements
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Automated Data Pipelines
&lt;/h2&gt;

&lt;p&gt;The current workflow can be enhanced using orchestration tools such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apache Airflow&lt;/li&gt;
&lt;li&gt;Dagster&lt;/li&gt;
&lt;li&gt;Prefect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These would automate scheduling, monitoring, and failure handling.&lt;/p&gt;




&lt;h2&gt;
  
  
  dbt Transformations
&lt;/h2&gt;

&lt;p&gt;SQL transformations could be migrated to dbt to provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version-controlled models&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Data lineage&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Incremental Loading
&lt;/h2&gt;

&lt;p&gt;Instead of reprocessing all data, production pipelines would process only new or changed records.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster execution&lt;/li&gt;
&lt;li&gt;Lower compute costs&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Slowly Changing Dimensions
&lt;/h2&gt;

&lt;p&gt;Customer and product attributes change over time.&lt;/p&gt;

&lt;p&gt;Slowly Changing Dimensions, especially Type 2 dimensions, allow historical changes to be tracked while preserving previous states.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production Data Sources
&lt;/h2&gt;

&lt;p&gt;If the data came from real systems, the architecture would likely include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify APIs&lt;/li&gt;
&lt;li&gt;Salesforce&lt;/li&gt;
&lt;li&gt;ERP systems&lt;/li&gt;
&lt;li&gt;Payment platforms&lt;/li&gt;
&lt;li&gt;Cloud databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional considerations would include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;API limits&lt;/li&gt;
&lt;li&gt;Schema changes&lt;/li&gt;
&lt;li&gt;Duplicate events&lt;/li&gt;
&lt;li&gt;Late arriving data&lt;/li&gt;
&lt;li&gt;Data governance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Cloud Deployment
&lt;/h2&gt;

&lt;p&gt;A production warehouse could move from local PostgreSQL to platforms such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snowflake&lt;/li&gt;
&lt;li&gt;BigQuery&lt;/li&gt;
&lt;li&gt;Amazon Redshift&lt;/li&gt;
&lt;li&gt;Azure Synapse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard could then connect directly to cloud analytics infrastructure.&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This project demonstrates the core principles behind modern analytics engineering.&lt;/p&gt;

&lt;p&gt;It combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data ingestion&lt;/li&gt;
&lt;li&gt;Data cleaning&lt;/li&gt;
&lt;li&gt;Dimensional modeling&lt;/li&gt;
&lt;li&gt;SQL transformations&lt;/li&gt;
&lt;li&gt;Business analytics&lt;/li&gt;
&lt;li&gt;Dashboard preparation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key lesson is that analytics is not only about writing queries. It is about building reliable systems that transform raw data into trusted information for decision-making.&lt;/p&gt;

&lt;p&gt;I had fun doing this kind of project outside of work, so I will definitely do more.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Ideal Transformation Steps in dbt</title>
      <dc:creator>Otwoma E.</dc:creator>
      <pubDate>Tue, 11 Oct 2022 21:54:03 +0000</pubDate>
      <link>https://dev.to/otasium/ideal-transformation-steps-in-dbt-4kcf</link>
      <guid>https://dev.to/otasium/ideal-transformation-steps-in-dbt-4kcf</guid>
      <description>&lt;p&gt;Analytics is a cornerstone of decision making at many of the world's most successful institutions. Having the right tools to make the most of your data is therefore key in maintaining an advantage over competitors and innovating at the cutting edge. Everyday, more than 2.5 quintillion bytes of raw data is produced; that's 18 zeros. Transforming this massive amount of data is therefore a key step towards making it useful for any practical applications. &lt;br&gt;
&lt;strong&gt;dbt&lt;/strong&gt; (data build tool) is a powerful tool to transform raw data into various forms to allow the generation of insights for any organization. The modern data stack can be broken down to three key steps, Extraction, Transformation and Loading, aka ETL. dbt handles the T in ETL. In order to create an efficient data warehouse, dbt enables a stepwise data transformation process that allows for flexibility and modularity that supports version control like conventional software engineering. &lt;/p&gt;

&lt;h4&gt;
  
  
  Sources (src_) - Raw Data
&lt;/h4&gt;

&lt;p&gt;The first layer in your transformation process is the ingestion of raw data from your production systems. This data can be stored on Snowflake, S3 or BigQuery, dbt allows for integration from a variety of different sources. No additional transformation and are essentially a base for the rest of the transformation process. These tables have names with the prefix &lt;code&gt;src_... .sql&lt;/code&gt; and follow the dbt &lt;code&gt;snake-case&lt;/code&gt; naming convention.&lt;/p&gt;

&lt;h4&gt;
  
  
  Staging (stg_) - Lightly Transformed Raw Data
&lt;/h4&gt;

&lt;p&gt;Typically materialized as views, these are tables with a one-to-one relationship with sources tables. They contain very light transformations that clean and standardize the raw data before any further modifications are done downstream. For instance, you may want to change the names of certain columns to match business logic or avoid confusion with existing logic, truncate dates or change the currency or a payment column.&lt;br&gt;
Make sure to limit your transformations to these four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type casting&lt;/li&gt;
&lt;li&gt;Simple calculations&lt;/li&gt;
&lt;li&gt;Categorizing data (think &lt;code&gt;CASE WHEN ...&lt;/code&gt; statements)&lt;/li&gt;
&lt;li&gt;Renaming columns
If a staging table is built on top of a source table names &lt;code&gt;src_farmer_payments.sql&lt;/code&gt; then the staging table built on top of it can be called &lt;code&gt;stg_farmer_payments.sql&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Intermediate (int_) Models
&lt;/h4&gt;

&lt;p&gt;After transforming data to the staging models, you may find it useful to have another layer before creating the final fact or dimension models that are the final marts that go in yoru data warehouse. Intermediate models are useful for performing slightly more intricate calculations meant to enrich the data before a mart is created. For instance, based on &lt;code&gt;stg_farmer_payments.sql&lt;/code&gt; from the examples above, we can have &lt;code&gt;int_farmer_payments_pivoted.sql&lt;/code&gt; which provides a different look at the data and can be used to create a different mart. &lt;br&gt;
Intermediate models should be built on staging models to leverage the transformations already done on raw data. You can use intermediate models for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolating complex processes in your transformation process for easier debugging later&lt;/li&gt;
&lt;li&gt;Collapse a column to constituent parts before using it to create a mart, e.g ungrouping payments or undoing a pivot.&lt;/li&gt;
&lt;li&gt;Simplifying data marts - put some joins in intermediate models to have simpler data marts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Fact (fct_) Models
&lt;/h4&gt;

&lt;p&gt;The next step in the dbt transformation process is creating fact models that contain immutable data. This is typically data about events that have occurred or are currently happening. For instance, this can be data about user sessions, transactions, orders, votes, stories, sales calls etc. They are typically narrow and long tables. The naming convention is &lt;code&gt;fct_ ... .sql&lt;/code&gt; so we could have &lt;code&gt;fct_payment_receipts.sql&lt;/code&gt; to track each payment received by a company.&lt;/p&gt;

&lt;h4&gt;
  
  
  Dimensions (dim_) Models
&lt;/h4&gt;

&lt;p&gt;These models are based on previous models that are already transformed. Typically used to represent data about a person, place or thing. These models are very wide and short. For instance, you can use these to store all the static information about a patient in a hospital, or about a bank branch or a city landmark. Naming convention is as follows &lt;code&gt;dim_patients.sql&lt;/code&gt;, &lt;code&gt;dim_students.sql&lt;/code&gt;, you get the idea. &lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Models
&lt;/h4&gt;

&lt;p&gt;It is entirely possible that your organization has special data and business logic that would necessitate the creation of a new model with a different name prefix. dbt allows you to do this, however, you should endeavour to maintain dbt folder structure recommendations to ensure your project is clean.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;These transformation steps are inline with the recommended dbt folder structure. You can expected to see &lt;code&gt;src_&lt;/code&gt; tables in the Sources folder, &lt;code&gt;dim_&lt;/code&gt; in the Dimensions folder and so forth. You can read more about dbt folder structure and transformations on the &lt;a href="https://docs.getdbt.com/guides/best-practices"&gt;dbt docs page&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>dbt</category>
      <category>sql</category>
      <category>database</category>
    </item>
  </channel>
</rss>
