<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ezeliora Godsfavour on Medium]]></title>
        <description><![CDATA[Stories by Ezeliora Godsfavour on Medium]]></description>
        <link>https://medium.com/@ezeliorafavour?source=rss-383d2760ba79------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*zTWIFjtJjMc173NxPkT2_Q.jpeg</url>
            <title>Stories by Ezeliora Godsfavour on Medium</title>
            <link>https://medium.com/@ezeliorafavour?source=rss-383d2760ba79------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 09:34:32 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ezeliorafavour/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Women’s Access to Loans and Financial Inclusion Across Countries]]></title>
            <link>https://medium.com/@ezeliorafavour/womens-access-to-loans-and-financial-inclusion-across-countries-69e698199276?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/69e698199276</guid>
            <category><![CDATA[financial-inclusion]]></category>
            <category><![CDATA[women-inclusion]]></category>
            <category><![CDATA[finance]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Mon, 12 Jan 2026 07:30:00 GMT</pubDate>
            <atom:updated>2026-01-12T07:43:29.716Z</atom:updated>
            <content:encoded><![CDATA[<p>Women’s access to financial services is widely recognized as a key driver of economic growth, poverty reduction, and gender equality. Over the past decade, governments, financial institutions, and development organizations have invested heavily in expanding financial access for women, particularly through bank accounts and digital financial services.</p><p>However, access alone does not guarantee empowerment. While more women may now own bank accounts, many still face barriers to using these services effectively, especially when it comes to accessing credit, starting businesses, and growing enterprises.</p><p>This project explores women’s financial inclusion from a data-driven perspective by analyzing global trends in <strong>women’s account ownership</strong> and <strong>female business ownership</strong>. Using publicly available data and interactive dashboards, the project seeks to understand whether improvements in financial access are translating into meaningful economic participation for women.</p><h4>Problem Statement</h4><p>Despite global efforts to close the gender gap in financial access, significant disparities remain across countries and regions. In many cases, women may have bank accounts but lack access to loans, credit facilities, or financial products that support entrepreneurship and wealth creation.</p><p>The key challenge addressed in this project is:</p><blockquote><strong><em>Does increased financial access for women lead to real economic empowerment and participation, particularly through business ownership?</em></strong></blockquote><p>Without answering this question, policymakers and financial institutions risk focusing only on surface-level inclusion metrics, such as account ownership, while overlooking deeper structural barriers that limit women’s economic opportunities.</p><h4>Project Objective</h4><p>The main objective of this project is to analyze and visualize global trends in women’s financial inclusion and economic participation using reliable, real-world data.</p><p>Specifically, the project aims to:</p><ul><li>Examine changes in women’s financial account ownership over time</li><li>Identify countries with the largest improvements or declines in inclusion</li><li>Analyze patterns in female business ownership across regions and income levels</li><li>Compare financial access with actual economic participation</li><li>Highlight gaps between access and empowerment</li><li>Provide data-driven insights and recommendations for policymakers, development organizations, and financial institutions</li></ul><p>The final output of this project is an interactive <strong>Power BI dashboard</strong> designed to help users explore trends, compare countries, and uncover actionable insights related to women’s financial inclusion and economic participation. Prior to dashboard development, <strong>Python was used for initial data exploration, cleaning, and visualization</strong>, enabling early pattern detection and validation of key indicators before translating the analysis into an interactive business intelligence tool.</p><h4><strong>Data Description</strong></h4><h4>Data Used</h4><p>This project uses <strong>women’s financial inclusion data</strong> covering multiple countries across several years. The dataset includes key indicators such as <strong>female and male account ownership</strong>, <strong>gender gaps in access to financial services</strong>, and <strong>women’s participation in business ownership</strong>. These indicators allow for meaningful comparisons across <strong>countries, regions, and time periods</strong>.</p><p>The dataset makes it possible to:</p><ul><li>Measure <strong>progress in women’s financial access</strong> over time</li><li>Identify <strong>gender gaps</strong> between men and women</li><li>Compare <strong>regional and country-level differences</strong></li><li>Examine whether <strong>financial access translates into real economic participation</strong>, such as business ownership</li></ul><p>Because of its structured and time-based format, the dataset is highly suitable for <strong>data analysis and visualization</strong>. It supports trend analysis and enables the creation of <strong>interactive dashboards in Power BI</strong>, while <strong>Python</strong> was used for initial data exploration, cleaning, and visualization. This workflow allows the analysis to move from raw data to <strong>clear, actionable insights</strong>.</p><h4>Data Source</h4><p>The data used in this project was sourced from <strong>World Bank Open Data</strong>, accessed through the <strong>World Bank API</strong> and scraped using Python. This is a trusted, publicly available global dataset widely used in economic and social research.</p><p>This dataset was chosen because <strong>financial inclusion is closely linked to women’s economic empowerment</strong>. Access to financial services such as bank accounts, savings, and credit is often the first step toward independence, entrepreneurship, and long-term economic growth. Despite this, many women around the world continue to face structural and economic barriers to accessing financial services.</p><h4>Time Period</h4><ul><li><strong>2011 to 2024</strong></li></ul><p>This time range allows for long-term trend analysis and helps capture both periods of progress and decline in women’s financial inclusion across countries.</p><h4>Key Variables</h4><p>The dataset includes the following key variables:</p><ul><li>Country Name / Country Code</li><li>Year</li><li>Indicator Name, including: Women with financial accounts (% age 15+), Men with financial accounts (% age 15+), Firms with female ownership (%)</li><li>Value (percentage)</li><li>Region/Country</li></ul><h4>Data Limitations</h4><p>While the dataset is comprehensive, several limitations were identified:</p><ul><li>Some countries had <strong>missing values</strong> for certain years</li><li><strong>Regional aggregates</strong> could distort country-level insights</li><li><strong>Business ownership data</strong> was not available for all countries</li></ul><p>These limitations were carefully handled during <strong>data cleaning, filtering, and analysis</strong> to ensure that insights remained accurate and reliable.</p><h4>Methodology</h4><h4>Data Preparation &amp; Cleaning</h4><p>Before analysis, the data required significant preparation to ensure accuracy.</p><h4>Key steps included:</h4><ul><li>Standardizing column names and formats</li><li>Removing duplicate records</li><li>Dropping rows with missing values</li><li>Converting year values to numeric format</li><li>Separating <strong>countries from regions</strong> to avoid misleading comparisons</li><li>Normalizing indicator names for consistency</li></ul><p>These steps ensured the dataset was reliable and analysis-ready.</p><h4>Tools &amp; Technologies Used</h4><ul><li><strong>Python</strong> (Requests, Pandas): Data scraping, cleaning and visualization</li><li><strong>World Bank API</strong>: Data source</li><li><strong>Power BI</strong>: Dashboard creation and interactive analysis</li><li><strong>Excel</strong></li></ul><p>Python was chosen for automation and flexibility, while Power BI enabled interactive storytelling and exploration.</p><h4>Data Exploration &amp; Preprocessing</h4><p>During initial exploration, several patterns quickly became visible.</p><h4>What I Discovered Early</h4><ul><li>Women’s account ownership was generally increasing</li><li>Large differences existed between countries</li><li>Some regions masked poor-performing countries</li><li>Business ownership values were much lower than account ownership</li></ul><h4>Findings</h4><ul><li>Women’s financial inclusion has improved globally, with an average increase of about <strong>9.2%</strong></li><li>Significant gender gaps still exist in many countries</li><li>Progress is uneven, with some countries improving rapidly while others decline</li><li>Female business ownership remains much lower than account ownership</li><li>High female entrepreneurship appears in both developing and emerging economies</li></ul><h4>Insights</h4><ul><li>Financial access does not automatically lead to empowerment</li><li>Progress in inclusion is fragile and can reverse</li><li>Income level alone does not determine women’s entrepreneurship</li><li>Structural barriers like credit access and legal rights still limit women</li></ul><p>The data clearly shows that <strong>access is only the first step</strong>.</p><h4>Recommendations</h4><p>Based on the analysis, the following actions are recommended:</p><ol><li>Move beyond account ownership to focus on <strong>credit access and usage</strong></li><li>Expand digital and mobile banking for underserved women</li><li>Improve legal and property rights to enable collateral-based lending</li><li>Increase financial literacy programs targeted at women</li><li>Support women entrepreneurs with SME financing and mentorship</li><li>Monitor countries showing declining inclusion to protect gains</li><li>Collect more gender-disaggregated financial data</li></ol><h4>Dashboard</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Fa6Z3wahG_MMgpTZbzaJxg.png" /></figure><p>The dashboard allows users to explore trends, compare countries, and understand the gap between financial access and economic participation.</p><h4>Conclusion</h4><p>This project shows that while women’s financial inclusion is improving globally, <strong>true empowerment remains incomplete</strong>.</p><p>Opening bank accounts is important, but it is not enough.</p><p>Real inclusion requires access to credit, supportive policies, and opportunities that allow women to fully participate in the economy. By combining data analysis with interactive dashboards, this project helps shift the conversation from how many women are included to how well they are empowered.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69e698199276" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[In Memory of Chidinma Adaeze Ezeliora]]></title>
            <link>https://medium.com/@ezeliorafavour/in-memory-of-chidinma-adaeze-ezeliora-dd048688cacc?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/dd048688cacc</guid>
            <category><![CDATA[grief-and-loss]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sun, 04 Jan 2026 19:59:12 GMT</pubDate>
            <atom:updated>2026-01-04T19:59:12.538Z</atom:updated>
            <content:encoded><![CDATA[<p>Grief has always been something I struggle with. I honestly don’t know how to grieve, and I wish I had someone to talk to about you, someone who knew you, someone with whom we shared memories together. I don’t know how to start those conversations, so instead I am writing about the times I’ve thought of you and any other thing that comes to mind.</p><p>This post is three or four years late. Every time I tried to write or talk about it, I broke down in tears. But today, even as I sob, I have decided to write.</p><p>Every time I hear of a woman who dies as a result of femicide, I think of you. I think of how underreported femicide is. Your name never appeared in the news or on social media. I don’t even know his name, I just know that I <strong>hate</strong> him so much. One day, when I am ready, I will update this post with his name. But I don’t trust this country to deliver justice. If only I had known then what I know now, I would have done something, written, protested, anything. I just know I would have tried.</p><p>The fight against femicide will always remain personal to me because you died from it. People don’t understand: you would still be alive if that murderer hadn’t taken your life. You were so lively, so full of joy, and he stole you from us just like that.</p><p>Every December 31st, I think of you. If we were in the village for Christmas, you would have insisted we go for crossover night. You were always the one organizing where to go. We would have slept at your place, eaten if we were hungry, and talked until daybreak, catching up on each other’s lives with little sleep. It was always fun. Someone once asked me why I don’t want to go for crossover night, and when I said it was because you died, he asked if you died on crossover night. I just laughed, because he couldn’t possibly understand.</p><p>I think of you whenever I see your brother. I don’t enjoy football, but whenever Chibueze is playing, I go to watch him. The last time I went, his team won. Nne, you would have been proud. At the end of the match, I hugged him and congratulated him, thinking of you all the while.</p><p>So much has changed since you left. At our recent Umunna get-together, we launched a book that carried the names of everyone from our kindred. Two of your brothers, Akachukwu and Chiagozie, were there, they’ve grown so much. I haven’t seen Chisom since you passed, and it reminds me how different everything feels now. I thought of how your dad would have made the event more lively. I hope both of you are resting in peace.</p><p>I still struggle to visit your home. I haven’t been there since you died. I’m afraid I’ll break down. Death feels so final. What do you mean, you’re gone forever? It doesn’t make sense.</p><p>We had one thing in common: we were the only girls surrounded by brothers. I was the quiet one (not anymore, lol), and you were the social, outgoing one. I loved hanging out with you. You were like the sister I never had.</p><p>I carry guilt about our last encounter. I remember Ifechukwu calling me to say you were in town (Awka) and needed a place to stay. You were waiting at the junction. I was angry at Ife for not giving me a heads-up. If only I had known that would be the last time I saw you. I would have begged you to stay longer. Maybe we would have talked about your boyfriend/fiancé (the one that ended up killing you, crazy!). I don’t know if you realized I was upset, but I regret it deeply. Nne, I am sorry. These days, I don’t care when family or friends show up unannounced, because what if I never see them again?</p><p>I feel guilty that when I try to picture your face, it’s no longer as clear as it used to be. I fear the memories are fading. That’s why I want to do a project in your honor. Your memory must live on.</p><p>This would have been my tribute to you, though I don’t even remember writing one before now. Your death was such a shock. On the day of your burial, I had my final year exams and couldn’t attend. I came the next day instead, and I will never forget how sad your mother was, chai, the pain was unbearable to witness.</p><p>Before the burial, the family carried out an autopsy. They said the boy was in jail, but like I’ve said before, I don’t trust this country to deliver justice. God, I wish I knew his name. My anger burns at the way the family found out you had died, the way he had planned to discard you, Ada Anayo, as if you were nothing. May God continue to bless the mortuary attendant for insisting he reached out to your family members, because how else would we have known ? Oh, God!… they said time heals, but I disagree, your death is and will ALWAYS remain a great loss, but I pray God continues to give us the strength to bear this heavy burden.</p><p>I often wonder where you would have been today. <strong>You were a star</strong>, destined for greatness. In our next life, come back as my cousin again or even better, my sister.</p><p><strong>Chidinma Adaeze Ezeliora, may your soul continue to rest in perfect peace.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dd048688cacc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Standing at another life-defining moment]]></title>
            <link>https://medium.com/@ezeliorafavour/standing-at-another-life-defining-moment-35c208866aac?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/35c208866aac</guid>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sun, 07 Dec 2025 20:19:26 GMT</pubDate>
            <atom:updated>2025-12-08T05:08:40.165Z</atom:updated>
            <content:encoded><![CDATA[<p>Anytime I think about my life (because I’m always thinking, lol), I’ve realized there are these moments that feel <em>life-defining</em>, and I strongly think I’m at another one right now.</p><p>I called my mummy tonight because I was worried, tense, anxious, overthinking has finally given me a headache. I have an interview tomorrow, and for some reason I just needed to hear her voice. I don’t know why I always expect her to do something magical to calm me down, but she did. She reminded me of my name and where it even came from.</p><p>My name is <strong>Godsfavour Oluebubechukwu</strong> (work of a glorious God), and according to my mum, my aunty actually got a revelation before I was born that this was supposed to be my name. And she said that this name is what will continue to follow me. Tell me why I was now crying and emotional. 😭</p><p>She reminded me of how far I’ve come, how God has already gone ahead of me, and then she told me to stop worrying and drink water, that part really cracked me up.</p><p>And to my friends, thank you for the encouragement and for helping me prep. I recently had a bad interview, my first one since I started job-hunting, and I wasn’t prepared at all. I’ll write about that later.</p><p>Right now I’m trying to book a space at a café for tomorrow’s interview because my network at home is not reliable at all, and seriously hoping I finally get the <strong>ONE</strong> chance I need to start the career I have always wanted.</p><p>So if you’re reading this, please wish me well… and say a prayer for me. 💛</p><p>I’m going to end this by quoting Psalm 1 Vs 3:</p><p><strong>“He is like a tree planted by streams of water, yielding its fruit in season, and his leaf does not wither. In all that he does, he prospers.”</strong></p><p>And now I’m personalizing it:</p><p><strong>“Godsfavour Oluebubechukwu is like a tree planted by streams of water, yielding her fruit in season, and her leaf does not wither. In all that she does, she prospers.”</strong></p><p>See you guys next time✨</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=35c208866aac" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Trying My Best: Reflections From a Worn-Out Learner]]></title>
            <link>https://medium.com/@ezeliorafavour/trying-my-best-reflections-from-a-worn-out-learner-05593cf2bcde?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/05593cf2bcde</guid>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sun, 23 Nov 2025 18:25:01 GMT</pubDate>
            <atom:updated>2025-11-23T18:25:01.706Z</atom:updated>
            <content:encoded><![CDATA[<p>I’ve put in so much work this year learning, working, volunteering. Now that the year is coming to an end, I’m burnt out and honestly just ready to wrap everything up. I’m exhausted, and I can’t wait to celebrate Christmas with my family. Christmas is my favorite holiday, and right after that comes my birthday.</p><p>But learning these days has been hard. I’m struggling to study, and the only thing I’m genuinely excited to read right now is fiction (I promised myself I’d read 50 books this year, and I’m currently on book 37). Still, I remind myself that I’m not bigger than the program, the show must go on.</p><p>I recently found a new way to learn: by teaching. I tried creating content on YouTube, but right now it’s overwhelming. With videos, I have to edit, reshoot scenes, learn so many aspects of video production… and honestly, I’m not in the right frame of mind for all that. Writing feels so much better. With writing, I can breathe. So for now, I’ll be sticking to it.</p><p>I’m learning new concepts, and as I’m writing this, I’ve released two articles. I promise to make each article better than the last. I’ll return to creating video content in 2026. My favorite thing about writing is that it allows me to research, learn, practice, and then explain things in a way <em>I</em> understand. In the process, I get to create something I wish I had seen on the internet.</p><p>I’m going to try to write about my year before I close my laptop next month for the holidays, because I love coming back to read these reflections a few months later. I really hope I remember everything that has happened in my career this year, because honestly, this year feels like five years in one, lol.</p><p>This article is basically just to say that I’m learning by teaching — and I’ll be writing more. Stay tuned.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=05593cf2bcde" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Beginner’s Guide to Window Functions in PostgreSQL]]></title>
            <link>https://medium.com/@ezeliorafavour/beginners-guide-to-window-functions-in-postgresql-0c290687885b?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/0c290687885b</guid>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[sql-window-functions]]></category>
            <category><![CDATA[beginner]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sun, 23 Nov 2025 17:30:51 GMT</pubDate>
            <atom:updated>2025-11-23T17:30:51.222Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/982/1*7U70fAQ2cFlIj32rtTC5PA.png" /><figcaption>Click to view image <a href="https://www.google.com/imgres?q=aggregate%20function%20vs%20Window%20Functions%20in%20PostgreSQL&amp;imgurl=https%3A%2F%2Fmiro.medium.com%2F0*6PgyqKUc9nKobVBK.png&amp;imgrefurl=https%3A%2F%2Fmedium.com%2F%40tranquochuyqn93%2Fwindow-function-in-postgresql-131eb293bb31&amp;docid=3OicNcB7DAONpM&amp;tbnid=jbTVZBczfxox-M&amp;vet=12ahUKEwiVzqbu4YiRAxW5V0EAHaLzA5YQM3oECB4QAA..i&amp;w=982&amp;h=311&amp;hcb=2&amp;ved=2ahUKEwiVzqbu4YiRAxW5V0EAHaLzA5YQM3oECB4QAA">source</a></figcaption></figure><h4>Introduction</h4><p>Window functions are a powerful tool in PostgreSQL that let you perform calculations across multiple rows while still keeping all your rows visible. Think of it like having a calculator that can look at other rows while working on the current row.</p><h4>What Are Window Functions?</h4><p>Imagine you have a spreadsheet with sales data. With regular functions, if you want to calculate totals by region, you’d group everything together and lose the individual row details. Window functions are different, they let you add calculations to each row while still being able to “peek” at other related rows.</p><p>For example, you can show each sale AND the total for that region in the same row, without losing any data.</p><h4>Let’s create a simple sales table to practice with:</h4><pre>CREATE TABLE sales (<br>    id SERIAL PRIMARY KEY,<br>    employee VARCHAR(50),<br>    region VARCHAR(50),<br>    amount INT<br>);<br>INSERT INTO sales (employee, region, amount) VALUES<br>(&#39;Alice&#39;, &#39;West&#39;, 500),<br>(&#39;Bob&#39;, &#39;West&#39;, 300),<br>(&#39;Charlie&#39;, &#39;East&#39;, 700),<br>(&#39;Diana&#39;, &#39;East&#39;, 400),<br>(&#39;Eve&#39;, &#39;West&#39;, 600);</pre><p>This gives us a table that looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/788/1*1nDaD6m2ZzlWhz0lKQbxkA.png" /></figure><h4>Basic Window Function Syntax</h4><p>Here’s the basic structure:</p><pre>function_name() OVER (<br>    PARTITION BY column1<br>    ORDER BY column2<br>)</pre><p>Let’s break it down:</p><ul><li><strong>function_name()</strong>: The calculation you want to do (like SUM, AVG, ROW_NUMBER)</li><li><strong>OVER</strong>: This magic word tells PostgreSQL “this is a window function”</li><li><strong>PARTITION BY</strong>: Divides your data into groups (like creating separate mini-tables)</li><li><strong>ORDER BY</strong>: Puts rows in order within each group</li></ul><h4>Example 1: Numbering Rows with ROW_NUMBER()</h4><p>Let’s give each employee a number within their region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) as row_num<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>We’re looking at each row of sales</li><li>PARTITION BY region splits our data into two groups: West and East</li><li>ORDER BY amount DESC sorts by amount from highest to lowest within each region</li><li>ROW_NUMBER() assigns 1, 2, 3... to each row in its group</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/757/1*tUlkz2XHRQBwMohY4CpfsA.png" /></figure><p>See how the numbering starts over for each region? That’s the PARTITION BY at work!</p><h4>Example 2: Finding Totals with SUM()</h4><p>Let’s add a column showing the total sales for each region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    SUM(amount) OVER (PARTITION BY region) as region_total<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>PARTITION BY region again splits data by region</li><li>SUM(amount) adds up all amounts in that region</li><li>The result appears on EVERY row for that region</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/789/1*NDP-7iesNr0sXo27gvINMg.png" /></figure><p>Notice how all West employees see 1400 (500+300+600) and all East employees see 1100 (700+400). The rows aren’t collapsed, everyone gets their own row!</p><h4>Example 3: Creating a Running Total</h4><p>Let’s create a running total that accumulates as we go through each region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    SUM(amount) OVER (PARTITION BY region ORDER BY amount) as running_total<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>PARTITION BY region creates separate groups</li><li>ORDER BY amount processes rows from smallest to largest sale</li><li>SUM(amount) adds up amounts from the start up to the current row</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/793/1*tsArcd6DrBfPvM1uF6ukkg.png" /></figure><p>See how the running_total grows? Bob starts at 300, then Alice adds her 500 to get 800, then Eve adds 600 to reach 1400.</p><h4>Example 4: Ranking with RANK()</h4><p>Let’s rank employees by their sales within each region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    RANK() OVER (PARTITION BY region ORDER BY amount DESC) as rank<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>We’re ranking from highest to lowest sales (DESC means descending)</li><li>If two people had the same amount, they’d get the same rank</li><li>The next rank would skip a number (if two people tie for 1st, the next is 3rd)</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/721/1*Fzi2br2rVz8rjlo3zRII1A.png" /></figure><h4>Example 5: Comparing with Previous Row Using LAG()</h4><p>Let’s see how each employee’s sales compare to the previous employee in their region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    LAG(amount) OVER (PARTITION BY region ORDER BY amount) as previous_amount,<br>    amount - LAG(amount) OVER (PARTITION BY region ORDER BY amount) as difference<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>LAG(amount) looks at the previous row&#39;s amount</li><li>We order by amount so “previous” means the next smaller sale</li><li>We calculate the difference between current and previous</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/958/1*fr4r2und-gqYrwCiEkDtLg.png" /></figure><p>Bob and Diana show NULL because they’re first in their regions , there’s no previous row to look at!</p><h4>Example 6: Calculating Percentages</h4><p>Let’s see what percentage each sale is of their region’s total:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    ROUND(100.0 * amount / SUM(amount) OVER (PARTITION BY region), 2) as percent_of_region<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>SUM(amount) OVER (PARTITION BY region) gets the region total</li><li>We divide each amount by that total and multiply by 100</li><li>ROUND(..., 2) rounds to 2 decimal places</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/832/1*48jTS8Vay9pJRieqz6xKQQ.png" /></figure><p>Eve contributed almost 43% of West region sales!</p><h4>Example 7: Finding the Top Performer in Each Region</h4><p>Let’s tag the highest-selling employee in each region:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    FIRST_VALUE(employee) OVER (PARTITION BY region ORDER BY amount DESC) as top_performer,<br>    FIRST_VALUE(amount) OVER (PARTITION BY region ORDER BY amount DESC) as top_amount<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>ORDER BY amount DESC puts the highest sale first</li><li>FIRST_VALUE(employee) grabs the employee name from that first row</li><li>Every row in the region sees the same top performer</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/970/1*D3CVn8b-bWzxS70MPOOKVg.png" /></figure><h4>Example 8: Looking Ahead with LEAD()</h4><p>Let’s see the next person’s sales amount (the person with the next higher sale):</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    LEAD(amount) OVER (PARTITION BY region ORDER BY amount) as next_higher_amount<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>LEAD(amount) looks at the NEXT row&#39;s amount</li><li>Ordered by amount, so we see who’s ahead of us in sales</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/846/1*hwXvqMrcKDApeRlvqS7ckw.png" /></figure><p>Eve and Charlie are the top in their regions, so there’s no one ahead of them (NULL).</p><h4>Example 9: Average Sales in Each Region</h4><p>Let’s see how each employee compares to their region’s average:</p><pre>SELECT <br>    employee,<br>    region,<br>    amount,<br>    ROUND(AVG(amount) OVER (PARTITION BY region), 2) as region_avg,<br>    amount - ROUND(AVG(amount) OVER (PARTITION BY region), 2) as diff_from_avg<br>FROM sales;</pre><p><strong>What’s happening here:</strong></p><ul><li>AVG(amount) OVER (PARTITION BY region) calculates average for each region</li><li>We subtract to see if each employee is above or below average</li></ul><p><strong>Result:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/932/1*CWioshsJyPO85UokubzLfw.png" /></figure><p>Bob is 166.67 below the West average, while Eve is 133.33 above it!</p><h4>Key Takeaways</h4><ul><li><strong>The Big Difference</strong>: Regular GROUP BY collapses rows. Window functions keep all rows and add calculations.</li><li><strong>PARTITION BY</strong>: Think of this as creating invisible groups. Calculations happen within each group separately.</li><li><strong>ORDER BY</strong>: Controls the order that rows are processed. This matters for running totals, LAG/LEAD, and rankings.</li><li><strong>No ORDER BY</strong>: If you skip ORDER BY, the window function looks at ALL rows in the partition at once (like Example 2 with SUM).</li></ul><h4>Common Mistakes to Avoid</h4><ol><li><strong>Forgetting PARTITION BY</strong>: Without it, the window is the entire table</li><li><strong>Using window functions in WHERE</strong>: You can’t. Use a subquery instead</li><li><strong>Expecting fewer rows</strong>: Window functions don’t reduce rows , you still get one result per input row</li></ol><h4>Practice Tips</h4><p>Start simple! Try these steps:</p><ol><li>Write a regular SELECT to see your data</li><li>Add a simple window function like ROW_NUMBER()</li><li>Experiment with PARTITION BY to see how it divides your data</li><li>Try different ORDER BY options to see how results change</li><li>Combine window functions to create more complex analyses</li></ol><h4>Practice Questions</h4><p>Use the same <strong>sales</strong> table above.<br>Try each question on your own first before checking any solution.</p><ol><li>Add a column that shows each employee’s row number (no partition). <strong><em>Hint:</em></strong><em> </em><em>ROW_NUMBER() OVER (ORDER BY amount)</em></li><li>Show each sale along with the total sales for the entire table. <strong><em>Hint:</em></strong><em> </em><em>SUM(amount) OVER ()</em></li><li>Show each employee along with the average sale across the entire table. <strong><em>Hint:</em></strong><em> </em><em>AVG(amount) OVER ()</em></li><li>Number of employees within each region from highest to lowest sale amount. <strong>Hint:</strong> Partition by region</li><li>Create a running total of sales within each region, ordered by amount.</li><li>Show each employee and the previous sale amount in the same region. <strong>Hint:</strong> LAG(amount)</li><li>Show each employee and the next highest sale in their region. <strong>Hint:</strong> LEAD(amount)</li><li>Find all employees whose sales are above their region’s average. <strong>Hint:</strong> Use a subquery or CTE because window functions can’t be in WHERE.)</li><li>For each region, calculate the difference between each employee’s sale and the region’s top sale. <strong>Hint:</strong> FIRST_VALUE(amount)</li><li>Get the top 2 employees by sales within each region. <strong>Hint:</strong> ROW_NUMBER() then filter</li></ol><p>Window functions become intuitive with practice. Use the sales table above, modify them, and see what happens. That hands-on experience is the best teacher!</p><h4>Solutions</h4><p>If you have difficulty, check out my solutions <a href="https://github.com/favy-codez/Beginner-s-Guide-to-Window-Functions-in-PostgreSQL">here</a>. Each solution includes the SQL query and a short breakdown.</p><h4>Share Your Work</h4><p>I’d love to see how you solved these! Post your solutions on social media and tag me — I’ll cheer you on and give feedback.</p><ul><li><strong>Twitter/X:</strong> <a href="https://x.com/Favour_Ezeliora">here</a></li><li><strong>LinkedIn:</strong> <a href="https://www.linkedin.com/in/godsfavour-ezeliora/">here</a></li></ul><p>Feel free to share your queries, screenshots, or even what you struggled with. I’m excited to see your progress!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0c290687885b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Beginner friendly guide to CTEs (Common Table Expressions) in PostgreSQL]]></title>
            <link>https://medium.com/@ezeliorafavour/a-beginner-friendly-guide-to-ctes-common-table-expressions-in-postgresql-bf5f31416b7a?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/bf5f31416b7a</guid>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[cte]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sat, 22 Nov 2025 10:45:08 GMT</pubDate>
            <atom:updated>2025-11-23T16:52:51.052Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oTkBqUauf7Zvtg7HTwj8Iw.png" /></figure><h4>Introduction</h4><p>Common Table Expressions (CTEs) are one of the most powerful and readable tools in SQL, especially PostgreSQL. They help you break down complex logic into smaller, easier steps.</p><p><strong>In this guide, I will:</strong></p><ul><li>Explain CTEs</li><li>Demonstrate multi-step CTEs</li><li>Combine CTEs with window functions (ROW_NUMBER, running totals, LAG/LEAD)</li><li>Use CTEs to clean messy data</li><li>Explore recursive CTEs</li><li>Share practice questions (with solutions)</li></ul><h4>Setting Up the Tables in PostgreSQL</h4><p>Before we start querying, let’s create some sample tables.<br> Open your PostgreSQL terminal (psql) and run:</p><pre>-- Create customers table<br>CREATE TABLE customers (<br>    customer_id SERIAL PRIMARY KEY,<br>    name VARCHAR(50),<br>    city VARCHAR(50),<br>    referrer_id INT<br>);</pre><pre>-- Insert sample customers<br>INSERT INTO customers (name, city, referrer_id) VALUES<br>(&#39;Alice&#39;, &#39;Lagos&#39;, NULL),<br>(&#39;Bob&#39;, &#39;Abuja&#39;, 1),<br>(&#39;Chika&#39;, &#39;Port Harcourt&#39;, 2),<br>(&#39;David&#39;, &#39;Enugu&#39;, NULL);</pre><pre>-- Create orders table<br>CREATE TABLE orders (<br>    order_id SERIAL PRIMARY KEY,<br>    customer_id INT REFERENCES customers(customer_id),<br>    order_date DATE,<br>    amount NUMERIC<br>);</pre><pre>-- Insert sample orders<br>INSERT INTO orders (customer_id, order_date, amount) VALUES<br>(1, &#39;2025-01-05&#39;, 250),<br>(2, &#39;2025-01-07&#39;, 120),<br>(1, &#39;2025-02-10&#39;, 300),<br>(3, &#39;2025-02-15&#39;, 150),<br>(4, &#39;2025-03-01&#39;, 400);</pre><h4>Quick Explanation</h4><ul><li><strong>SERIAL PRIMARY KEY</strong> → auto-increments IDs for you</li><li><strong>VARCHAR(50)</strong> → text up to 50 characters</li><li><strong>referrer_id</strong> → points to another customer (used later for recursion)</li><li><strong>REFERENCES</strong> → makes sure customer_id exists in customers</li><li><strong>NUMERIC</strong> → great for money, avoids rounding issues</li></ul><h4>Data Story</h4><ul><li>Alice placed two orders (250, 300)</li><li>Bob placed one order (120)</li><li>Chika placed one order (150)</li><li>David placed one order (400)</li></ul><p><strong>Now we’re ready to write some SQL.</strong></p><h4>What Is a CTE?</h4><p>A CTE (Common Table Expression) is a temporary result you create inside a query.</p><p>Think of it as saying:</p><blockquote><em>“Let me create this mini-table for a moment… then use it to write cleaner SQL.”</em></blockquote><h4>Basic Structure</h4><pre>WITH cte_name AS (<br>    SELECT ...<br>)<br>SELECT *<br>FROM cte_name;</pre><p>What <strong>“create this table for a moment”</strong> really means</p><ul><li>It’s <strong>not</strong> a real table</li><li>It only exists <strong>during that one query</strong></li><li>The moment the query finishes → the CTE disappears</li></ul><p>It makes your SQL cleaner, easier to read, and easier to debug.</p><h4>Why Use CTEs?</h4><p>CTEs help you:</p><ul><li>Avoid messy nested queries</li><li>Break logic into steps</li><li>Reuse calculations</li><li>Name each step clearly</li><li>Write readable SQL</li></ul><p>It’s basically writing SQL in step-by-step English.</p><h4>A Simple Example: Get All Orders Above $100</h4><p><strong>Without a CTE:</strong></p><pre>SELECT order_id, customer_id, amount<br>FROM orders<br>WHERE amount &gt; 100;</pre><p><strong>With a CTE:</strong></p><pre>WITH high_orders AS (<br>    SELECT order_id, customer_id, amount<br>    FROM orders<br>    WHERE amount &gt; 100<br>)<br>SELECT *<br>FROM high_orders;</pre><p>Now you can build on top of high_orders without repeating filters.</p><h4>Analogy</h4><p>A CTE is like writing a quick note:</p><p>📝 <em>“Here are all the orders above $100.”</em></p><p>You carry that note into the next step.<br> When the query ends → the note is thrown away.</p><h4>Multi-Step Logic With Multiple CTEs</h4><p>Sometimes one step isn’t enough. You want to calculate something first, then filter from that result.</p><h4>Scenario</h4><p>We want to:</p><ol><li>Calculate total spending per customer</li><li>Get only customers who spent more than $500</li></ol><h4>Query</h4><pre>WITH customer_totals AS (<br>    SELECT customer_id, SUM(amount) AS total_spent<br>    FROM orders<br>    GROUP BY customer_id<br>),<br>big_spenders AS (<br>    SELECT customer_id, total_spent<br>    FROM customer_totals<br>    WHERE total_spent &gt; 500<br>)<br>SELECT *<br>FROM big_spenders;</pre><h4>Breakdown</h4><p><strong>customer_totals</strong></p><ul><li>Groups orders</li><li>Calculates total_spent</li></ul><p><strong>big_spenders</strong></p><ul><li>Filters totals above 500</li></ul><p><strong>Final SELECT</strong></p><ul><li>Shows the final clean result</li></ul><h4>Analogy</h4><p>Like cooking:</p><ul><li>Chop vegetables → customer_totals</li><li>Pick only the fresh ones → big_spenders</li><li>Serve the dish → final SELECT</li></ul><h4>CTEs + Window Functions</h4><p>Window functions let you look at rows <em>in relation</em> to each other. They don’t reduce rows like GROUP BY. They add extra information.</p><h4>Window Functions and What They Do</h4><ul><li><strong>ROW_NUMBER()</strong> → numbering rows</li><li><strong>PARTITION BY</strong> → group rows</li><li><strong>ORDER BY</strong> → sort inside each group</li><li><strong>SUM() OVER</strong> → running totals</li><li><strong>LAG() / LEAD()</strong> → previous / next values</li></ul><p><strong>Think of them as giving each row context:</strong></p><ul><li><strong>ROW_NUMBER</strong> → “you’re 1st, 2nd, 3rd…”</li><li><strong>LAG</strong> → “here’s the previous row”</li><li><strong>LEAD</strong> → “here’s the next row”</li></ul><h4>Example 1: Number Each Customer’s Orders</h4><pre>WITH ordered AS (<br>    SELECT *,<br>           ROW_NUMBER() OVER (<br>               PARTITION BY customer_id<br>               ORDER BY order_date<br>           ) AS rn<br>    FROM orders<br>)<br>SELECT *<br>FROM ordered;</pre><h4>Example 2: Running Total Per Customer</h4><pre>WITH totals AS (<br>    SELECT customer_id, order_id, amount,<br>           SUM(amount) OVER (<br>             PARTITION BY customer_id<br>             ORDER BY order_date<br>           ) AS running_total<br>    FROM orders<br>)<br>SELECT * FROM totals;</pre><h4>Example 3: Previous and Next Order Amount</h4><pre>WITH deltas AS (<br>    SELECT customer_id, order_id, order_date, amount,<br>           LAG(amount) OVER (<br>             PARTITION BY customer_id ORDER BY order_date<br>           ) AS prev_amount,<br>           LEAD(amount) OVER (<br>             PARTITION BY customer_id ORDER BY order_date<br>           ) AS next_amount<br>    FROM orders<br>)<br>SELECT * FROM deltas;</pre><h4>Recursive CTEs: Referral Chains</h4><p>Recursive CTEs let SQL “loop” until no more data can be added.</p><h4>Example: Who Referred Whom?</h4><pre>WITH RECURSIVE referral_chain AS (<br>    -- Start with customers who have no referrer<br>    SELECT customer_id, name, referrer_id<br>    FROM customers<br>    WHERE referrer_id IS NULL<br>UNION ALL<br>    -- Add people referred by those already in the chain<br>    SELECT c.customer_id, c.name, c.referrer_id<br>    FROM customers c<br>    JOIN referral_chain rc ON c.referrer_id = rc.customer_id<br>)<br>SELECT * FROM referral_chain;</pre><h4>CTEs for Cleaning Data</h4><p>CTEs help you clean data step-by-step without writing one giant query.</p><h4>Example 1: Replace NULL amounts with 0</h4><pre>WITH cleaned AS (<br>    SELECT order_id,<br>           COALESCE(amount, 0) AS safe_amount<br>    FROM orders<br>)<br>SELECT * FROM cleaned;</pre><h4>Example 2: Clean Customer Names</h4><pre>WITH cleaned AS (<br>    SELECT name,<br>           TRIM(LOWER(name)) AS clean_name<br>    FROM customers<br>)<br>SELECT * FROM cleaned;</pre><p>Let’s look at a real-world question:</p><h4>Top 5 Customers Last Month</h4><pre>WITH monthly AS (<br>    SELECT customer_id, amount<br>    FROM orders<br>    WHERE EXTRACT(MONTH FROM order_date) = 2<br>),<br>customer_totals AS (<br>    SELECT customer_id, SUM(amount) AS total_spent<br>    FROM monthly<br>    GROUP BY customer_id<br>)<br>SELECT *<br>FROM customer_totals<br>ORDER BY total_spent DESC<br>LIMIT 5;</pre><h4>Practice Questions</h4><p>Try these before checking the solutions:</p><ol><li>Calculate each customer’s total spending, then return customers who spent more than $300.</li><li>Find each customer’s most recent order.</li><li>Compute a running total of spending per customer.</li><li>Find the customer who spent the most in February.</li><li>Clean customer names by:</li></ol><ul><li>making them lowercase</li><li>removing spaces</li><li>replacing NULL with &quot;unknown&quot;</li></ul><h4>Final Tips</h4><ul><li>Start with simple queries, then wrap them in a CTE</li><li>Use multiple CTEs to break complex logic into steps</li><li>Learn window functions , they work beautifully with CTEs</li><li>Use COALESCE to handle NULLs</li><li>Use recursive CTEs for hierarchy problems</li></ul><h4>Solutions</h4><p>If you have difficulty, check out my solutions <a href="https://github.com/favy-codez/A-Beginner-Friendly-Guide-to-CTEs-Common-Table-Expressions-in-PostgreSQL">here</a>. Each solution includes the SQL query and a short breakdown.</p><h4>Share Your Work</h4><p>I’d love to see how you solved these! Post your solutions on social media and tag me — I’ll cheer you on and give feedback.</p><ul><li><strong>Twitter/X:</strong> <a href="https://x.com/Favour_Ezeliora">here</a></li><li><strong>LinkedIn:</strong> <a href="https://www.linkedin.com/in/godsfavour-ezeliora/">here</a></li></ul><p>Feel free to share your queries, screenshots, or even what you struggled with. I’m excited to see your progress!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bf5f31416b7a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tranzit Ride Operations Dashboard and SQL Data Analysis]]></title>
            <link>https://medium.com/@ezeliorafavour/tranzit-ride-operations-dashboard-and-sql-data-analysis-ceed07d3a83a?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/ceed07d3a83a</guid>
            <category><![CDATA[power-bi]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[transport]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Tue, 04 Nov 2025 20:13:50 GMT</pubDate>
            <atom:updated>2025-11-04T20:25:44.392Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/733/1*6KMXodwsBjCGwOcNnMeoaQ.png" /><figcaption>I created this using Canva</figcaption></figure><h4>Overview</h4><p>This SQL task builds upon my <a href="https://github.com/favy-codez/Tranzit-Ride-Operations-Dashboard"><strong>Tranzit-Ride Operations Dashboard</strong> </a>project. It focuses on exploring and analyzing data from the <strong>drivers</strong>, <strong>trips</strong>, and <strong>riders</strong> tables using SQL. The goal was to uncover meaningful insights about ride activity patterns, driver performance, payment behavior, and overall operational efficiency.</p><p>Through a series of structured SQL queries, I identified trends that support data-driven decision-making and provide a deeper understanding of how the platform operates day to day.</p><h4>Project Objective</h4><p>This project aims to:</p><ul><li>Analyze trip operations and completion trends</li><li>Evaluate driver performance based on ratings, vehicle conditions, and distance covered</li><li>Examine payment patterns and rider behavior</li><li>Provide a visual and analytical overview through a dashboard and SQL queries</li></ul><h4>Tools Used</h4><ul><li><strong>SQL Server</strong> — for querying and aggregating trip, driver, and rider data</li><li><strong>Power BI — </strong>for dashboard visualization</li><li><strong>Power Query - for</strong> data cleaning and preparation</li><li><strong>GitHub</strong> — for version control and project documentation</li></ul><h4>Dataset Description</h4><p>The project uses three main tables:</p><ul><li><strong>trz.trips</strong> → contains trip details such as request time, distance, status, fare, payment method, and locations.</li><li><strong>trz.drivers</strong> → includes driver information, ratings, vehicle details, and home zones.</li><li><strong>trz.riders</strong> → contains rider details, tiers, and preferred payment methods.</li></ul><p>📊 <strong>View the Dashboard </strong><a href="https://app.powerbi.com/view?r=eyJrIjoiZTllYjExMzctNDQ1NC00ZDdhLWI1MjEtMWNiNTczMTNlNDZjIiwidCI6IjE5NmRlODRmLTZhNDctNGYxOC04Y2E2LTAzMGU3ODA3ZDY4MiJ9"><strong>Here</strong></a></p><h4>SQL Analysis</h4><p>To complement the dashboard visuals, I wrote SQL queries to perform deeper operational analysis.<br> Some key queries include:</p><ol><li><strong>Total distance covered by each driver</strong> — to identify top-performing drivers</li><li><strong>Drivers with perfect ratings and lower vehicle conditions</strong> — to find service quality insights</li><li><strong>Trip cancellations and payment method trends</strong> — to understand user preferences</li><li><strong>Classification of vehicles</strong> based on year and condition using CASE statements</li><li><strong>Combining trips, drivers, and riders tables</strong> to get a unified operational dataset</li></ol><p>📘 <strong>View the Full SQL Queries on GitHub </strong><a href="https://github.com/favy-codez/Tranzit-Ride-Operations-Dashboard-and-SQL-Data-Analysis"><strong>here</strong></a></p><h4>Key Insights</h4><ul><li>Most rides were completed successfully, as seen from the trip status queries showing a high number of “completed” rides compared to “cancelled.”</li><li>Card payments have become the most frequently used method, confirmed by the GROUP BY payment_method and ORDER BY queries showing card leading other payment types.</li><li>Drivers with higher vehicle condition scores had higher average ratings, as identified from queries comparing vehicle_condition_score and driver_rating.</li><li>A few drivers covered the most total distance (SUM(distance_km)), showing potential route specialization or high trip frequency among top performers.</li><li>Trips where drivers’ home areas matched their drop-off areas had higher completion rates, as observed from the join between the drivers and trips tables.</li><li>Vehicles with newer model years generally had better condition scores and driver ratings, confirmed by classification queries using CASE statements on vehicle_year and vehicle_condition_score.</li><li>The COUNT() and GROUP BY queries revealed that most drivers signed up in early months, showing strong onboarding at the start of the year.</li><li>Aggregation results from the riders table showed that the majority of riders belong to the <strong>Standard</strong> tier, with fewer upgrading to <strong>Gold</strong> or <strong>Platinum</strong>.</li><li>The platform’s revenue and commission structure, calculated through SUM(total_fare_naira), indicated stable earnings and efficiency across completed rides.</li><li>Overall, SQL analysis highlighted strong operational consistency, with room to optimize cancellations, weekend demand, and wallet payment adoption.</li></ul><h4>Documentation</h4><p>I also created a short write-up explaining the data cleaning process, KPIs, and dashboard insights.<br> 📄 <strong>Read the full dashboard documentation </strong><a href="https://medium.com/@ezeliorafavour/tranzit-ride-operations-analysis-report-1f0d5d43b087"><strong>here</strong></a></p><h4>Conclusion</h4><p>The Tranzit Ride Operations Dashboard and SQL Analysis together provide a clear operational view for ride-sharing platforms.<br>By combining <strong>SQL-based querying</strong> and <strong>data visualization</strong>, this project highlights how structured analysis supports better decision-making, improves driver management, and enhances rider experience.</p><h4>Resources</h4><ul><li>📘 <a href="https://github.com/favy-codez/Tranzit-Ride-Operations-Dashboard">Full SQL Queries on GitHub</a></li><li>📊 <a href="https://app.powerbi.com/view?r=eyJrIjoiZTllYjExMzctNDQ1NC00ZDdhLWI1MjEtMWNiNTczMTNlNDZjIiwidCI6IjE5NmRlODRmLTZhNDctNGYxOC04Y2E2LTAzMGU3ODA3ZDY4MiJ9">Interactive Dashboard</a></li><li>📄 <a href="https://medium.com/@ezeliorafavour/tranzit-ride-operations-analysis-report-1f0d5d43b087">Dashboard Documentation</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ceed07d3a83a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Analyzing Students’ Mental Health using SQL]]></title>
            <link>https://medium.com/@ezeliorafavour/analyzing-students-mental-health-using-sql-90122c3b3552?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/90122c3b3552</guid>
            <category><![CDATA[data-analysis]]></category>
            <category><![CDATA[posgresql]]></category>
            <category><![CDATA[sql]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Thu, 25 Sep 2025 09:48:06 GMT</pubDate>
            <atom:updated>2025-09-25T09:48:52.436Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/259/1*0siI3-Cc78imCDi6X2Uiig.png" /><figcaption>Image credit: <a href="https://www.google.com/imgres?q=mental%20health&amp;imgurl=https%3A%2F%2Fwww.ucheck.co.uk%2Fwp-content%2Fuploads%2Fmental-health-2313426_1280.png&amp;imgrefurl=https%3A%2F%2Fwww.ucheck.co.uk%2Fblogs%2Fpositive-mental-health-at-work%2F&amp;docid=j_unQvgY_KotCM&amp;tbnid=jpfINP22j5A1hM&amp;vet=12ahUKEwjrhr3YzfOPAxVKVUEAHbi2IqAQM3oECBoQAA..i&amp;w=1200&amp;h=900&amp;hcb=2&amp;ved=2ahUKEwjrhr3YzfOPAxVKVUEAHbi2IqAQM3oECBoQAA">ucheck</a> blog</figcaption></figure><h4>Introduction</h4><p>Does going to university in a different country affect your mental health? A Japanese international university surveyed its students in 2018 and published a study the following year that was approved by several ethical and regulatory boards.</p><p><strong>The study found that:</strong></p><ul><li>International students have a higher risk of mental health difficulties than the general population.</li><li>Social connectedness (belonging to a social group) and acculturative stress (stress associated with joining a new culture) are predictive of depression.</li></ul><p>This project is part of the Data Camp<strong> Career Track: </strong><a href="https://app.datacamp.com/learn/career-tracks/associate-data-analyst-in-sql"><strong>Associate Data Analyst in SQL</strong></a>. It applies PostgreSQL skills to analyze the dataset from the study and investigate whether the <strong>length of stay in Japan</strong> contributes to students’ mental health outcomes.</p><h4>Project Description</h4><p>Studying abroad can be both exciting and challenging. Cultural adjustment, social belonging, and academic pressure may impact students’ mental well-being.</p><h4>In this Data Camp-guided<strong> </strong>project, SQL is applied to:</h4><ul><li>Explore the dataset.</li><li>Perform exploratory data analysis (EDA).</li><li>Identify correlations between social connectedness, acculturative stress, and depression.</li><li>Examine the effect of length of stay on student well-being.</li></ul><p>This project contributes to building my analytical skills as part of the <a href="https://app.datacamp.com/learn/career-tracks/associate-data-analyst-in-sql"><strong>Associate Data Analyst in SQL track</strong></a>, reinforcing concepts of querying, filtering, grouping, and aggregating data to uncover insights.</p><h4>Dataset Description</h4><p>The dataset consists of student survey responses collected from international students.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/801/1*z6z6dUZvxTtwQh4VMZsJOw.png" /><figcaption>Dataset Description</figcaption></figure><h4>Project Instructions</h4><p><strong>Goal:</strong> Explore how length of stay (stay) impacts average mental health diagnostic scores of international students.</p><p><strong>Required Columns:</strong></p><ul><li>stay – length of stay in Japan.</li><li>count_int – number of international students per stay.</li><li>average_phq – average PHQ-9 score (todep).</li><li>average_scs – average SCS score (tosc).</li><li>average_as – average ASISS score (toas).</li></ul><h4>Data Preprocessing</h4><p>Since the project was completed on the Data Camp<strong> platform</strong>, the dataset was preloaded in a table named students. There was no need to import or clean raw data, instead, the preprocessing focused on <strong>exploring the dataset</strong> and writing SQL queries to analyze it.</p><h3>Step 1: Explore the Dataset</h3><p>To view the data and confirm the available columns:</p><pre>SELECT * <br>FROM students;</pre><p>This provided an overview of the table and confirmed the relevant columns.</p><h4>Step 2: SQL Query Approach</h4><p>The query was designed based on the project instructions:</p><ol><li><strong>Select the </strong><strong>stay column</strong> – to group results by length of stay.</li><li><strong>Count international students</strong> (COUNT(*) AS count_int) with WHERE inter_dom = &#39;Inter&#39;.</li><li><strong>Aggregate averages of diagnostic scores</strong>, rounded to two decimals: todep → average_phq (PHQ-9 test for depression), tosc → average_scs (SCS test for social connectedness), toas → average_as (ASISS test for acculturative stress)</li><li><strong>Group results by </strong><strong>stay</strong> – to calculate results per length of stay.</li><li><strong>Order results by </strong><strong>stay DESC</strong> – so the longest stays appear first.</li></ol><h4>Step 3: Final SQL Query</h4><pre>SELECT stay, <br>       COUNT(*) AS count_int,<br>       ROUND(AVG(todep), 2) AS average_phq, <br>       ROUND(AVG(tosc), 2) AS average_scs, <br>       ROUND(AVG(toas), 2) AS average_as<br>FROM students<br>WHERE inter_dom = &#39;Inter&#39;<br>GROUP BY stay<br>ORDER BY stay DESC;</pre><p>This query produced the required <strong>a table</strong>, showing how the length of stay impacts the average PHQ-9, SCS, and ASISS scores of international students.</p><h4>Result</h4><p>The query returned the following table, showing how the <strong>length of stay</strong> relates to the mental health outcomes of international students:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vKwSUF3Cr521sgnocFBErw.png" /><figcaption>Query Result</figcaption></figure><h4>Findings</h4><ul><li>Students with <strong>low social connectedness</strong> reported higher depression scores.</li><li><strong>Acculturative stress</strong> showed a strong positive correlation with depression.</li><li><strong>Length of stay</strong> influenced adaptation: longer stays were linked to lower depression scores.</li><li>Students who accessed <strong>support services</strong> generally had better mental health outcomes.</li></ul><h4>Recommendations</h4><ul><li>Encourage <strong>social integration programs</strong> for international students.</li><li>Provide <strong>stress management workshops</strong> to reduce acculturative stress.</li><li>Strengthen awareness and access to <strong>student support services</strong>.</li><li>Pay special attention to students in their <strong>first year abroad</strong>, as they face the highest adaptation challenges.</li></ul><h4>Conclusion</h4><p>This SQL project, completed as part of the <strong>DataCamp Career Track: Associate Data Analyst in SQL</strong>, reinforces the key role of data manipulation and analysis in uncovering meaningful insights.</p><p>The findings support the original study’s conclusions:</p><ul><li>Social connectedness and acculturative stress are strong predictors of depression.</li><li>Length of stay impacts adaptation and mental health outcomes.</li></ul><p>The project highlights how SQL can transform raw survey data into insights that inform student support policies and improve the academic experience of international students.</p><h4>References</h4><ul><li>Data Camp’s <strong>Associate Data Analyst in SQL</strong> track — <a href="https://app.datacamp.com/learn/career-tracks/associate-data-analyst-in-sql">Link</a></li><li>View project here— <a href="https://www.datacamp.com/datalab/w/d3d35d14-e836-4723-9ff1-6ac78465f62c/edit?emitCellOutputs=false&amp;reducedMenuBar=true&amp;showExploreMore=false&amp;showLeftNavigation=false&amp;showNavBar=false&amp;showPublicationButton=false&amp;showOnlyRelevantSampleIntegrationIds[]=89e17161-a224-4a8a-846b-0adc0fe7a4b1&amp;showOnlyRelevantSampleIntegrationIds[]=e0c96696-ae0a-46fb-b6f9-1a43eb428ecb&amp;showOnlyRelevantSampleIntegrationIds[]=b1fcb109-b4fe-4543-bc98-681df8c4dc6e&amp;showOnlyRelevantSampleIntegrationIds[]=fcf37a0e-f8bd-4c85-95a5-201d3eebea48&amp;showOnlyRelevantSampleIntegrationIds[]=db697c09-0402-4a02-b327-26018dc2ecce&amp;showOnlyRelevantSampleIntegrationIds[]=7569175e-98be-4c89-9873-c20f699a9cc7&amp;fetchUnlistedSampleIntegrationIds[]=7569175e-98be-4c89-9873-c20f699a9cc7#ab01f727-3ae9-491a-bf8b-6d507e7dbf15">Link</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=90122c3b3552" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tranzit-Ride Operations Analysis Report]]></title>
            <link>https://medium.com/@ezeliorafavour/tranzit-ride-operations-analysis-report-1f0d5d43b087?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/1f0d5d43b087</guid>
            <category><![CDATA[sql-server]]></category>
            <category><![CDATA[ridesharing]]></category>
            <category><![CDATA[power-bi]]></category>
            <category><![CDATA[data-analysis]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Thu, 18 Sep 2025 06:59:29 GMT</pubDate>
            <atom:updated>2025-09-18T07:02:54.970Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*-Q9A8LzMxCwQDnOig9Ej5Q.png" /><figcaption>I created this using Canva</figcaption></figure><h4>Introduction</h4><p>Tranzit-Ride is a fictional ride-sharing company created to illustrate how operational data can be leveraged to evaluate performance, monitor trends, and guide strategic decision-making. This report consolidates findings from the Tranzit-Ride operations dashboard, highlighting demand patterns, financial performance, driver and rider behaviours, and trip dynamics.</p><p>By integrating insights from multiple perspectives: trips, revenue, drivers, and riders, this report identifies opportunities for business growth, efficiency improvements, and customer satisfaction. The ultimate goal is to demonstrate how data-driven strategies can strengthen both profitability and long-term sustainability in ride-sharing operations.</p><h4>Background</h4><p>The ride-sharing industry thrives on scale, efficiency, and customer trust. Platforms like Tranzit-Ride must balance rider convenience, driver earnings, and platform profitability while managing challenges such as seasonal demand fluctuations, cancellations, uneven earnings distribution, and low digital payment adoption.</p><p>To address these challenges, Tranzit-Ride’s operations data was analysed to uncover trends and actionable insights. The analysis focuses on four main areas:</p><ol><li><strong>Trips &amp; Demand Patterns</strong> — trip volumes, cancellations, and peak periods.</li><li><strong>Revenue &amp; Payments</strong> — revenue sources, commission structure, and payment preferences.</li><li><strong>Driver Analysis</strong> — earnings distribution, fleet composition, and completion rates.</li><li><strong>Rider Analysis</strong> — rider segmentation, spending behaviour, and engagement levels.</li></ol><h4>Tools used</h4><ul><li><strong>SQL Server</strong> — used to query and extract raw data directly from the company’s operational database, enabling accurate analysis and reporting.</li><li><strong>Excel</strong> — supported data exploration, and data dictionary creation.</li><li><strong>Power BI</strong> — connected to SQL Server for modelling and interactive dashboard creation.</li><li><strong>Power BI Service</strong> → Enabled publishing and sharing dashboards with stakeholders, ensuring real-time access and collaborative insights across the organization.</li></ul><p>The process involved:</p><ul><li><strong>Data Extraction &amp; Cleaning</strong> — I connected to the <strong>SQL Server database</strong> where the Tranzit-Ride data was stored. Using SQL queries, I pulled out the main tables (Trips, Drivers, Riders, etc). I also used the queries to check the data quality and remove fields that were not useful. After that, I created a simple data dictionary in Excel to explain what each field meant for the business. I cleaned the dataset by removing duplicates, giving columns clear names, and making sure each column had the right data type for use in Power BI.</li><li><strong>Data Modelling</strong> — In Power BI, I built a <strong>Star Schema model</strong> to organize the data. <strong>Fact Table:</strong> trz trips — trip details like revenue, payment method, driver, rider, and status. <strong>Dimension Tables: </strong>Date — to track time-based insights like daily, weekly, and seasonal patterns. trz drivers — information on drivers such as vehicle type, ratings, and earnings. trz riders — information on riders such as tier, spending, and trip frequency. These tables were connected using Trip ID, Driver ID, and Rider ID, making it easy to analyse across different categories.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*hZFkxzBrnK0mT6ynYpv-eA.png" /><figcaption>Data model for Tranzit-Rides</figcaption></figure><ul><li><strong>DAX Measures</strong> — I created a table for my custom DAX measures _Measuresin Power BI to drive key metrics, such as Total revenue, average revenue per trip, Average trips per rider, Driver Earnings, Commission %, Cancellation Rate, and Wallet Adoption %. These measures supported deeper insights and comparisons across time, segments, and performance metrics.</li><li><strong>Visualization and Deployment: </strong>Designed the <strong>mockup dashboards</strong> in Excel to outline KPIs and visuals before implementation.</li><li>Built <strong>interactive dashboards</strong> in Power BI with slicers for month, day, payment method, and rider tier.</li><li>Published the final reports to <strong>Power BI Service</strong>, enabling access to stakeholders for real-time performance monitoring.</li></ul><h4>Dashboards</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-NRiE8fAfEd9o6DjxiTYlw.png" /><figcaption>Summary of trips, drivers, riders, revenue, and fares.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J-q5OcXwqo9nsjFwL4sQHg.png" /><figcaption>Rider behaviour, trip frequency, spend, and payment choices.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zysuFDzR20MvtepvUitKEw.png" /><figcaption>Driver activity, earnings, ratings, and vehicle types.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hfhcl7ZE6ebXoycvGXjLvg.png" /><figcaption>Trip volumes, completion, cancellations, payments, and locations.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bK_v58jUR0IHU-uVSnAsiw.png" /><figcaption>Revenue trends, commission, driver earnings, and payments.</figcaption></figure><p><strong>Access the interactive dashboard here</strong>: <a href="https://app.powerbi.com/view?r=eyJrIjoiZTllYjExMzctNDQ1NC00ZDdhLWI1MjEtMWNiNTczMTNlNDZjIiwidCI6IjE5NmRlODRmLTZhNDctNGYxOC04Y2E2LTAzMGU3ODA3ZDY4MiJ9">https://app.powerbi.com/view?r=eyJrIjoiZTllYjExMzctNDQ1NC00ZDdhLWI1MjEtMWNiNTczMTNlNDZjIiwidCI6IjE5NmRlODRmLTZhNDctNGYxOC04Y2E2LTAzMGU3ODA3ZDY4MiJ9</a></p><h4>Observations</h4><ul><li>120K total trips; 102K (85%) completed, 18K (15%) cancelled.</li><li>Demand peaked in August (12.4K trips) and dropped sharply in September (7K).</li><li>Highest demand on Tuesdays–Fridays (17K–19K trips); lowest on Sundays (13.8K).</li><li>Peak ride hours are 8 AM and 6 PM, aligned with commute demand.</li><li>Total revenue = ₦650.5M, with ₦121M commission (~18.5%).</li><li>Avg. revenue per trip = ₦5.42K; per rider ≈ ₦81.3K.</li><li>Revenue the highest on Fridays (₦103M), lowest on Sundays (₦71M).</li><li>Payments: 58% Card, 35% Cash, 7% Wallet.</li><li>3K active drivers, avg. 48 trips each.</li><li>Avg. driver earnings = ₦4.14K; total driver earnings = ₦497M.</li><li>Driver ratings ≈ 5, reflecting strong satisfaction.</li><li>A few drivers (D2824, D3828) earned &gt;₦0.5M each, showing concentration of earnings.</li><li>Fleet dominated by Sedans (77%), followed by Mini (12.5%) and SUV (10%).</li><li>Driver completion rate = 85–89%.</li><li>8K riders: 6.5K Standard, 1.2K Gold, 200 Platinum.</li><li>Average 15 trips per rider.</li><li>A few top riders spent ₦0.42M–₦0.45M each, contributing disproportionately to revenue.</li></ul><h4>Key Insights</h4><ul><li>Strong demand mid-year, followed by a post-summer decline.</li><li>Weekends underperform compared to weekdays.</li><li>Trip demand is commute-driven, concentrated at 8 AM and 6 PM.</li><li>Platform revenue is volume-driven; commission rates are stable.</li><li>September’s sharp revenue dip points to seasonal or operational inefficiencies.</li><li>Card adoption is strong, but wallet usage remains very low.</li><li>Driver earnings are uneven, concentrated among top performers.</li><li>Heavy reliance on Sedans limits service diversity.</li><li>Completion rates are good, but cancellations are still significant.</li><li>Most riders remain in Standard tier; very few upgrade to premium tiers.</li><li>A small group of riders accounts for a large share of revenue, signalling loyalty potential.</li><li>Average rider engagement (15 trips) is moderate, with room for growth.</li></ul><h4>Recommendations</h4><ul><li>Launch targeted promotions in low-demand months (Sep–Oct).</li><li>Introduce weekend incentives (discounts, loyalty points) to improve weekend performance.</li><li>Partner with corporate firms to secure consistent commuter bookings.</li><li>Investigate September’s earnings dip by reviewing trip volumes and cancellation causes.</li><li>Promote wallet adoption with bonuses, cashback, and loyalty credits.</li><li>Explore dynamic pricing strategies during peak demand months.</li><li>Analyse top drivers’ strategies and replicate them across the wider driver base.</li><li>Implement driver mentorship programs to reduce earnings inequality.</li><li>Offer bonuses to drivers with low cancellation rates.</li><li>Expand fleet diversity with more SUVs and Minis to serve premium and budget markets.</li><li>Run tier upgrade campaigns with perks for Gold and Platinum riders.</li><li>Develop loyalty programs for high-spend riders.</li><li>Launch personalized reactivation campaigns for inactive or low-frequency riders.</li><li>Incentivize digital payments with rewards for card and wallet usage.</li></ul><h4>Conclusion</h4><p>This project successfully consolidated and analysed operational data from Tranzit-Ride to uncover patterns in trips, revenue, driver activity, rider engagement, and payment behaviours. Through detailed observations, key insights, and targeted recommendations, the analysis provided a clear picture of both the strengths and challenges facing the platform.</p><p>The value of this analysis lies in its ability to transform raw operational data into actionable business strategies. By identifying seasonal demand fluctuations, underutilization of promotions, uneven revenue distribution among drivers, low wallet adoption, and the dominance of Standard-tier riders, the report highlights practical opportunities to increase efficiency, improve rider and driver satisfaction, and boost profitability.</p><p>The broader implications extend beyond Tranzit-Ride: this approach demonstrates how data analytics can guide decision-making in the ride-sharing industry. Findings such as the need for demand smoothing, fleet diversification, and loyalty programs for high-value riders are applicable to similar mobility platforms aiming to optimize growth and sustainability.</p><p>Looking ahead, this work can be extended by incorporating predictive modelling to forecast demand more accurately, segmenting riders and drivers for more personalized strategies, and tracking the impact of implemented recommendations over time. With continuous monitoring and refinement, the insights derived here can serve as a foundation for building a more resilient, customer-focused, and profitable ride-sharing platform.</p><h3>Let’s stay connected</h3><ul><li><a href="https://linktr.ee/ezelioragodsfavour">https://linktr.ee/ezelioragodsfavour</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f0d5d43b087" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solving the SQL Murder Mystery with PostgreSQL]]></title>
            <link>https://medium.com/@ezeliorafavour/solving-the-sql-murder-mystery-with-postgresql-93b3df0cc0b2?source=rss-383d2760ba79------2</link>
            <guid isPermaLink="false">https://medium.com/p/93b3df0cc0b2</guid>
            <category><![CDATA[murder-mystery]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[investigation]]></category>
            <dc:creator><![CDATA[Ezeliora Godsfavour]]></dc:creator>
            <pubDate>Sun, 14 Sep 2025 17:25:27 GMT</pubDate>
            <atom:updated>2025-09-14T17:25:27.722Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/343/0*8Cqp4AnWYRj8PuV0" /></figure><h3>Introduction</h3><p>A crime has taken place, and the detective needs your help. The detective gave you the crime scene report, but you somehow lost it. You vaguely remember that the crime was a <strong>murder</strong> that occurred sometime on <strong>January 15, 2018</strong>, and that it took place in <strong>SQL City</strong>.</p><p>Your task? Start by retrieving the corresponding crime scene report from the <strong>police department’s database</strong> and follow the trail of clues until the case is solved.</p><p>This SQL Murder Mystery was originally designed in SQLite, but for this project, I recreated the challenge using <strong>PostgreSQL</strong>. Documenting this journey not only showcases how SQL can be used to solve real-world-style problems but also demonstrates structured querying for investigation and insights.</p><h4>Importing the database</h4><p>I first created a new database in PostgreSQL called <strong>police_investigation</strong> to hold the case data:</p><pre>CREATE DATABASE police_investigation;</pre><p>After importing the dataset, I connected to it:</p><pre>\c police_investigation;</pre><h4>Exploring the Database</h4><p>To explore the available tables within the public schema, I ran the following query:</p><pre>SELECT table_name<br>FROM information_schema.tables<br>WHERE table_schema = &#39;public&#39;;</pre><h4>Result—key tables in police_investigation</h4><p><strong>Table Names</strong></p><p>crime_scene_report- Records of incidents and crimes</p><p>person- Personal details of people in SQL City</p><p>interview- Transcripts of interviews with suspects and witnesses</p><p>get_fit_now_member- Gym membership data</p><p>get_fit_now_check_in — Gym attendance/check-in logs</p><p>drivers_license — License details such as age, height, and plate information</p><p>income — Financial records</p><p>facebook_event_checkin — Event attendance records</p><p>These tables form the foundation of the investigation. With the schema in place, I could now begin querying the database to uncover the details of the murder case.</p><h4>Step 1: Crime Scene Report</h4><p>First, I retrieved the crime scene report for the murder.</p><pre>SELECT * <br>FROM crime_scene_report<br>WHERE Date = 20180115 <br>  AND City = &#39;SQL City&#39; <br>  AND crime_type = &#39;murder&#39;;</pre><p><strong>Result: </strong>The report mentioned <strong>two witnesses</strong>:</p><ul><li>One lived at the <strong>last house on Northwestern Dr</strong>.</li><li>The other was <strong>Annabel</strong>, living on <strong>Franklin Ave</strong>.</li></ul><h4>Step 2: Identifying the Witnesses</h4><pre>-- Witness 1: last house on Northwestern Dr<br>SELECT *<br>FROM Person<br>WHERE address_street_name = &#39;Northwestern Dr&#39;<br>ORDER BY address_number DESC;</pre><pre>-- Witness 2: Annabel on Franklin Ave<br>SELECT *<br>FROM Person<br>WHERE name LIKE &#39;Annabel%&#39;<br>  AND address_street_name = &#39;Franklin Ave&#39;;</pre><p><strong>Result</strong>:</p><ul><li>Witness 1 → <strong>Morty Schapiro</strong> (ID: 14887)</li><li>Witness 2 → <strong>Annabel Miller</strong> (ID: 16371)</li></ul><h4>Step 3: Interviewing Witnesses</h4><pre>SELECT *<br>FROM interview<br>WHERE person_id IN (14887, 16371);</pre><p><strong>Clues</strong>:</p><ul><li>Morty: Saw a man with a <strong>Get Fit Now Gym gold bag</strong> (membership starts with <em>48Z</em>) and a car with <strong>H42W</strong> in the plate.</li><li>Annabel: Saw the same man at the <strong>gym on Jan 9, 2018</strong>.</li></ul><h4>Step 4: Finding Suspects at the Gym</h4><pre>SELECT *<br>FROM get_fit_now_member<br>WHERE id LIKE &#39;%48Z%&#39;<br>  AND membership_status = &#39;gold&#39;;</pre><p><strong>Result</strong>: Two suspects.</p><ul><li><strong>Joe Germuska</strong> → ID: 48Z7A, person_id: 28819</li><li><strong>Jeremy Bowers</strong> → ID: 48Z55, person_id: 67318</li></ul><h4>Step 5: Narrowing with Gym Check-ins</h4><pre>SELECT *<br>FROM get_fit_now_check_in<br>WHERE check_in_date = 20180109<br>  AND membership_id IN (&#39;48Z7A&#39;, &#39;48Z55&#39;);</pre><p><strong>Result</strong>: Both suspects checked in on Jan 9, so more clues were needed.</p><h4>Step 6: Matching license plates</h4><pre>SELECT p.name, dl.age, dl.height, dl.hair_color, dl.gender,<br>       dl.plate_number, dl.car_make, dl.car_model<br>FROM drivers_license dl<br>LEFT JOIN person p<br>  ON dl.id = p.license_id<br>WHERE plate_number LIKE &#39;%H42W%&#39;;</pre><p><strong>Result</strong>: <strong>Jeremy Bowers</strong> matched the witness description.</p><p>✅ Murderer identified: <strong>Jeremy Bowers</strong>.</p><h4>Step 7: The real mastermind</h4><p>Finally, I interrogated Jeremy.</p><pre>SELECT *<br>FROM interview<br>WHERE person_id = 67318;</pre><p><strong>Jeremy confessed</strong>:</p><ul><li>He was hired by a wealthy <strong>woman</strong></li><li>Height: <strong>65&quot;–67&quot;</strong></li><li><strong>Red hair</strong>, drives a <strong>Tesla Model S</strong></li><li>Attended the <strong>SQL Symphony Concert 3 times in Dec 2017</strong></li></ul><p>This trail led to the true mastermind:<br> 🎯 <strong>Miranda Priestly</strong>.</p><h4>Step 8: Identifying the Mastermind</h4><pre>SELECT dl.*, p.*<br>FROM drivers_license dl<br>LEFT JOIN person p ON dl.id = p.license_id<br>WHERE height BETWEEN 65 AND 67<br>  AND hair_color = &#39;red&#39;<br>  AND gender = &#39;female&#39;<br>  AND car_make = &#39;Tesla&#39;<br>  AND car_model = &#39;Model S&#39;;</pre><p>Suspects: <strong>Red Korb, Regina George, Miranda Priestly</strong></p><pre>SELECT *<br>FROM facebook_event_checkin<br>WHERE event_name = &#39;SQL Symphony Concert&#39;<br>  AND person_id IN (&#39;78881&#39;,&#39;90700&#39;,&#39;99716&#39;);</pre><p>✅ Only <strong>Miranda Priestly</strong> attended the concert 3 times.</p><pre>SELECT i.*, p.name<br>FROM income i<br>JOIN person p ON i.ssn = p.ssn<br>WHERE id = 99716;</pre><p>Annual income → <strong>$310,000</strong> 💰</p><p>🎯 <strong>Mastermind confirmed: Miranda Priestly</strong></p><h4>Insights from the Case</h4><ul><li>Witnesses were <strong>Morty Schapiro</strong> and <strong>Annabel Miller</strong>.</li><li>The murderer was identified as <strong>Jeremy Bowers</strong>.</li><li>Jeremy confessed he was hired by a wealthy woman.</li><li>Using license, car, and event data, the mastermind was revealed: <strong>Miranda Priestly</strong>.</li><li>Miranda Priestly’s income records confirmed her profile as the one who financed the crime.</li></ul><h4>What I learned from this project</h4><ul><li>Practiced <strong>multi-table JOINs</strong> to combine information from different sources.</li><li>Strengthened filtering skills using <strong>WHERE, LIKE, and ranges</strong>.</li><li>Applied <strong>stepwise logic</strong> to gradually narrow down suspects.</li><li>Learned the importance of <strong>cross-checking data</strong> (e.g., gym check-ins, car plates, and event attendance).</li><li>Experienced how SQL can be used for <strong>storytelling and real-world investigations</strong>.</li></ul><h4>Conclusion</h4><p>The SQL Murder Mystery turned out to be more than just a coding puzzle — it was a perfect blend of storytelling and technical problem-solving. By moving through each clue step by step, I was able to apply <strong>SQL concepts in a practical, engaging way</strong>: filtering, joining, and analysing across multiple tables to uncover the truth.</p><p>This challenge reinforced the importance of <strong>structured problem-solving in data analysis</strong>: start broad, narrow down with filters, and validate with multiple data points until you reach a solid conclusion.</p><p>In the end, what began as a lost crime scene report unfolded into a full investigation that revealed not just the murderer, but the true mastermind behind the crime.</p><p>SQL isn’t just about databases, it’s about uncovering insights, solving problems, and sometimes, even catching criminals.</p><p>👉 Try the challenge here: <a href="https://mystery.knightlab.com/">SQL Murder Mystery</a></p><p>👉 Link to solution on GitHub — <a href="https://github.com/favy-codez/SQL-Murder-Mystery-with-PostgreSQL">https://github.com/favy-codez/SQL-Murder-Mystery-with-PostgreSQL</a></p><p>Case closed. 🕵️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=93b3df0cc0b2" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>