<?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: Nelly Mogere</title>
    <description>The latest articles on DEV Community by Nelly Mogere (@nelly_mogere_194bac0cb2ba).</description>
    <link>https://dev.to/nelly_mogere_194bac0cb2ba</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%2F3854620%2F9d52e1e8-9689-4f72-aafe-0f9888dda120.jpeg</url>
      <title>DEV Community: Nelly Mogere</title>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nelly_mogere_194bac0cb2ba"/>
    <language>en</language>
    <item>
      <title>Need to calculate running totals or rank rows without GROUP BY headaches? Enter SQL Window Functions. Let’s break down how they work with practical, real-world examples.</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Thu, 13 Aug 2026 09:34:28 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/need-to-calculate-running-totals-or-rank-rows-without-group-by-headaches-enter-sql-window-4kck</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/need-to-calculate-running-totals-or-rank-rows-without-group-by-headaches-enter-sql-window-4kck</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo" class="crayons-story__hidden-navigation-link"&gt;SQL Window Functions: Rank, Compare, and Analyze Without Losing Rows&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/nelly_mogere_194bac0cb2ba" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854620%2F9d52e1e8-9689-4f72-aafe-0f9888dda120.jpeg" alt="nelly_mogere_194bac0cb2ba profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/nelly_mogere_194bac0cb2ba" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Nelly Mogere
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Nelly Mogere
                
                
              
              &lt;div id="story-author-preview-content-4386451" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/nelly_mogere_194bac0cb2ba" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3854620%2F9d52e1e8-9689-4f72-aafe-0f9888dda120.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Nelly Mogere&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo" id="article-link-4386451"&gt;
          SQL Window Functions: Rank, Compare, and Analyze Without Losing Rows
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;6&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>backend</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>SQL Window Functions: Rank, Compare, and Analyze Without Losing Rows</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Thu, 13 Aug 2026 09:20:17 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/sql-window-functions-rank-compare-and-analyze-without-losing-rows-4olo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A practical PostgreSQL tutorial using a safari trips dataset.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;em&gt;GROUP BY&lt;/em&gt; query can tell us how much a rider spent. But the moment we group the data, the individual trips disappear. What if we need both on the same row?&lt;/p&gt;

&lt;p&gt;This is the problem &lt;strong&gt;window functions&lt;/strong&gt; solve. A window function performs a calculation across rows related to the current row, but unlike a regular aggregate, it does not group those rows into one output row. Each row keeps its identity while gaining information from the rows around it.&lt;/p&gt;

&lt;p&gt;Before we start writing project queries, let us first build a clear picture of the window-function categories, the syntax they share, and the question each category answers. Once that foundation is in place, the safari trips examples will make much more sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  The project dataset
&lt;/h2&gt;

&lt;p&gt;The project uses PostgreSQL and a &lt;code&gt;safari&lt;/code&gt; schema with three tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;drivers&lt;/code&gt; stores driver names, car models, join dates, and status.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;riders&lt;/code&gt; stores rider names, cities, and membership tiers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trips&lt;/code&gt; stores trip dates, routes, distances, fares, ratings, and payment methods.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The supplied dataset contains 10 drivers, 12 riders, and 40 trips. We will use these rows to rank drivers, place totals beside trips, compare fares, calculate a rolling average, and divide riders into spend groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what makes a function a window function?
&lt;/h2&gt;

&lt;p&gt;Consider a normal aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spend&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns one row per rider. That is useful when we only need the summary, but the individual trips are no longer part of the result.&lt;/p&gt;

&lt;p&gt;Now add an &lt;code&gt;OVER&lt;/code&gt; clause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rider_total_spend&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The calculation still uses &lt;code&gt;SUM(fare)&lt;/code&gt;, but &lt;code&gt;OVER&lt;/code&gt; turns it into a window calculation. Instead of collapsing the trips, SQL returns every trip and places the rider's total beside it.&lt;/p&gt;

&lt;p&gt;That difference is the foundation for everything that follows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anatomy of a window expression
&lt;/h2&gt;

&lt;p&gt;A window expression generally follows this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;window_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;grouping_column&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ordering_column&lt;/span&gt;
    &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;frame_start&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;frame_end&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every function needs every clause. Each part answers a separate question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Window function&lt;/td&gt;
&lt;td&gt;What calculation should SQL perform?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OVER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Should this calculation operate as a window function?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PARTITION BY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which rows belong in the same group for this calculation?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Window &lt;code&gt;ORDER BY&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;In what sequence should SQL evaluate the rows?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Window frame&lt;/td&gt;
&lt;td&gt;Which rows around the current row should the calculation include?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;OVER&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;OVER&lt;/code&gt; marks the calculation as a window function. Without it, &lt;code&gt;SUM()&lt;/code&gt;, &lt;code&gt;COUNT()&lt;/code&gt;, and &lt;code&gt;AVG()&lt;/code&gt; are regular aggregate functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;PARTITION BY&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PARTITION BY&lt;/code&gt; divides the result into independent windows. A calculation partitioned by &lt;code&gt;rider_id&lt;/code&gt; starts again for each rider. A ranking partitioned by &lt;code&gt;city&lt;/code&gt; starts again for each city.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;PARTITION BY&lt;/code&gt; is omitted, the entire result is treated as one window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Window &lt;code&gt;ORDER BY&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ORDER BY&lt;/code&gt; inside &lt;code&gt;OVER&lt;/code&gt; defines the sequence used by the calculation. Ranking functions need it to determine position. &lt;code&gt;LAG()&lt;/code&gt; and &lt;code&gt;LEAD()&lt;/code&gt; need it to know which row is previous or next. An aggregate can use it to build a running calculation.&lt;/p&gt;

&lt;p&gt;This is different from the final &lt;code&gt;ORDER BY&lt;/code&gt;, which only controls how the returned rows are displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The window frame
&lt;/h3&gt;

&lt;p&gt;A frame narrows an ordered window to rows around the current row. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This frame includes the current row and the two rows before it. In our project, it creates a three-trip rolling average.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main categories of window functions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsohudj89o98t5vpvt7pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsohudj89o98t5vpvt7pi.png" alt=" " width="665" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Aggregate window functions
&lt;/h2&gt;

&lt;p&gt;Regular aggregate functions such as &lt;code&gt;SUM()&lt;/code&gt;, &lt;code&gt;COUNT()&lt;/code&gt;, and &lt;code&gt;AVG()&lt;/code&gt; can also be used as window functions.&lt;/p&gt;

&lt;p&gt;Without a window &lt;code&gt;ORDER BY&lt;/code&gt;, an aggregate returns the value for the full partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every trip for the same rider receives that rider's total spend.&lt;/p&gt;

&lt;p&gt;When an &lt;code&gt;ORDER BY&lt;/code&gt; is added, the calculation follows a sequence. A frame can then control whether the function uses all earlier rows or only a selected number of nearby rows.&lt;/p&gt;

&lt;p&gt;Aggregate window functions answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the rider's total spend beside each trip?&lt;/li&gt;
&lt;li&gt;What is the average fare across a driver's trips?&lt;/li&gt;
&lt;li&gt;What is the rolling average of the current fare and the previous two fares?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Ranking window functions
&lt;/h2&gt;

&lt;p&gt;Ranking functions assign positions to ordered rows. The project uses four of them:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROW_NUMBER()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gives every row a unique sequential number.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RANK()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gives ties the same rank and leaves gaps after the tied rows.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DENSE_RANK()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gives ties the same rank without leaving gaps.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NTILE(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Divides ordered rows into &lt;code&gt;n&lt;/code&gt; approximately equal buckets.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, if three rows tie at position 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ROW_NUMBER()&lt;/code&gt; still gives them different numbers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RANK()&lt;/code&gt; gives all three rank 3, then the next row jumps to rank 6.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DENSE_RANK()&lt;/code&gt; gives all three rank 3, then the next row receives rank &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fycljwxknsklcmxkv34cb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fycljwxknsklcmxkv34cb.png" alt=" " width="322" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function you choose depends on the question. &lt;code&gt;ROW_NUMBER()&lt;/code&gt; is useful when exactly one row must be selected from each group. &lt;code&gt;RANK()&lt;/code&gt; works for leaderboards where gaps after ties are meaningful. &lt;code&gt;DENSE_RANK()&lt;/code&gt; keeps tied positions fair without creating gaps. &lt;code&gt;NTILE()&lt;/code&gt; creates ordered groups such as quartiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Offset or row-comparison functions
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;LAG()&lt;/code&gt; and &lt;code&gt;LEAD()&lt;/code&gt; pull a value from another row in the ordered window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LAG()&lt;/code&gt; looks backward to a previous row.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LEAD()&lt;/code&gt; looks forward to a following row.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What was the previous trip's fare?&lt;/li&gt;
&lt;li&gt;How much did the fare change from the previous trip?&lt;/li&gt;
&lt;li&gt;What value comes next in the ordered sequence?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first row returned by &lt;code&gt;LAG()&lt;/code&gt; has no previous row, so the result is &lt;code&gt;NULL&lt;/code&gt;. In the same way, &lt;code&gt;LEAD()&lt;/code&gt; returns &lt;code&gt;NULL&lt;/code&gt; at the end when no following row exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Framed window calculations
&lt;/h2&gt;

&lt;p&gt;A frame is not a separate function, but it creates an important branch of window analysis. It defines how much of an ordered partition the current calculation can see.&lt;/p&gt;

&lt;p&gt;The project uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combined with &lt;code&gt;AVG(fare)&lt;/code&gt;, this calculates the average of the current trip and the previous two trips. The frame grows naturally at the start because fewer than three rows are available, then moves forward one row at a time.&lt;/p&gt;

&lt;p&gt;With the categories clear, we can now apply them to the safari dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 1: Compare the ranking functions
&lt;/h2&gt;

&lt;p&gt;We will begin with the trips for driver 2 because repeated rider ratings make the effect of ties visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rider_rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_rating&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;row_number_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_rating&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rank_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DENSE_RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_rating&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dense_rank_value&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_rating&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three functions use the same ordered rows. The only difference is how they assign positions when ratings tie. This makes the query a useful reference before choosing a ranking function for a real task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 2: Rank drivers by total revenue
&lt;/h2&gt;

&lt;p&gt;A window function works on the rows produced by its query. Because the ranking needs one revenue row per driver, we first calculate driver revenue in a CTE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;driver_revenue&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;driver_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_revenue&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;revenue_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;driver_revenue&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drivers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;revenue_rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query has two clear stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Group the trips to produce one total-revenue row per driver.&lt;/li&gt;
&lt;li&gt;Rank those driver rows from highest to lowest revenue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no &lt;code&gt;PARTITION BY&lt;/code&gt; because we want one leaderboard across all drivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 3: Keep every trip and add the rider's total spend
&lt;/h2&gt;

&lt;p&gt;Now we return to the problem from the introduction. We need each trip's fare and the rider's total spend on the same row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rider_total_spend&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PARTITION BY rider_id&lt;/code&gt; places trips from the same rider in the same calculation window. There is no window &lt;code&gt;ORDER BY&lt;/code&gt; because we want the total for the full rider partition, not a running total.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WHERE&lt;/code&gt; clause limits the displayed example to rider 2. The window result still demonstrates the key benefit: the trip rows remain separate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 4: Find each driver's highest-fare trip
&lt;/h2&gt;

&lt;p&gt;This task needs exactly one trip for every driver. &lt;code&gt;ROW_NUMBER()&lt;/code&gt; is a good fit because it gives each trip a unique position inside its driver partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;ranked_trips&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;driver_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;driver_id&lt;/span&gt;
            &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;fare&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trip_id&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;fare_position&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ranked_trips&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drivers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare_position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PARTITION BY driver_id&lt;/code&gt; restarts the numbering for each driver. Ordering by &lt;code&gt;fare DESC&lt;/code&gt; places the highest fare first. The extra date and trip ID columns provide a consistent order if fares tie.&lt;/p&gt;

&lt;p&gt;The window value is calculated in the CTE and filtered in the outer query. That separation is important because the result of a window function is not filtered in the same query level where it is calculated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 5: Rank riders inside their city
&lt;/h2&gt;

&lt;p&gt;So far, the revenue leaderboard used one window across all drivers. This query shows how &lt;code&gt;PARTITION BY&lt;/code&gt; changes the comparison group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;rider_total_spend&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spend&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spend&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;city_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rider_total_spend&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rts&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;riders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city_rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CTE first calculates one total-spend row for every rider who has trips. The outer query then ranks those riders, but the ranking restarts whenever the city changes.&lt;/p&gt;

&lt;p&gt;This is the same ranking pattern as the driver leaderboard. The difference is the partition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 6: Compare each fare with the previous trip
&lt;/h2&gt;

&lt;p&gt;Ranking functions care about position. Offset functions use that position to retrieve another row's value.&lt;/p&gt;

&lt;p&gt;For driver 8, we order trips by date and use &lt;code&gt;LAG()&lt;/code&gt; to retrieve the previous fare:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;previous_fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;fare_change&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drivers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first trip has no earlier row, so &lt;code&gt;previous_fare&lt;/code&gt; and &lt;code&gt;fare_change&lt;/code&gt; are &lt;code&gt;NULL&lt;/code&gt;. Every later row can subtract the previous fare from the current fare.&lt;/p&gt;

&lt;p&gt;The window &lt;code&gt;ORDER BY&lt;/code&gt; defines what “previous” means. Without a meaningful sequence, a previous-row comparison would not answer a clear question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 7: Calculate a three-trip rolling average
&lt;/h2&gt;

&lt;p&gt;The next query combines an aggregate window function with an ordered frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
            &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;
            &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rolling_average_3&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drivers&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trip_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9nkxg79e9lgnn44f3bjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9nkxg79e9lgnn44f3bjl.png" alt=" " width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The partition keeps the calculation inside the selected driver's trip history. The window order arranges the trips. The frame then tells &lt;code&gt;AVG()&lt;/code&gt; to use only the current trip and the previous two trips.&lt;/p&gt;

&lt;p&gt;This is different from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;driver_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That version calculates one average over the driver's full partition. The frame turns it into a moving calculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query 8: Divide riders into spend quartiles
&lt;/h2&gt;

&lt;p&gt;Finally, &lt;code&gt;NTILE(4)&lt;/code&gt; assigns riders to four ordered spend buckets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;rider_spend&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="n"&gt;rider_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fare&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spend&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trips&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rider_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NTILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spend&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;spend_quartile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;rider_spend&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;safari&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;riders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;spend_quartile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spend&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rider_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CTE produces one total-spend row per rider who has taken a trip. &lt;code&gt;NTILE(4)&lt;/code&gt; then divides those ordered rows into four approximately equal groups, with the highest spenders first.&lt;/p&gt;

&lt;p&gt;Because this is a small dataset, the buckets should not be interpreted as exact statistical percentiles. They are ordered groups based on the available rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical way to choose a window function
&lt;/h2&gt;

&lt;p&gt;When a question looks like a window-function problem, I work through it in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define the output row.&lt;/strong&gt; Should one row represent a trip, a rider, or a driver?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify any grouped step.&lt;/strong&gt; If the value being ranked is a total, calculate that total first in a CTE.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the category.&lt;/strong&gt; Do you need an aggregate, ranking, offset, or bucket calculation?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the partition.&lt;/strong&gt; Should the calculation restart for every rider, driver, or city?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the order.&lt;/strong&gt; What determines rank, previous row, or movement through the data?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the frame if needed.&lt;/strong&gt; Should the function see the full partition or only nearby rows?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter in an outer query.&lt;/strong&gt; If you need the top row from each partition, calculate the window value first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort the final output.&lt;/strong&gt; Add a final &lt;code&gt;ORDER BY&lt;/code&gt; so the result is easy to inspect.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach keeps the SQL tied to the question. Instead of starting with a function name, start with the output grain, comparison group, and sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Window functions are easier to understand when they are treated as a family of tools rather than one feature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregate window functions add totals, counts, and averages without collapsing rows.&lt;/li&gt;
&lt;li&gt;Ranking functions assign positions and handle ties in different ways.&lt;/li&gt;
&lt;li&gt;Offset functions compare the current row with earlier or later rows.&lt;/li&gt;
&lt;li&gt;Frames create running and rolling calculations inside an ordered partition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the safari project, those branches work together to preserve trip details, rank drivers and riders, compare fares, calculate rolling averages, and build spend quartiles.&lt;/p&gt;

&lt;p&gt;The next time &lt;code&gt;GROUP BY&lt;/code&gt; gives you the right calculation but removes the detail you still need, look at the &lt;code&gt;OVER&lt;/code&gt; clause. The calculation may belong beside the original rows rather than in a collapsed summary.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Write Clearer SQL: When to Use CTEs or Subqueries</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:41:40 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/write-clearer-sql-when-to-use-ctes-or-subqueries-1cdl</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/write-clearer-sql-when-to-use-ctes-or-subqueries-1cdl</guid>
      <description>&lt;p&gt;&lt;strong&gt;CTEs&lt;/strong&gt; and &lt;strong&gt;subqueries&lt;/strong&gt; can often produce the same result. The important difference is not what they return, but how clearly they communicate the steps of your query.&lt;/p&gt;

&lt;p&gt;That distinction matters when a query grows beyond one filter or calculation. A compact subquery can keep simple logic close to where it is used. A Common Table Expression (CTE) can give each stage of a longer query a name, making the whole statement easier to read, test, and maintain.&lt;/p&gt;

&lt;p&gt;This article uses PostgreSQL-style syntax, but the main ideas apply to most relational databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the mental model
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;subquery&lt;/strong&gt; is a query placed inside another SQL statement. It is wrapped in parentheses, and its result is used by the outer query.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;CTE&lt;/strong&gt; is a named result set defined at the beginning of a statement with &lt;code&gt;WITH&lt;/code&gt;. It exists only while that statement runs; it is not a permanent table.&lt;/p&gt;

&lt;p&gt;Think of the difference this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A subquery is an expression inside a larger sentence.&lt;/li&gt;
&lt;li&gt;A CTE is a named paragraph that the final query can reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither is automatically better. The surrounding problem determines which structure communicates the logic more clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The main types of CTEs
&lt;/h2&gt;

&lt;p&gt;CTEs are usually divided into two main types based on how they behave:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-recursive CTE:&lt;/strong&gt; Runs its query once and returns an intermediate result to the statement that follows. Use it to organize filters, joins, aggregations, or multi-step calculations. Most CTEs you write will be non-recursive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recursive CTE:&lt;/strong&gt; Includes a recursive member that references the CTE itself. It repeatedly adds related rows until no more rows match, making it suitable for employee hierarchies, category trees, folder structures, and similar data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qqjh6so8ngtjsxgkn9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qqjh6so8ngtjsxgkn9e.png" alt=" " width="554" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may also hear the terms &lt;strong&gt;standalone CTE&lt;/strong&gt; and &lt;strong&gt;nested CTE&lt;/strong&gt;. These describe structure rather than separate behavioral types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;standalone CTE&lt;/strong&gt; is defined once and used directly by the main &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, or &lt;code&gt;DELETE&lt;/code&gt; statement.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;nested or chained CTE&lt;/strong&gt; is commonly used to describe multiple CTEs in one &lt;code&gt;WITH&lt;/code&gt; clause where a later CTE reads from an earlier one. This creates a sequence of named steps. It is not the same as placing one &lt;code&gt;WITH&lt;/code&gt; clause inside another, and some databases restrict where nested &lt;code&gt;WITH&lt;/code&gt; clauses may appear.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first CTE example later in this article is standalone and non-recursive. The multi-stage salary example is chained and non-recursive, while the organization example is recursive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 1: A standalone, non-recursive CTE
&lt;/h2&gt;

&lt;p&gt;When a calculation feeds several parts of a query, a CTE makes the stages visible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Step 1: create one average-salary row per department.&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;department_averages&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;average_salary&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- Step 2: compare each employee with the named intermediate result.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;average_salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;department_averages&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;average_salary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it from top to bottom:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate the average salary for every department.&lt;/li&gt;
&lt;li&gt;Join those averages to the employees.&lt;/li&gt;
&lt;li&gt;Keep employees whose salary is above their department's average.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CTE does not merely shorten the query. Its name, &lt;code&gt;department_averages&lt;/code&gt;, explains what the intermediate result means. That becomes more valuable when you add more aggregations, joins, or business rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Chained non-recursive CTEs
&lt;/h3&gt;

&lt;p&gt;You can also define multiple CTEs in one &lt;code&gt;WITH&lt;/code&gt; clause. Each CTE should represent one understandable step, and later CTEs can build on earlier ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Step 1: summarize salary data by department.&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;department_totals&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_salary&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="c1"&gt;-- Step 2: calculate a benchmark from the summarized rows.&lt;/span&gt;
&lt;span class="n"&gt;average_department_total&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;average_total&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;department_totals&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- Step 3: return departments above that benchmark.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;department_totals&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;average_department_total&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;average_total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second CTE consumes the result of the first, and the final query gives the complete calculation a clear direction. Still, avoid splitting a query into tiny pieces just to use more CTEs; names help only when they clarify meaningful stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example 3: A recursive CTE
&lt;/h2&gt;

&lt;p&gt;A recursive CTE can reference itself. This makes it useful for hierarchical data such as employee-manager relationships, categories, and folder trees.&lt;/p&gt;

&lt;p&gt;Assume &lt;code&gt;manager_id&lt;/code&gt; points to another row in &lt;code&gt;employees&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; &lt;span class="n"&gt;organization&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Anchor member: begin with employees who have no manager.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;

    &lt;span class="k"&gt;UNION&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;

    &lt;span class="c1"&gt;-- Recursive member: find the next level in the hierarchy.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;organization&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
        &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;-- Read the completed hierarchy after recursion stops.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;organization&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first query is the &lt;strong&gt;anchor member&lt;/strong&gt;: it selects the top-level manager. The second is the &lt;strong&gt;recursive member&lt;/strong&gt;: it finds employees managed by rows already discovered. Recursion continues until no new rows match.&lt;/p&gt;

&lt;p&gt;PostgreSQL uses &lt;code&gt;WITH RECURSIVE&lt;/code&gt;. SQL Server uses &lt;code&gt;WITH&lt;/code&gt; and supports the &lt;code&gt;MAXRECURSION&lt;/code&gt; query hint to limit recursive iterations. Because recursion controls differ by database, check your database documentation and ensure the recursive member always moves toward a stopping condition.&lt;/p&gt;

&lt;p&gt;The CTE examples show how named query steps work in practice. Once those patterns are clear, we can compare them with subqueries and see when a smaller, local expression is the better choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now compare subqueries
&lt;/h2&gt;

&lt;p&gt;Suppose an &lt;code&gt;employees&lt;/code&gt; table contains &lt;code&gt;employee_id&lt;/code&gt;, &lt;code&gt;employee_name&lt;/code&gt;, &lt;code&gt;department&lt;/code&gt;, and &lt;code&gt;salary&lt;/code&gt;. To find employees who earn more than the company-wide average, a scalar subquery is a natural fit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- The outer query returns the employees we want to inspect.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- This scalar subquery returns one value: the company average.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inner query returns one value: the average salary. The outer query compares each employee's salary with that value. Because the calculation is short, used once, and directly related to the filter, giving it a separate name would add little value.&lt;/p&gt;

&lt;p&gt;Subqueries can appear in &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;FROM&lt;/code&gt;, and &lt;code&gt;HAVING&lt;/code&gt;. They may return one value, one row, multiple rows, or a table-like result. The operator must match that shape: use &lt;code&gt;=&lt;/code&gt; for one value, for example, and &lt;code&gt;IN&lt;/code&gt; or &lt;code&gt;EXISTS&lt;/code&gt; when working with a set of rows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXISTS&lt;/code&gt; is especially useful when you only need to know whether a related row is present. It stops being about values from the inner query; the question becomes true or false:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Return departments that have at least one highly paid employee.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;departments&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- SELECT 1 signals that the returned columns do not matter.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;
      &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inner query is correlated through &lt;code&gt;department_id&lt;/code&gt;. For each department, the database checks whether a matching employee exists. This is often clearer than joining tables and then removing duplicate departments.&lt;/p&gt;

&lt;p&gt;A correlated subquery is different because it references the current row of the outer query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Evaluate every employee in the outer query.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Recalculate the average for the outer employee's department.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This asks whether each employee earns more than the average for their own department. It is expressive, but correlated subqueries can require repeated work. On large tables, compare the execution plan with an equivalent join or CTE instead of assuming which form is faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  CTE or subquery? Use this checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Prefer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A short calculation used once&lt;/td&gt;
&lt;td&gt;Subquery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A simple membership or existence check&lt;/td&gt;
&lt;td&gt;Subquery with &lt;code&gt;IN&lt;/code&gt; or &lt;code&gt;EXISTS&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Several nested levels are becoming hard to follow&lt;/td&gt;
&lt;td&gt;CTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The query has clear, sequential transformation steps&lt;/td&gt;
&lt;td&gt;CTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The same named result is referenced more than once&lt;/td&gt;
&lt;td&gt;CTE, then inspect the plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hierarchical or graph-like traversal is required&lt;/td&gt;
&lt;td&gt;Recursive CTE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;=&lt;/code&gt; with a multi-row result.&lt;/strong&gt; A scalar comparison expects one value. If the subquery can return several rows, use an appropriate set operator such as &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;ANY&lt;/code&gt;, &lt;code&gt;ALL&lt;/code&gt;, or &lt;code&gt;EXISTS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring &lt;code&gt;NULL&lt;/code&gt; with &lt;code&gt;NOT IN&lt;/code&gt;.&lt;/strong&gt; If the inner result contains &lt;code&gt;NULL&lt;/code&gt;, &lt;code&gt;NOT IN&lt;/code&gt; can evaluate to unknown and return no rows. A correlated &lt;code&gt;NOT EXISTS&lt;/code&gt; is often safer when expressing "no matching row."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming a CTE stores data.&lt;/strong&gt; A CTE exists only for one statement. Use a temporary table when results must survive across statements or need their own indexes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing recursion without a stopping path.&lt;/strong&gt; The recursive member must eventually stop finding rows. Cycles in hierarchical data can otherwise cause repeated work or hit a database recursion limit.&lt;/p&gt;

&lt;p&gt;Readability is the first decision, not the last. If the subquery is understandable at a glance, keep it. If readers must count parentheses or mentally execute several nested levels, name the stages with CTEs.&lt;/p&gt;

&lt;p&gt;Performance is database- and query-specific. A CTE is not automatically materialized, cached, or faster than a subquery. Optimizers may inline, materialize, or transform expressions differently. Use &lt;code&gt;EXPLAIN&lt;/code&gt; or your database's execution-plan tool with realistic data before making performance claims.&lt;/p&gt;

&lt;p&gt;The practical rule is simple: keep local logic local with a subquery, and use a CTE when naming the steps makes the query easier to reason about. Clear SQL is easier to review today and safer to change later.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SQL Intermediate Project: Build a Greenwood Academy Database with PostgreSQL</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:44:47 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/sql-intermediate-project-build-a-greenwood-academy-database-with-postgresql-9p5</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/sql-intermediate-project-build-a-greenwood-academy-database-with-postgresql-9p5</guid>
      <description>&lt;h2&gt;
  
  
  Intermediate SQL Portfolio Project: Master Key Skills
&lt;/h2&gt;

&lt;p&gt;If you already know basic SQL and want a project that feels more practical, this Greenwood Academy database is a good next step. It starts with simple tables, then moves into real database work: inserting data, updating records, filtering results, using operators, counting records, formatting dates, writing &lt;code&gt;CASE WHEN&lt;/code&gt; logic, and using a subquery for a more advanced question.&lt;/p&gt;

&lt;p&gt;This project is labelled as intermediate SQL because it goes beyond only creating tables and selecting rows. It shows how SQL can help organize school data and turn it into information that teachers, administrators, students, and technical teams can understand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20vrirs6vcvf256xn21z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F20vrirs6vcvf256xn21z.png" alt="screenshot of the project files in VS Code" width="799" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this project is about
&lt;/h2&gt;

&lt;p&gt;Greenwood Academy is a fictional school database. The database stores students, subjects, and exam results.&lt;/p&gt;

&lt;p&gt;Instead of working with random examples, the project follows a story. A school needs to keep student details, know which subjects are taught, record exam marks, correct mistakes, remove cancelled results, and create simple reports.&lt;/p&gt;

&lt;p&gt;The database has three main tables:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Table&lt;/th&gt;
&lt;th&gt;What it stores&lt;/th&gt;
&lt;th&gt;Example use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;students&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Student names, gender, date of birth, class, and city&lt;/td&gt;
&lt;td&gt;Find Form 3 students from Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;subjects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subject names, departments, teachers, and credit hours&lt;/td&gt;
&lt;td&gt;Find all Science subjects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exam_results&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Student results, marks, grades, and exam dates&lt;/td&gt;
&lt;td&gt;Find results above 70 marks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The main idea is simple: first create a clean structure, then add data, then ask useful questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Creating the schema
&lt;/h2&gt;

&lt;p&gt;The project starts by creating a schema called &lt;code&gt;greenwood_academy&lt;/code&gt;. A schema helps group related database objects together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;greenwood_academy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;search_path&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;greenwood_academy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the school database objects organized in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Creating the students table
&lt;/h2&gt;

&lt;p&gt;The first table stores student information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;date_of_birth&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important column here is &lt;code&gt;student_id&lt;/code&gt;. It is the primary key, which means every student gets a unique identifier.&lt;/p&gt;

&lt;p&gt;For a non-technical reader, this is like giving every learner a school admission number. Even if two students have the same name, the database can still tell them apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Creating the subjects table
&lt;/h2&gt;

&lt;p&gt;The second table stores the subjects offered by Greenwood Academy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;subject_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;teacher_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;credits&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;UNIQUE&lt;/code&gt; rule on &lt;code&gt;subject_name&lt;/code&gt; prevents the same subject from being entered more than once with the exact same name.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Creating the exam results table
&lt;/h2&gt;

&lt;p&gt;The third table connects students to subjects through exam performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;subject_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exam_date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the project becomes relational. The &lt;code&gt;exam_results&lt;/code&gt; table does not store the full student name or full subject details. It stores &lt;code&gt;student_id&lt;/code&gt; and &lt;code&gt;subject_id&lt;/code&gt;, then uses foreign keys to connect back to the correct records.&lt;/p&gt;

&lt;p&gt;That means one student can have many exam results, and one subject can appear in many exam results.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Altering tables after creation
&lt;/h2&gt;

&lt;p&gt;A database design often changes as the project grows. In this project, I added a phone number column, renamed a subject column, removed the phone number column, and added a grade column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;credits&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;credit_hours&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;grade&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part is important because real databases are not always designed perfectly on the first attempt. &lt;code&gt;ALTER TABLE&lt;/code&gt; lets you adjust the structure while continuing to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Inserting student data
&lt;/h2&gt;

&lt;p&gt;After creating the tables, I inserted sample students into the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Amina'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Wanjiku'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2008-03-12'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Brian'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ochieng'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2007-07-25'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cynthia'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mutua'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'F'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2008-11-05'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kisumu'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'David'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kamau'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'M'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2007-02-18'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output from the &lt;code&gt;students&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_id&lt;/th&gt;
&lt;th&gt;first_name&lt;/th&gt;
&lt;th&gt;last_name&lt;/th&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;Wanjiku&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;Ochieng&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Cynthia&lt;/td&gt;
&lt;td&gt;Mutua&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;Kamau&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  7. Inserting subjects and exam results
&lt;/h2&gt;

&lt;p&gt;The subjects table stores what the school teaches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;subject_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;teacher_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;credit_hours&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Mathematics'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sciences'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mr. Njoroge'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'English'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Languages'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ms. Adhiambo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Biology'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sciences'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ms. Otieno'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'History'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Humanities'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mr. Waweru'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exam results table then records marks for specific students and subjects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;subject_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;marks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;grade&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-16'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-17'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'C'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the three tables start working together.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Updating and deleting records
&lt;/h2&gt;

&lt;p&gt;In a real system, data changes. A student may move, a mark may be corrected, or an exam result may be cancelled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Esther Akinyi moved from Nakuru to Nairobi&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Correct marks for result_id 5 from 49 to 59&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;result_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Remove a cancelled exam result&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;result_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section shows that SQL is not only for reading data. It is also used to maintain accurate records.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Filtering students and subjects
&lt;/h2&gt;

&lt;p&gt;Filtering is one of the most useful SQL skills. It allows you to ask focused questions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find all students in Form 4&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Form 4'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;first_name&lt;/th&gt;
&lt;th&gt;last_name&lt;/th&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;Ochieng&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;Kamau&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hassan&lt;/td&gt;
&lt;td&gt;Abdi&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Another example finds all subjects in the Sciences department.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find all subjects in the Sciences department&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Sciences'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;subject_name&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;teacher_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;Sciences&lt;/td&gt;
&lt;td&gt;Mr. Njoroge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biology&lt;/td&gt;
&lt;td&gt;Sciences&lt;/td&gt;
&lt;td&gt;Ms. Otieno&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Physics&lt;/td&gt;
&lt;td&gt;Sciences&lt;/td&gt;
&lt;td&gt;Mr. Kamande&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chemistry&lt;/td&gt;
&lt;td&gt;Sciences&lt;/td&gt;
&lt;td&gt;Ms. Muthoni&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Computer Studies&lt;/td&gt;
&lt;td&gt;Sciences&lt;/td&gt;
&lt;td&gt;Mr. Oduya&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  10. Combining conditions with AND and OR
&lt;/h2&gt;

&lt;p&gt;SQL becomes more useful when conditions are combined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find Form 3 students from Nairobi&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Form 3'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;first_name&lt;/th&gt;
&lt;th&gt;last_name&lt;/th&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;Wanjiku&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace&lt;/td&gt;
&lt;td&gt;Mwangi&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;James&lt;/td&gt;
&lt;td&gt;Kariuki&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;AND&lt;/code&gt; keyword means both conditions must be true.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;OR&lt;/code&gt; keyword is used when either condition can be true.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find students who are in Form 2 or Form 4&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Form 2'&lt;/span&gt;
   &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Form 4'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  11. Using BETWEEN, IN, NOT IN, and LIKE
&lt;/h2&gt;

&lt;p&gt;Intermediate SQL includes operators that make queries cleaner.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BETWEEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match values inside a range&lt;/td&gt;
&lt;td&gt;Marks from 50 to 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match any value in a list&lt;/td&gt;
&lt;td&gt;Students from Nairobi, Mombasa, or Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude values in a list&lt;/td&gt;
&lt;td&gt;Students not in Form 2 or Form 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match a text pattern&lt;/td&gt;
&lt;td&gt;Subjects containing Studies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find exam results where marks are between 50 and 80&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find students who live in selected cities&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kisumu'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find subjects whose name contains Studies&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%Studies%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These operators reduce repetition and make the question easier to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Working with string functions
&lt;/h2&gt;

&lt;p&gt;String functions help format text without changing the original stored data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show city names in uppercase and lowercase&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;UPPER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;upper_city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;lower_city&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;city&lt;/th&gt;
&lt;th&gt;upper_city&lt;/th&gt;
&lt;th&gt;lower_city&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;td&gt;NAIROBI&lt;/td&gt;
&lt;td&gt;nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;td&gt;MOMBASA&lt;/td&gt;
&lt;td&gt;mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;td&gt;KISUMU&lt;/td&gt;
&lt;td&gt;kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Another useful string function is &lt;code&gt;CONCAT&lt;/code&gt;, which joins text values together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Combine subject name and teacher name&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' - '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;teacher_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;subject_teacher&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;subject_teacher&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mathematics - Mr. Njoroge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;English - Ms. Adhiambo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biology - Ms. Otieno&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  13. Working with dates
&lt;/h2&gt;

&lt;p&gt;Dates are very important in school data because students have birth dates and exams have exam dates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Extract year, month, and day from each student's date of birth&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;YEAR&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;birth_year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MONTH&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;birth_month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DAY&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;birth_day&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;first_name&lt;/th&gt;
&lt;th&gt;date_of_birth&lt;/th&gt;
&lt;th&gt;birth_year&lt;/th&gt;
&lt;th&gt;birth_month&lt;/th&gt;
&lt;th&gt;birth_day&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;2008-03-12&lt;/td&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;2007-07-25&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The project also formats exam dates into a friendlier format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Format each exam date as a friendly date&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Day, DDth Month YYYY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;friendly_date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  14. Important gotcha: hidden spaces in TO_CHAR day names
&lt;/h2&gt;

&lt;p&gt;This was one of the most important small lessons I noticed while working with dates.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;TO_CHAR(exam_date, 'Day')&lt;/code&gt; is used, PostgreSQL pads the day name with extra spaces so all day names line up with the longest day name, &lt;code&gt;Wednesday&lt;/code&gt;. That means the output may look correct on the screen, but the value can contain hidden trailing spaces.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format used&lt;/th&gt;
&lt;th&gt;What it looks like&lt;/th&gt;
&lt;th&gt;What may actually be returned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TO_CHAR(exam_date, 'Day')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Saturday&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;'Saturday '&lt;/code&gt; with hidden padding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TO_CHAR(exam_date, 'FMDay')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Saturday&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;'Saturday'&lt;/code&gt; without padding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This matters because hidden spaces can break conditional logic. A value that looks like &lt;code&gt;Saturday&lt;/code&gt; may not equal the text &lt;code&gt;'Saturday'&lt;/code&gt; if the database actually returned &lt;code&gt;'Saturday '&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This can fail because 'Day' may return padded text such as 'Saturday '&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Day'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;exam_day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Day'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Saturday'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sunday'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Weekend'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Weekday'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;day_type&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The safer version from this project uses &lt;code&gt;FMDay&lt;/code&gt;. The &lt;code&gt;FM&lt;/code&gt; modifier means fill mode, and it removes the blank padding from the formatted result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Safer version: FMDay removes hidden padding from the day name&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FMDay'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;exam_day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'FMDay'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Saturday'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sunday'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Weekend'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Weekday'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;day_type&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another safe option is to wrap the formatted value with &lt;code&gt;TRIM()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Alternative fix: TRIM removes leading and trailing spaces&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Day'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;exam_day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;TRIM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TO_CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exam_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Day'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Saturday'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Sunday'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Weekend'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Weekday'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;day_type&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lesson is simple: when using &lt;code&gt;TO_CHAR()&lt;/code&gt; for day names in conditions, use &lt;code&gt;FMDay&lt;/code&gt; or &lt;code&gt;TRIM()&lt;/code&gt;. Otherwise, the query may look correct but still classify weekends as weekdays because of invisible spaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjp31g3aquszrdb4erwpr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjp31g3aquszrdb4erwpr.png" alt="Add a screenshot of one date function query result" width="608" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6wgjj8accwkaetkpd7jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6wgjj8accwkaetkpd7jf.png" alt="Weekend or weekday labelling" width="527" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  15. Counting records
&lt;/h2&gt;

&lt;p&gt;Counting helps turn raw records into summaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Count how many students are currently in Form 3&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_students&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Form 3'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;total_students&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Another count checks exam results with marks of 70 or above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Count exam results with marks of 70 or above&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_results&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the database starts to behave like a reporting tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  16. Using CASE WHEN for readable labels
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CASE WHEN&lt;/code&gt; is useful when raw values need to become meaningful labels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Label each exam result with a grade description&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;result_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;marks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Distinction'&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Merit'&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Pass'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Fail'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;result_grade&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;exam_results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;result_id&lt;/th&gt;
&lt;th&gt;marks&lt;/th&gt;
&lt;th&gt;result_grade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;Merit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;Distinction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;Distinction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;55&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jkauy43aa0kwafzub52.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jkauy43aa0kwafzub52.png" alt="Case when output" width="528" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  17. Using CASE WHEN with class labels
&lt;/h2&gt;

&lt;p&gt;The project also labels students as Senior or Junior based on their class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Label students as Senior or Junior&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Form 3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 4'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Senior'&lt;/span&gt;
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Form 2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Junior'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;class_label&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;first_name&lt;/th&gt;
&lt;th&gt;last_name&lt;/th&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;class_label&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Amina&lt;/td&gt;
&lt;td&gt;Wanjiku&lt;/td&gt;
&lt;td&gt;Form 3&lt;/td&gt;
&lt;td&gt;Senior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;Ochieng&lt;/td&gt;
&lt;td&gt;Form 4&lt;/td&gt;
&lt;td&gt;Senior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Esther&lt;/td&gt;
&lt;td&gt;Akinyi&lt;/td&gt;
&lt;td&gt;Form 2&lt;/td&gt;
&lt;td&gt;Junior&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This shows how SQL can add meaning to stored values.&lt;/p&gt;

&lt;h2&gt;
  
  
  18. Finding upcoming birthdays with a subquery
&lt;/h2&gt;

&lt;p&gt;The most advanced query in this project finds students whose birthdays are coming up in the next 30 days.&lt;/p&gt;

&lt;p&gt;The challenge is that &lt;code&gt;date_of_birth&lt;/code&gt; includes the original year of birth. To compare birthdays with the current date, the query first calculates each student's birthday in the current year, then filters from that calculated result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find students whose birthday is coming up in the next 30 days&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;birthday_this_year&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;days_until_birthday&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;date_of_birth&lt;/span&gt;
            &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;YEAR&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;EXTRACT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;YEAR&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;date_of_birth&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 year'&lt;/span&gt;
        &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;birthday_this_year&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;birthday_this_year&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'30 days'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a strong SQL example because it solves a problem in two stages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inner query&lt;/td&gt;
&lt;td&gt;Calculates each student's birthday in the current year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outer query&lt;/td&gt;
&lt;td&gt;Filters only birthdays between today and the next 30 days&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The lesson here is that SQL queries can be built step by step. When a question is too complex, create the missing calculated value first, then filter from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned from this project
&lt;/h2&gt;

&lt;p&gt;This project helped me see SQL as more than a language for writing commands. It is a way to organize a real-world situation into clear tables and useful questions.&lt;/p&gt;

&lt;p&gt;I practiced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating schemas and tables&lt;/li&gt;
&lt;li&gt;Using primary keys and foreign keys&lt;/li&gt;
&lt;li&gt;Inserting sample data&lt;/li&gt;
&lt;li&gt;Updating and deleting records&lt;/li&gt;
&lt;li&gt;Filtering with &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;AND&lt;/code&gt;, and &lt;code&gt;OR&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;BETWEEN&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;NOT IN&lt;/code&gt;, and &lt;code&gt;LIKE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Working with string and date functions&lt;/li&gt;
&lt;li&gt;Counting records with &lt;code&gt;COUNT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grouping records with &lt;code&gt;GROUP BY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Creating labels with &lt;code&gt;CASE WHEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Solving a multi-step question with a subquery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Greenwood Academy is a small database project, but it follows the same thinking used in larger systems. First, you decide what information matters. Then you store it in the right tables. After that, SQL helps you ask better questions and produce answers that people can use.&lt;/p&gt;

&lt;p&gt;That is what makes this an intermediate SQL project. It is not only about syntax. It is about using SQL to turn raw data into structured, readable, and meaningful information.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>database</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SQL Beginner Project: Build a Real-World Supermarket Database with PostgreSQL</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Sat, 18 Jul 2026 23:10:22 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/sql-beginner-project-build-a-real-world-supermarket-database-with-postgresql-4e7a</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/sql-beginner-project-build-a-real-world-supermarket-database-with-postgresql-4e7a</guid>
      <description>&lt;h1&gt;
  
  
  SQL Beginner Project: Build a Real-World Supermarket Database with PostgreSQL
&lt;/h1&gt;

&lt;p&gt;If you are learning SQL and want a project that feels practical, this Sunrise Supermarket database is a great place to start. Instead of only reading theory, this project shows how to design tables, insert real sample data, clean and update records, filter business data, summarize results, and connect tables with joins.&lt;/p&gt;

&lt;p&gt;In this article, I will walk through the SQL concepts I practiced while building a small supermarket database from scratch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqmeowsft67lnk0b2a8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flqmeowsft67lnk0b2a8b.png" alt=" " width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SQL?
&lt;/h2&gt;

&lt;p&gt;SQL stands for Structured Query Language. It is the language used to communicate with relational databases. With SQL, you can create database structures, store data, update records, delete records, and ask questions about the data using queries.&lt;/p&gt;

&lt;p&gt;SQL is widely used in software development, data analysis, backend engineering, business intelligence, and reporting. For example, a supermarket can use SQL to manage customers, products, orders, stock levels, and sales reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Areas Covered in This Project
&lt;/h2&gt;

&lt;p&gt;This project covers the following beginner-friendly SQL topics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Database creation and schema selection&lt;/li&gt;
&lt;li&gt;Creating tables with primary keys and foreign keys&lt;/li&gt;
&lt;li&gt;Using constraints such as &lt;code&gt;UNIQUE&lt;/code&gt;, &lt;code&gt;CHECK&lt;/code&gt;, and &lt;code&gt;DEFAULT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inserting data into related tables&lt;/li&gt;
&lt;li&gt;Altering table structure with &lt;code&gt;ALTER TABLE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Updating and deleting records&lt;/li&gt;
&lt;li&gt;Filtering data with &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, and &lt;code&gt;LIKE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sorting and limiting results with &lt;code&gt;ORDER BY&lt;/code&gt; and &lt;code&gt;LIMIT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grouping data with &lt;code&gt;GROUP BY&lt;/code&gt; and &lt;code&gt;HAVING&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Joining multiple tables with &lt;code&gt;INNER JOIN&lt;/code&gt; and &lt;code&gt;LEFT JOIN&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project Scenario: Sunrise Supermarket
&lt;/h2&gt;

&lt;p&gt;Sunrise Supermarket wants to move its records into a proper database. The database needs four connected tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;customers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;products&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orders&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;order_items&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tables help answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which customers placed orders?&lt;/li&gt;
&lt;li&gt;Which products cost more than a certain amount?&lt;/li&gt;
&lt;li&gt;Which orders are still pending?&lt;/li&gt;
&lt;li&gt;What products were ordered and in what quantity?&lt;/li&gt;
&lt;li&gt;How can customer, order, and product data be joined together?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Creating the Database Schema
&lt;/h2&gt;

&lt;p&gt;A schema helps organize database objects such as tables. In this project, I created a schema called &lt;code&gt;sunrise&lt;/code&gt; and set it as the active search path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create the schema for the supermarket database&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;sunrise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Use the sunrise schema for the rest of the project&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;search_path&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;sunrise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffax0l2qmqcxc7hmyh8ep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffax0l2qmqcxc7hmyh8ep.png" alt=" " width="367" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Creating the Customers Table
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;customers&lt;/code&gt; table stores customer details. Each customer has a unique &lt;code&gt;customer_id&lt;/code&gt;, and the email and phone number should not be repeated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create the customers table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;full_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;county&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Add a unique constraint to phone_number&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
    &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;unique_phone&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiz491mt2ikxrvr7vqrlz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiz491mt2ikxrvr7vqrlz.png" alt=" " width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Inserting Customers
&lt;/h2&gt;

&lt;p&gt;After creating the table, I inserted four customer records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Insert customer records&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Grace Wambui'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'grace.wambui@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0711223344'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Kevin Mutiso'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'kevin.mutiso@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0722334455'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Nakuru'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Faith Chebet'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'faith.chebte@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0733445566'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Eldoret'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ibrahim Noor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ibrahim.noor@gmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'0744556677'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- View all customers&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;full_name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;th&gt;phone_number&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:grace.wambui@gmail.com"&gt;grace.wambui@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0711223344&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Kevin Mutiso&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:kevin.mutiso@gmail.com"&gt;kevin.mutiso@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0722334455&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:faith.chebte@gmail.com"&gt;faith.chebte@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0733445566&lt;/td&gt;
&lt;td&gt;Eldoret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Ibrahim Noor&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:ibrahim.noor@gmail.com"&gt;ibrahim.noor@gmail.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0744556677&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. Creating the Products Table
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;products&lt;/code&gt; table stores supermarket products. The &lt;code&gt;unit_price&lt;/code&gt; must be greater than zero, and stock starts at &lt;code&gt;0&lt;/code&gt; if no value is provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create the products table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;product_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Inserting Products
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Insert product records&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Maize Flour 2kg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Groceries'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cooking Oil 1L'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Groceries'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bathing Soap'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Toiletries'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Notebook A4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Stationery'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- View all products&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_id&lt;/th&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;category&lt;/th&gt;
&lt;th&gt;unit_price&lt;/th&gt;
&lt;th&gt;stock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;Groceries&lt;/td&gt;
&lt;td&gt;180.00&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;Groceries&lt;/td&gt;
&lt;td&gt;320.00&lt;/td&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Bathing Soap&lt;/td&gt;
&lt;td&gt;Toiletries&lt;/td&gt;
&lt;td&gt;85.00&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Notebook A4&lt;/td&gt;
&lt;td&gt;Stationery&lt;/td&gt;
&lt;td&gt;60.00&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zg1qo07nta4hfu3j349.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0zg1qo07nta4hfu3j349.png" alt=" " width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Creating Orders and Order Items
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;orders&lt;/code&gt; table connects each order to a customer. The &lt;code&gt;order_items&lt;/code&gt; table connects each order to the products bought.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create the orders table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'Pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Create the order_items table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;order_item_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxq8n98m2ak2ww3njq286.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxq8n98m2ak2ww3njq286.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Inserting Orders and Order Items
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;orders&lt;/code&gt; references &lt;code&gt;customers&lt;/code&gt;, customers must already exist before orders are inserted. Because &lt;code&gt;order_items&lt;/code&gt; references both &lt;code&gt;orders&lt;/code&gt; and &lt;code&gt;products&lt;/code&gt;, both tables must already contain data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Insert order records&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Delivered'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-02'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Pending'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-03'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Delivered'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-04'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Cancelled'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Insert order item records&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Altering Table Structure
&lt;/h2&gt;

&lt;p&gt;After creating the first version of the tables, I made three structure changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Renamed &lt;code&gt;stock&lt;/code&gt; to &lt;code&gt;stock_quantity&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;loyalty_points&lt;/code&gt; to customers&lt;/li&gt;
&lt;li&gt;Increased &lt;code&gt;product_name&lt;/code&gt; to &lt;code&gt;VARCHAR(150)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Rename stock column to stock_quantity&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;RENAME&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;stock_quantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Add loyalty_points to customers with a default value of 0&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;loyalty_points&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Allow longer product names&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fki0dns76am9u9465e8j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fki0dns76am9u9465e8j2.png" alt=" " width="198" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Updating and Deleting Data
&lt;/h2&gt;

&lt;p&gt;The project also required changing an order status and deleting a cancelled order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Update order_id 2 so its status becomes Delivered&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Delivered'&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Delete the cancelled order&lt;/span&gt;
&lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F781zpk50ujj0zqfofcmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F781zpk50ujj0zqfofcmc.png" alt=" " width="678" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Filtering Products and Customers
&lt;/h2&gt;

&lt;p&gt;Filtering is one of the most common SQL skills. Here are examples using comparison operators, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, and &lt;code&gt;LIKE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Products priced above 100
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show every product priced above 100&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;unit_price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;180.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;320.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32vyn6wgxen968yh3us7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32vyn6wgxen968yh3us7.png" alt=" " width="435" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Customers not based in Nairobi
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show every customer not based in Nairobi&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;full_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Mutiso&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Eldoret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ibrahim Noor&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Products priced between 60 and 200
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show products priced between 60 and 200, inclusive&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;unit_price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Notebook A4&lt;/td&gt;
&lt;td&gt;60.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bathing Soap&lt;/td&gt;
&lt;td&gt;85.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;180.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Customers in selected counties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show customers who live in Nairobi, Nakuru, or Mombasa&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Nakuru'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Mombasa'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;full_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Mutiso&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ibrahim Noor&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Products with Oil in the name
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show products whose name contains Oil&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%Oil%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fluaesgnx45aj458scqn8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fluaesgnx45aj458scqn8.png" alt=" " width="346" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Sorting and Limiting Results
&lt;/h2&gt;

&lt;p&gt;Sorting helps organize query results, while &lt;code&gt;LIMIT&lt;/code&gt; controls how many rows are returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show orders that are still Pending, sorted by earliest order_date first&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Pending'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output after updating order &lt;code&gt;2&lt;/code&gt; to &lt;code&gt;Delivered&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;order_date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show the two most expensive products&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;unit_price&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;unit_price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;320.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;180.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  12. Grouping and Aggregates
&lt;/h2&gt;

&lt;p&gt;Aggregate functions summarize data. In this project, I used grouping to count how many orders each customer placed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Count how many orders each customer has placed&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_orders&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output after deleting order &lt;code&gt;4&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;total_orders&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcy1i4sjbtrb1j7r5wrye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcy1i4sjbtrb1j7r5wrye.png" alt=" " width="440" height="102"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show only customers who placed more than one order&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_orders&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;total_orders&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  13. Joining Tables
&lt;/h2&gt;

&lt;p&gt;Joins are used to combine related information from multiple tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inner join customers with orders
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show each customer's name next to their order_id and status&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;full_name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Mutiso&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Delivered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tueik225jivpvmhwg03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tueik225jivpvmhwg03.png" alt=" " width="550" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Left join orders with order items
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show every order with its order items&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F203vmfyuh36l50fcyiiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F203vmfyuh36l50fcyiiz.png" alt=" " width="800" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Join order items with products
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show each order item with the product name and quantity&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_item_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;order_item_id&lt;/th&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;quantity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bathing Soap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Notebook A4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Join all four tables
&lt;/h3&gt;

&lt;p&gt;This query combines customers, orders, order items, and products to show who ordered what.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Join all four tables to show one row per item ordered&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;full_name&lt;/th&gt;
&lt;th&gt;order_id&lt;/th&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;quantity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Bathing Soap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin Mutiso&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace Wambui&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Notebook A4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  14. Total Quantity Ordered Per Product
&lt;/h2&gt;

&lt;p&gt;The final challenge was to group the joined data and calculate total quantity ordered per product.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Show total quantity ordered per product across all customers&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_quantity&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;product_name&lt;/th&gt;
&lt;th&gt;total_quantity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maize Flour 2kg&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bathing Soap&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cooking Oil 1L&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notebook A4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Lessons from This SQL Project
&lt;/h2&gt;

&lt;p&gt;This project helped me practice how relational databases work in a real-world example. The biggest lessons were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tables should be created in the correct order when foreign keys are involved.&lt;/li&gt;
&lt;li&gt;Constraints help protect data quality.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALTER TABLE&lt;/code&gt; is useful when database requirements change.&lt;/li&gt;
&lt;li&gt;Filtering helps answer specific business questions.&lt;/li&gt;
&lt;li&gt;Aggregates and grouping turn raw records into summaries.&lt;/li&gt;
&lt;li&gt;Joins are powerful because they connect data from multiple related tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building the Sunrise Supermarket database was a helpful beginner SQL project because it covered both database design and practical querying. By the end, I had practiced creating tables, inserting data, updating records, filtering results, summarizing data, and joining tables together.&lt;/p&gt;

&lt;p&gt;If you are learning SQL, I recommend building a small project like this because it helps you understand how SQL is used in real business situations.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>database</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Analysing 8 Years of Femicide in Kenya with Excel - A Data Story</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Sat, 04 Jul 2026 01:48:46 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/analysing-8-years-of-femicide-in-kenya-with-excel-a-data-story-2o88</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/analysing-8-years-of-femicide-in-kenya-with-excel-a-data-story-2o88</guid>
      <description>&lt;p&gt;&lt;strong&gt;Content Warning:&lt;/strong&gt; This article discusses femicide, the murder of women and girls. The data covers real cases of gender-based violence. If this is a sensitive topic for you, please take care.&lt;/p&gt;




&lt;p&gt;Femicide isn't just a statistic. Every row in the dataset I worked with was someone's daughter, sister, wife, or friend.&lt;br&gt;
When I came across the &lt;strong&gt;Femicide in Kenya (2016-2023)&lt;/strong&gt; dataset on Kaggle, I knew I wanted to do more than scroll through it. I wanted to let the data speak, clearly and loudly. So I built a full analysis pipeline in Excel: cleaning, enriching, analysing, and visualising 507 documented cases over eight years.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the story of how I did it, what I found, and why it matters.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Where the Data Came From
&lt;/h2&gt;

&lt;p&gt;The dataset lives on &lt;a href="https://www.kaggle.com/" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt; and was compiled from multiple verified public sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Investigative journalism and news media&lt;/li&gt;
&lt;li&gt;Activist platforms and gender-based violence tracking organisations&lt;/li&gt;
&lt;li&gt;Social media reports and community alerts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each row represents one unique femicide case. The columns cover:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it tells us&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Date&lt;/td&gt;
&lt;td&gt;When it was reported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Victim Name / Age&lt;/td&gt;
&lt;td&gt;Who was targeted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;County / Town&lt;/td&gt;
&lt;td&gt;Where it happened&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Suspect Relationship&lt;/td&gt;
&lt;td&gt;Who did it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type of Femicide&lt;/td&gt;
&lt;td&gt;Intimate or Non-intimate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mode of Killing&lt;/td&gt;
&lt;td&gt;How it was done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Murder Scene&lt;/td&gt;
&lt;td&gt;Where specifically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case / Court Status&lt;/td&gt;
&lt;td&gt;What happened legally&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before I could analyse any of this, though, the data needed serious cleaning.&lt;/p&gt;


&lt;h2&gt;
  
  
  Cleaning the Data in Excel Power Query
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fotgc676t71dcji21ojny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fotgc676t71dcji21ojny.png" alt=" " width="799" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I loaded the raw file into &lt;strong&gt;Excel's Power Query Editor&lt;/strong&gt;, my go-to for structured, repeatable data transformations. Here's the exact cleaning process I followed, step by step.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Remove Duplicates
&lt;/h3&gt;

&lt;p&gt;The first thing I always do. Removed all duplicate rows upfront so every case was counted once and only once.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Standardise the Author Name Column
&lt;/h3&gt;

&lt;p&gt;The author names had inconsistent casing, some all caps, some all lowercase. I applied the &lt;strong&gt;PROPER&lt;/strong&gt; function to fix this, so &lt;code&gt;JOHN DOE&lt;/code&gt; and &lt;code&gt;john doe&lt;/code&gt; both become &lt;code&gt;John Doe&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Clean the Medium (News Source) Column
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Replaced all &lt;code&gt;null&lt;/code&gt; and empty values with &lt;code&gt;"Not Provided"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Removed the &lt;code&gt;(Kenya)&lt;/code&gt; suffix that was appended to source names. Since every entry is already Kenya-specific, this was just visual clutter making filters messy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 4: Fix the Type of Murder Column
&lt;/h3&gt;

&lt;p&gt;This one was subtle but important. The column had both &lt;code&gt;femicide&lt;/code&gt; and &lt;code&gt;Femicide&lt;/code&gt; as separate values - Excel treats them as distinct, which would split my pivot counts in two. I standardised everything to &lt;code&gt;Femicide&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5: Clean the Victim Name Column
&lt;/h3&gt;

&lt;p&gt;Some entries had &lt;code&gt;"unnamed"&lt;/code&gt; as the victim's name, others were blank. Both were replaced with &lt;code&gt;"Not Provided"&lt;/code&gt; for consistency.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 6: Fix the Suspect Relationship Column
&lt;/h3&gt;

&lt;p&gt;Two entries had trailing noise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Stranger&lt;/code&gt; had &lt;code&gt;"/Unknown relationship"&lt;/code&gt; appended - redundant. Removed it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Friend&lt;/code&gt; had &lt;code&gt;"/known to the victim"&lt;/code&gt; appended - also redundant. Removed it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These kinds of notes make sense in a notes field, not in a category column you'll filter on.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 7: Fill Remaining Nulls Across Text Columns
&lt;/h3&gt;

&lt;p&gt;A blanket step - replaced all remaining &lt;code&gt;null&lt;/code&gt; and empty cells in text columns with &lt;code&gt;"Not Provided"&lt;/code&gt;. This ensures no blank filter entries appear in pivot slicers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 8: Apply PROPER to More Columns
&lt;/h3&gt;

&lt;p&gt;Repeated the PROPER text function across other columns (county names, relationship types, etc.) to prevent capitalisation variants from appearing as duplicate filter options.&lt;/p&gt;


&lt;h2&gt;
  
  
  Data Enrichment - Adding a Region Column
&lt;/h2&gt;

&lt;p&gt;Once the data was clean, I needed to go one level higher than county for geographic analysis. Kenya has 47 counties - grouping them into the 8 administrative regions would give cleaner, more readable charts.&lt;/p&gt;

&lt;p&gt;I built a separate &lt;strong&gt;Mapping sheet&lt;/strong&gt; with each county mapped to its region, then used this XLOOKUP to create a new &lt;code&gt;Region&lt;/code&gt; column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=XLOOKUP(U2, Mapping!$A$2:$A$48, Mapping!$B$2:$B$48, "Unknown County")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This had a bonus effect: it doubled as &lt;strong&gt;a spell-checker for county names&lt;/strong&gt;. Any misspelled county would return &lt;code&gt;"Unknown County"&lt;/code&gt; - which flagged it for correction in the source data. A clean, self-validating approach.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Dashboard
&lt;/h2&gt;

&lt;p&gt;With clean, enriched data, I built &lt;strong&gt;Pivot Tables&lt;/strong&gt; for every question I wanted to answer, then assembled everything into a single interactive Excel dashboard with slicers for filtering by county, region, murder scene, relationship type, and more.&lt;/p&gt;

&lt;p&gt;Here's what the final dashboard looked like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5dl33myff7qa41bi0cp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi5dl33myff7qa41bi0cp.png" alt="Femicide Cases in Kenya Dashboard" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;KPI&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;507&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Most Affected County&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Counties Affected&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;45 out of 47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intimate Partner Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Most Vulnerable Age Range&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;26-36 years&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minor Victims&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Most Common Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stabbing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Most Common Scene&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Home&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What the Data Revealed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Home Is the Most Dangerous Place
&lt;/h3&gt;

&lt;p&gt;This was the finding that hit hardest. The majority of femicide victims were killed &lt;strong&gt;at home&lt;/strong&gt;, not on the streets, not in unknown locations, at home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1zoic105pedz4fdsn77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa1zoic105pedz4fdsn77.png" alt=" " width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We talk about home as a place of safety. The data dismantles that assumption.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. 83% of Cases Involved Intimate Partners
&lt;/h3&gt;

&lt;p&gt;Boyfriends. Husbands. Ex-partners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8 in 10 femicide cases&lt;/strong&gt; in this dataset were committed by someone the victim was in, or had been in, a relationship with. Intimate partner violence isn't just a contributor to femicide in Kenya. It is, overwhelmingly, the cause.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjt342vo5rouwg7xsu5q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxjt342vo5rouwg7xsu5q.png" alt=" " width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faba552usv8qzy2ig75ku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faba552usv8qzy2ig75ku.png" alt=" " width="752" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Most Targeted Age Group Is 26-36
&lt;/h3&gt;

&lt;p&gt;These are women in the prime of their adult lives, building careers, raising families, living independently. The data shows they are the most at-risk group.&lt;/p&gt;

&lt;p&gt;Twenty victims were minors. Twenty children.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fied1eqnppyltxrpt7quu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fied1eqnppyltxrpt7quu.png" alt=" " width="799" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Stabbing and Strangulation Dominate
&lt;/h3&gt;

&lt;p&gt;The two most recorded methods of killing were stabbing and strangulation. Both require physical proximity. This is not random or opportunistic violence - it is violence by someone close to the victim, in spaces familiar to her.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5t9c3f59e90hq7egi4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp5t9c3f59e90hq7egi4z.png" alt=" " width="752" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. This Is a National Crisis, Not a Nairobi Story
&lt;/h3&gt;

&lt;p&gt;Yes, Nairobi had the highest case count. But &lt;strong&gt;45 of Kenya's 47 counties&lt;/strong&gt; recorded at least one femicide case in this dataset. The geographic spread is a clear signal: this is a nationwide systemic problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The Justice System Is Failing These Women
&lt;/h3&gt;

&lt;p&gt;When I looked at the court verdict breakdown, the majority of cases never reached a guilty verdict. Many were ongoing. Some suspects were never brought to trial at all. The impunity is visible in the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Arguments Over Food
&lt;/h3&gt;

&lt;p&gt;I'll be honest, this stopped me in my tracks. Going through case descriptions, several murders were triggered by domestic arguments. One recurring trigger: &lt;strong&gt;food&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda9cn0qufecd8fqwts39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda9cn0qufecd8fqwts39.png" alt=" " width="799" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Arguments over food. Women losing their lives over arguments about food.&lt;/p&gt;

&lt;p&gt;That's not just a data point. That's a reflection of what happens when control, anger, and access to violence exist in the same home.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;I'm a data analyst. My tools are spreadsheets, formulas, and charts. But tools don't choose what they illuminate, we do.&lt;/p&gt;

&lt;p&gt;I chose to work on this dataset because the story it tells is important, and because too often data on gender-based violence gets buried in PDFs or left uncleaned on data platforms. I wanted to take it out of the raw and make it readable, visual, and undeniable.&lt;/p&gt;

&lt;p&gt;If you're a researcher, journalist, policymaker, or just someone who cares, I hope this analysis gives you something useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools I Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft Excel&lt;/strong&gt; - Power Query for cleaning, XLOOKUP for enrichment, Pivot Tables for aggregation, Charts for visualisation, Dashboard for presentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. No Python. No SQL. No Power BI. Just Excel used thoughtfully and thoroughly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Access the Files
&lt;/h2&gt;

&lt;p&gt;The full project, including the raw dataset, cleaned file, and dashboard, is on GitHub:&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://github.com/pinkleather221/kenya-femicide-analysis.git" rel="noopener noreferrer"&gt;https://github.com/pinkleather221/kenya-femicide-analysis.git&lt;/a&gt;)&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Note
&lt;/h2&gt;

&lt;p&gt;Behind every row in this dataset is a name. A face. A life that was ended by violence that could have been prevented.&lt;/p&gt;

&lt;p&gt;If this analysis can contribute, even a small amount, to better policy, stronger advocacy, or clearer public understanding of the femicide crisis in Kenya, then it was worth building.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful or have questions about the methodology, drop a comment below. And if you're working on similar social impact data projects, I'd love to connect.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>data</category>
      <category>analytics</category>
      <category>community</category>
      <category>diversity</category>
    </item>
    <item>
      <title>From Dirty Data to Business Intelligence: Building an End-to-End Power BI Analytics Solution with PostgreSQL, Power Query, DAX, and Star Schema</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Fri, 03 Jul 2026 12:44:39 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/from-dirty-data-to-business-intelligence-building-an-end-to-end-power-bi-analytics-solution-with-4bfj</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/from-dirty-data-to-business-intelligence-building-an-end-to-end-power-bi-analytics-solution-with-4bfj</guid>
      <description>&lt;p&gt;&lt;em&gt;How I transformed a messy education dataset into an interactive Business Intelligence solution using PostgreSQL, Power Query, Data Modeling, DAX, and Power BI.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every dashboard tells a story.&lt;/p&gt;

&lt;p&gt;But before the charts, KPIs, and colorful visuals come into existence, there's usually something much less exciting a spreadsheet full of inconsistent, duplicated, incomplete, and poorly structured data.&lt;br&gt;
That was exactly the challenge my team received during our Power BI training.&lt;br&gt;
Instead of being handed a perfectly structured dataset ready for visualization, we were given what many data professionals encounter in real organizations: a single flat dataset filled with inconsistencies that needed to be transformed into a reliable source of business intelligence.&lt;br&gt;
Our assignment wasn't simply to create attractive charts.&lt;br&gt;
Our assignment wasn't simply to create attractive charts.&lt;/p&gt;

&lt;p&gt;We were expected to complete the entire Business Intelligence workflow, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extracting data from a PostgreSQL database&lt;/li&gt;
&lt;li&gt;Establishing a secure database connection using SSL certificates&lt;/li&gt;
&lt;li&gt;Cleaning messy data using Power Query&lt;/li&gt;
&lt;li&gt;Designing a dimensional data model&lt;/li&gt;
&lt;li&gt;Creating relationships between tables&lt;/li&gt;
&lt;li&gt;Writing DAX measures&lt;/li&gt;
&lt;li&gt;Building interactive dashboards&lt;/li&gt;
&lt;li&gt;Producing meaningful business insights&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although this was an educational project, the workflow closely mirrors what Business Intelligence developers and Data Analysts do every day.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through that journey from start to finish.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Business Problem
&lt;/h2&gt;

&lt;p&gt;The dataset represented an education management system containing information about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Students&lt;/li&gt;
&lt;li&gt;Schools&lt;/li&gt;
&lt;li&gt;Teachers&lt;/li&gt;
&lt;li&gt;Subjects&lt;/li&gt;
&lt;li&gt;Academic performance&lt;/li&gt;
&lt;li&gt;Payments&lt;/li&gt;
&lt;li&gt;Locations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, everything appeared to exist inside one large table. While this may seem convenient, storing everything in a single table quickly creates problems.&lt;/p&gt;

&lt;p&gt;Some of the issues included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate information&lt;/li&gt;
&lt;li&gt;Repeated school names and teacher details&lt;/li&gt;
&lt;li&gt;Inconsistent county names and spelling&lt;/li&gt;
&lt;li&gt;Mixed capitalization&lt;/li&gt;
&lt;li&gt;Missing values&lt;/li&gt;
&lt;li&gt;Incorrect data types and invalid numerical values&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;em&gt;These are exactly the kinds of data quality issues that reduce trust in business reports. Our brief required us to clean the data, normalize it into logical tables, build relationships, and answer business questions through an interactive Power BI dashboard.&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7z3vretkc98cm8bwxef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7z3vretkc98cm8bwxef.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why We Used PostgreSQL Instead of Importing a CSV
&lt;/h2&gt;

&lt;p&gt;Many Power BI tutorials begin by importing an Excel or CSV file. While this works for demonstrations, most organizations do not store operational data inside spreadsheets.&lt;/p&gt;

&lt;p&gt;Instead, business data is typically stored inside relational databases such as PostgreSQL, SQL Server, MySQL, or Oracle.&lt;/p&gt;

&lt;p&gt;For this project, our dataset was hosted inside a PostgreSQL database, allowing us to simulate a real production environment where Power BI retrieves data directly from a database rather than from manually exported files.&lt;/p&gt;

&lt;p&gt;This approach offered several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A centralized source of truth&lt;/li&gt;
&lt;li&gt;Better data integrity&lt;/li&gt;
&lt;li&gt;Easier scalability&lt;/li&gt;
&lt;li&gt;Simplified data refreshes&lt;/li&gt;
&lt;li&gt;A more realistic enterprise workflow&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Configuring PostgreSQL for Secure Connections
&lt;/h2&gt;

&lt;p&gt;One of the most interesting parts of this project happened &lt;strong&gt;before&lt;/strong&gt; opening Power BI.&lt;/p&gt;

&lt;p&gt;To establish a secure connection to PostgreSQL, I first had to install and configure the required SSL certificates. Many cloud-hosted PostgreSQL instances enforce encrypted connections to protect data while it travels across the network. Without the correct certificates, Power BI cannot establish a trusted connection.&lt;/p&gt;

&lt;p&gt;The process involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing the PostgreSQL SSL certificate&lt;/li&gt;
&lt;li&gt;Configuring the database connection&lt;/li&gt;
&lt;li&gt;Verifying encrypted communication&lt;/li&gt;
&lt;li&gt;Testing the connection before importing data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although this step often receives little attention in beginner tutorials, it reflects what happens in production systems where database security is a priority.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Understanding secure connectivity is just as important as understanding visualization, because no dashboard can exist without reliable access to trusted data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvkbxyv1cqh66bhdptkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvvkbxyv1cqh66bhdptkm.png" alt=" " width="798" height="168"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting PostgreSQL to Power BI
&lt;/h2&gt;

&lt;p&gt;With the secure connection established, I opened &lt;strong&gt;Power BI Desktop&lt;/strong&gt; and selected &lt;strong&gt;Get Data &amp;gt; PostgreSQL Database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After providing the server name, database name, and authentication credentials, Power BI connected directly to PostgreSQL and displayed the available tables through the Navigator window.&lt;/p&gt;

&lt;p&gt;Rather than importing every available object, I selected only the tables required for analysis. This is a small but important optimization, because importing unnecessary tables increases model complexity and refresh times.&lt;/p&gt;
&lt;h2&gt;
  
  
  First Look at the Dataset
&lt;/h2&gt;

&lt;p&gt;The initial inspection confirmed what the assignment description had warned about. The dataset contained numerous quality issues, including duplicate records, null values, incorrect spellings, mixed casing, inconsistent location names, invalid numerical values, and poorly formatted dates.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important lesson:&lt;/strong&gt; At this point, jumping directly into building charts would have produced misleading results. A dashboard is only as reliable as the data behind it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this reason, data preparation became the next priority.&lt;/p&gt;


&lt;h2&gt;
  
  
  Cleaning the Data with Power Query
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Power Query&lt;/strong&gt; is one of Power BI's most powerful features. Instead of modifying the source database, Power Query allows analysts to transform incoming data while keeping the original source untouched. This creates a repeatable &lt;strong&gt;Extract, Transform, and Load (ETL)&lt;/strong&gt; process that can be refreshed whenever new data becomes available.&lt;/p&gt;

&lt;p&gt;Our cleaning process involved several key transformations:&lt;/p&gt;
&lt;h3&gt;
  
  
  Standardizing Text
&lt;/h3&gt;

&lt;p&gt;Some records used uppercase values, others used lowercase, and some contained abbreviations or spelling mistakes. For example, a county could appear in several different forms, causing Power BI to treat them as different locations. We standardized these values to ensure consistency across the entire dataset.&lt;/p&gt;
&lt;h3&gt;
  
  
  Correcting Data Types
&lt;/h3&gt;

&lt;p&gt;Columns representing numbers had occasionally been interpreted as text, and date columns contained inconsistent formats. Power Query made it possible to convert each column into its correct data type before analysis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Proper data types are essential because DAX calculations depend on them. A number stored as text cannot be summed or averaged.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Handling Missing Values
&lt;/h3&gt;

&lt;p&gt;Several fields contained null values. Rather than blindly deleting records, we evaluated each case individually. Depending on the context, we either replaced missing values, kept them for transparency, or excluded unusable records where appropriate. Documenting these decisions is an important part of responsible data preparation because every transformation influences the final analysis.&lt;/p&gt;
&lt;h3&gt;
  
  
  Removing Duplicates
&lt;/h3&gt;

&lt;p&gt;Repeated records inflate KPIs and distort business insights. Duplicate students, schools, or teachers could easily lead to incorrect counts. Power Query enabled us to identify and remove duplicate records while preserving unique entities.&lt;/p&gt;
&lt;h3&gt;
  
  
  Renaming Columns
&lt;/h3&gt;

&lt;p&gt;Clear naming conventions improve model readability. Instead of cryptic or inconsistent column names, we renamed fields using descriptive, business-friendly names. This makes DAX formulas easier to understand and simplifies collaboration.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a Repeatable Transformation Pipeline
&lt;/h3&gt;
&lt;h2&gt;
  
  
  One of Power Query's greatest strengths is that every transformation becomes an &lt;strong&gt;Applied Step&lt;/strong&gt;. Instead of manually repeating the cleaning process each time new data arrives, Power BI simply replays the transformation steps automatically during refresh. This makes the cleaning process reproducible, auditable, and significantly less error-prone.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvs6dhh171kcbqh1lnqh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvs6dhh171kcbqh1lnqh4.png" alt=" " width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwc1a55d3oougjxmh0dep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwc1a55d3oougjxmh0dep.png" alt=" " width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9494n5apnrump10qz5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe9494n5apnrump10qz5r.png" alt=" " width="320" height="796"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why We Did Not Keep Everything in One Flat Table
&lt;/h2&gt;

&lt;p&gt;Once the data was clean, an important question emerged: should everything remain inside one giant table?&lt;/p&gt;

&lt;p&gt;Technically, it could. Practically, it should not.&lt;/p&gt;

&lt;p&gt;Flat tables work for small datasets, but as projects grow they become difficult to maintain. Repeated information wastes storage, complicates updates, and slows analytical queries. Instead of keeping one massive table, we normalized the data into a dimensional model that better represents how business entities relate to one another.&lt;/p&gt;

&lt;p&gt;This decision laid the foundation for the next and arguably most important phase of the project: building a &lt;strong&gt;Star Schema&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Building a Scalable Data Model with a Star Schema
&lt;/h2&gt;

&lt;p&gt;At this point, our data had been cleaned, standardized, and prepared for analysis. However, one major challenge still remained: the dataset still represented an &lt;em&gt;operational&lt;/em&gt; system rather than an &lt;em&gt;analytical&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;Operational databases are designed to store information efficiently. Business Intelligence systems are designed to analyze information efficiently. Those are two very different objectives.&lt;/p&gt;

&lt;p&gt;If we had continued using one massive flat table, we would have encountered several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same school information repeated hundreds of times&lt;/li&gt;
&lt;li&gt;Teacher information duplicated for every student&lt;/li&gt;
&lt;li&gt;Location information stored repeatedly&lt;/li&gt;
&lt;li&gt;Larger file sizes and slower report performance&lt;/li&gt;
&lt;li&gt;More complex DAX calculations&lt;/li&gt;
&lt;li&gt;Higher maintenance costs whenever data changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, we redesigned the model using one of the most widely adopted dimensional modeling techniques in Business Intelligence: the &lt;strong&gt;Star Schema&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Star Schema?
&lt;/h3&gt;

&lt;p&gt;Before building the model, we briefly considered the three most common approaches:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flat Table:&lt;/strong&gt; Stores everything in one place. While it appears simple at first, it becomes increasingly difficult to maintain because every entity is repeated multiple times. Imagine a school with 800 students. The school's name would appear 800 separate times in the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snowflake Schema:&lt;/strong&gt; Introduces additional normalization by breaking hierarchical data into separate tables (Country &amp;gt; Region &amp;gt; County &amp;gt; Sub-county). This reduces redundancy further but also increases relationship complexity. For our assignment, this introduced unnecessary overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Star Schema:&lt;/strong&gt; Strikes an excellent balance between simplicity, performance, and scalability. The design consists of one central Fact Table surrounded by multiple Dimension Tables. Microsoft also recommends Star Schema as the preferred modeling approach for Power BI because it simplifies filtering, improves query performance, and makes DAX calculations easier to write.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7mee9vwetl47u1ob1z3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7mee9vwetl47u1ob1z3t.png" alt=" " width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Designing the Fact Table
&lt;/h2&gt;

&lt;p&gt;The fact table became the heart of our analytical model. Unlike dimension tables, which describe entities, the fact table stores &lt;strong&gt;measurable events&lt;/strong&gt; such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Student scores&lt;/li&gt;
&lt;li&gt;Fee payments&lt;/li&gt;
&lt;li&gt;Attendance records&lt;/li&gt;
&lt;li&gt;Performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every row in a fact table represents an event that can be analyzed. For example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6d3xdm2skap9x0mhcqsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6d3xdm2skap9x0mhcqsg.png" alt=" " width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the fact table references other entities rather than storing all their descriptive information repeatedly. Instead of storing the teacher's county, school address, and contact details every time, it simply stores a key pointing to the Teacher table. This dramatically reduces redundancy.&lt;/p&gt;


&lt;h2&gt;
  
  
  Building the Dimension Tables
&lt;/h2&gt;

&lt;p&gt;Once the fact table was identified, we separated descriptive information into dedicated dimension tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Student Dimension&lt;/strong&gt; contained: Student ID, Student Name, Gender, Age, School, County&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teacher Dimension&lt;/strong&gt; contained: Teacher ID, Teacher Name, Assigned School, Subject&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;School Dimension&lt;/strong&gt; contained: School ID, School Name, Category, Location&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subject Dimension&lt;/strong&gt; contained: Subject ID, Subject Name&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Location Dimension&lt;/strong&gt; centralized county and region information into a reusable table. This allows both schools and teachers to reference the same geographical information while maintaining consistency.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5lt71fi2xc3yswmywfxw.png" alt=" " width="800" height="505"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Understanding Primary Keys and Foreign Keys
&lt;/h2&gt;

&lt;p&gt;One concept that finally clicked for me during this project was the relationship between primary and foreign keys.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Primary Key&lt;/strong&gt; uniquely identifies each record within a table. For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;School ID&lt;/th&gt;
&lt;th&gt;School Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;Green Hills Academy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Sunrise School&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The School ID is unique. No two schools share the same identifier.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Foreign Key&lt;/strong&gt; references the primary key of another table. For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Student&lt;/th&gt;
&lt;th&gt;School ID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rather than writing the school name repeatedly, each student simply stores the corresponding School ID. Power BI then uses relationships to retrieve the descriptive information whenever required. This is one of the principles that makes relational databases so efficient.&lt;/p&gt;


&lt;h2&gt;
  
  
  Creating Relationships in Power BI
&lt;/h2&gt;

&lt;p&gt;Once the tables had been separated, we connected them using relationships. Power BI supports several relationship types:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-to-One:&lt;/strong&gt; Every record matches exactly one record in another table. Valid but relatively uncommon in analytical models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-to-Many:&lt;/strong&gt; This became the dominant relationship in our project. For example, one school has many students; one teacher has many student records; one subject has many assessment records. This relationship type reflects how educational data naturally behaves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Many-to-One:&lt;/strong&gt; Power BI often displays the same relationship from the opposite perspective. Many students belong to one school. The underlying relationship remains identical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ucxqhinvotdi12xmnn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ucxqhinvotdi12xmnn3.png" alt=" " width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding Cardinality
&lt;/h3&gt;

&lt;p&gt;Cardinality determines how Power BI filters information across related tables. If relationships are configured incorrectly, visuals may return duplicated values or incorrect totals. Because our model followed a proper Star Schema, defining cardinality became much more straightforward, with most relationships naturally following a One-to-Many structure.&lt;/p&gt;
&lt;h3&gt;
  
  
  Filter Direction
&lt;/h3&gt;

&lt;p&gt;Power BI allows filters to flow between related tables. Whenever possible, we maintained &lt;strong&gt;single-direction filtering&lt;/strong&gt;, allowing filters to flow from the dimension tables toward the fact table. For example, selecting a county automatically filtered schools, students, teachers, and performance records. This simplified report behavior while reducing ambiguity during DAX calculations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Normalization Matters
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Dividing one table into several smaller tables might appear to make the project more complicated. Ironically, the opposite is true. Normalization makes the model easier to maintain, easier to understand, faster to query, less repetitive, and more scalable. As datasets grow, these benefits become increasingly significant.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Turning Data into Insights with DAX
&lt;/h2&gt;

&lt;p&gt;After building the data model, the next step was teaching Power BI how to answer business questions. This is where &lt;strong&gt;Data Analysis Expressions (DAX)&lt;/strong&gt; became essential.&lt;/p&gt;

&lt;p&gt;Think of DAX as the language that teaches Power BI how to think. While relationships organize data, DAX transforms that organized data into business metrics.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calculated Columns vs. Measures
&lt;/h3&gt;

&lt;p&gt;One key lesson from this project was understanding the difference between these two approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Calculated Columns&lt;/strong&gt; compute values for every row and store the result in the model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measures&lt;/strong&gt; calculate results dynamically based on the filters applied by the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since dashboards are interactive, measures are generally the better choice because they recalculate automatically as users interact with slicers and filters.&lt;/p&gt;
&lt;h3&gt;
  
  
  Measures We Created
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Total Students&lt;/li&gt;
&lt;li&gt;Total Schools&lt;/li&gt;
&lt;li&gt;Total Teachers&lt;/li&gt;
&lt;li&gt;Average Student Score&lt;/li&gt;
&lt;li&gt;Student Distribution&lt;/li&gt;
&lt;li&gt;School Distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where the dataset lacked complete information, such as revenue or outstanding balances, we intentionally avoided creating misleading measures. Instead, we documented the limitation and focused our analysis on the data that was available.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important principle:&lt;/strong&gt; Good analysts never invent data to satisfy a report.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Applying Business Logic with DAX
&lt;/h3&gt;

&lt;p&gt;Beyond simple aggregations, DAX can encode business rules. Functions such as &lt;code&gt;IF()&lt;/code&gt;, &lt;code&gt;AND()&lt;/code&gt;, and &lt;code&gt;OR()&lt;/code&gt; allow multiple conditions to be evaluated, while &lt;code&gt;SWITCH(TRUE())&lt;/code&gt; provides a cleaner alternative to long nested IF statements.&lt;/p&gt;

&lt;p&gt;For example, a revenue classification measure could be written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Revenue Category =
SWITCH(
    TRUE(),
    ISBLANK([Revenue]), "Not Provided",
    [Revenue] &amp;gt; 500000, "High Revenue",
    [Revenue] &amp;gt; 100000, "Medium Revenue",
    [Revenue] &amp;gt; 0, "Low Revenue"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also learned how &lt;code&gt;CALCULATE()&lt;/code&gt; changes the filter context, enabling measures such as total students in a specific county or average scores for selected schools. This flexibility is one of the reasons DAX is such a powerful analytical language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the Interactive Dashboard
&lt;/h2&gt;

&lt;p&gt;With the model and measures complete, the final step was presenting the insights.&lt;/p&gt;

&lt;p&gt;Our dashboard combined several visualizations to answer the questions outlined in the project brief:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;KPI Cards&lt;/strong&gt; for key metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bar and Column Charts&lt;/strong&gt; for comparisons&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Donut Charts&lt;/strong&gt; for distributions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slicers&lt;/strong&gt; for filtering by school, county, teacher, subject, and gender&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tables&lt;/strong&gt; for detailed records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than filling the report with as many visuals as possible, we focused on clarity. Each chart answered a specific business question, and together they formed a cohesive analytical story.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkqonp1kcydv9lmqxv97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkqonp1kcydv9lmqxv97.png" alt=" " width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7vy0xcqps5e0xf8xs82l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7vy0xcqps5e0xf8xs82l.png" alt=" " width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Insights from the Project
&lt;/h2&gt;

&lt;p&gt;Building the dashboard highlighted how quickly insights become accessible once data is properly prepared. Users could identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schools with the highest student populations&lt;/li&gt;
&lt;li&gt;Student distribution across locations&lt;/li&gt;
&lt;li&gt;Gender distribution&lt;/li&gt;
&lt;li&gt;Teacher allocation&lt;/li&gt;
&lt;li&gt;Subject performance&lt;/li&gt;
&lt;li&gt;Academic trends across schools and regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More importantly, the project demonstrated that the quality of these insights depended entirely on the quality of the underlying data model.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Power BI Desktop
&lt;/h2&gt;

&lt;p&gt;One topic we covered during training was what happens after a report is built.&lt;/p&gt;

&lt;p&gt;Publishing a report to the &lt;strong&gt;Power BI Service&lt;/strong&gt; allows organizations to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share reports securely with stakeholders&lt;/li&gt;
&lt;li&gt;Schedule automatic data refreshes&lt;/li&gt;
&lt;li&gt;Create email subscriptions that send report snapshots at scheduled intervals&lt;/li&gt;
&lt;li&gt;Embed reports into web applications and organizational portals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also explored &lt;strong&gt;Microsoft Fabric&lt;/strong&gt;, Microsoft's unified analytics platform that brings together data engineering, data warehousing, real-time analytics, and business intelligence into a single ecosystem. Although our project was developed in Power BI Desktop, understanding how it fits within the broader Microsoft analytics platform provided valuable context for enterprise-scale solutions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons I Took Away
&lt;/h2&gt;

&lt;p&gt;This project changed the way I think about dashboards.&lt;/p&gt;

&lt;p&gt;Before, I assumed the most important skill in Power BI was creating attractive visualizations. I now realize that visualization is only the &lt;strong&gt;final stage&lt;/strong&gt; of a much larger process.&lt;/p&gt;

&lt;p&gt;The real work happens long before the first chart is created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting securely to data sources&lt;/li&gt;
&lt;li&gt;Cleaning inconsistent data&lt;/li&gt;
&lt;li&gt;Designing scalable data models&lt;/li&gt;
&lt;li&gt;Building meaningful relationships&lt;/li&gt;
&lt;li&gt;Writing reusable DAX measures&lt;/li&gt;
&lt;li&gt;Delivering insights that decision-makers can trust&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A visually impressive dashboard built on poor-quality data is still a poor dashboard.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;What began as a classroom assignment became an excellent introduction to the complete Business Intelligence workflow.&lt;/p&gt;

&lt;p&gt;Over the course of the project, I learned how to connect Power BI to a PostgreSQL database using secure SSL connections, transform messy data with Power Query, redesign a flat dataset into a Star Schema, create relationships between fact and dimension tables, write DAX measures, and communicate insights through an interactive dashboard.&lt;/p&gt;

&lt;p&gt;More importantly, I learned that &lt;strong&gt;Business Intelligence is not about creating charts.&lt;/strong&gt; It is about transforming raw, imperfect data into information that supports better decisions.&lt;/p&gt;

&lt;p&gt;As I continue exploring data engineering and analytics, this project serves as a reminder that every reliable dashboard begins with a well-designed data pipeline, a thoughtful data model, and a commitment to data quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;If you are learning Power BI, I would recommend exploring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/training/powerplatform/power-bi" rel="noopener noreferrer"&gt;Microsoft Learn - Power BI Learning Paths&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dax/" rel="noopener noreferrer"&gt;Microsoft Learn - DAX Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/power-query/" rel="noopener noreferrer"&gt;Microsoft Learn - Power Query Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/power-bi/guidance/star-schema" rel="noopener noreferrer"&gt;Microsoft Learn - Star Schema Guidance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/fabric/" rel="noopener noreferrer"&gt;Microsoft Fabric Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/" rel="noopener noreferrer"&gt;PostgreSQL Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pinkleather221/Power-BI-End-to-End-Education-Data-Analysis-" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;If you are working on Power BI, data engineering, or analytics projects, I would love to connect and exchange ideas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/pinkleather221/" rel="noopener noreferrer"&gt;https://github.com/pinkleather221/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>datascience</category>
      <category>powerfuldevs</category>
      <category>analytics</category>
      <category>database</category>
    </item>
    <item>
      <title>How Excel is Used in Real-World Data Analysis</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Sat, 06 Jun 2026 06:57:21 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/how-excel-is-used-in-real-world-data-analysis-g1</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/how-excel-is-used-in-real-world-data-analysis-g1</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In today’s digital world, data is everywhere. Businesses, schools, hospitals and organizations collect large amounts of information every day. One of the most commonly used tools for organizing and analyzing this data is Microsoft Excel.&lt;/p&gt;

&lt;p&gt;Before starting my data analytics journey, I viewed Excel as a simple spreadsheet application mainly used for typing tables and performing calculations. However, after learning more about it during my training at LuxDevHQ, I now understand how powerful and important it is in real-world data analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is excel&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Microsoft Excel is a spreadsheet software developed by Microsoft that helps users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organize data&lt;/li&gt;
&lt;li&gt;Perform calculations&lt;/li&gt;
&lt;li&gt;Analyze information&lt;/li&gt;
&lt;li&gt;Create charts and reports&lt;/li&gt;
&lt;li&gt;Visualize trends and patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Excel is widely used because it is easy to learn, flexible, and useful in many industries.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Real-World application of excel in data analysis&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Business decision-making&lt;br&gt;
Businesses use Excel to track and analyze important information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sales performance&lt;/li&gt;
&lt;li&gt;Inventory records&lt;/li&gt;
&lt;li&gt;Employee performance&lt;/li&gt;
&lt;li&gt;Customer trends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a company can use Excel to identify which products sell the most and which products are underperforming. This helps managers make better business decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Financial Reporting and Budgeting&lt;br&gt;
Excel is heavily used in finance and accounting. Companies use it to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculate profits and losses&lt;/li&gt;
&lt;li&gt;Track expenses&lt;/li&gt;
&lt;li&gt;Prepare budgets&lt;/li&gt;
&lt;li&gt;Forecast future revenue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Functions like SUMIF and AVERAGEIF help analysts quickly calculate totals and averages based on specific conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Excel Features and Formulas I Have Learned&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So far, I have learned several useful Excel features and formulas that are commonly used in data analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Features&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Freeze Panes&lt;/em&gt;&lt;br&gt;
This feature keeps important rows or columns visible while scrolling through large datasets.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remove Duplicates&lt;/em&gt;&lt;br&gt;
This helps clean data by removing repeated information, making datasets more accurate and organized.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Formulas&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;SUMIF&lt;/em&gt;&lt;br&gt;
Used to calculate totals based on a condition.&lt;br&gt;
Example:&lt;br&gt;
Calculating total sales from a specific region.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AVERAGEIF&lt;/em&gt;&lt;br&gt;
Used to find averages that meet certain conditions.&lt;br&gt;
Example:&lt;br&gt;
Finding the average marks of students in one class.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;COUNTIFS&lt;/em&gt;&lt;br&gt;
Used to count values that meet multiple conditions.&lt;br&gt;
Example:&lt;br&gt;
Counting customers from a certain city who purchased a specific product.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;My Personal Reflection&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;At first, I thought Excel was something simple and easy. However, after attending classes and practicing different features and formulas, I now realize that Excel is a powerful analytical tool used in real-world industries.&lt;/p&gt;

&lt;p&gt;Even with the few classes I have attended so far, I already feel more confident working with data. I can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find insights from datasets&lt;/li&gt;
&lt;li&gt;Calculate totals and averages&lt;/li&gt;
&lt;li&gt;Clean messy data&lt;/li&gt;
&lt;li&gt;Visualize information clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This learning experience has made me more interested in data analytics and I look forward to learning more advanced skills in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Excel plays a major role in real-world data analysis because it helps people organize, analyze and understand data efficiently. From business reporting to financial analysis and marketing insights, Excel continues to be one of the most valuable tools in the world of data.&lt;/p&gt;

&lt;p&gt;As a beginner in data analytics, learning Excel has given me confidence and a better understanding of how data can be used to solve real-world problems.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>datascience</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Built a Cloud Platform That Does Absolutely Nothing (And It Works Perfectly)</title>
      <dc:creator>Nelly Mogere</dc:creator>
      <pubDate>Mon, 06 Apr 2026 05:31:11 +0000</pubDate>
      <link>https://dev.to/nelly_mogere_194bac0cb2ba/i-built-a-cloud-platform-that-does-absolutely-nothing-and-it-works-perfectly-45fe</link>
      <guid>https://dev.to/nelly_mogere_194bac0cb2ba/i-built-a-cloud-platform-that-does-absolutely-nothing-and-it-works-perfectly-45fe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I’ve spent a ridiculous amount of time learning cloud architecture, Kubernetes, scaling systems… all the serious stuff.&lt;br&gt;
So naturally, I decided to build something completely useless.&lt;br&gt;
I present: Cloud Architect Simulator™ a platform that successfully deploys absolutely nothing… at scale.&lt;br&gt;
You can:&lt;br&gt;
Deploy apps that don’t exist&lt;br&gt;
Auto-scale to infinity (with 0 users 💀)&lt;br&gt;
Optimize your infrastructure using AI (which deletes everything for better performance)&lt;br&gt;
Brew coffee via API (spoiler: I’m a teapot ☕)&lt;br&gt;
There’s even a region selector with options like Mars (Beta) and Some Guy’s Laptop because… why not?&lt;br&gt;
The whole idea was to poke fun at how complex and over-engineered cloud systems can get except here, all that complexity leads to absolutely no outcome.&lt;br&gt;
And somehow… it still works perfectly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;




&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pinkleather221.github.io/Cloud-Architect-Simulator-Deploying-Nothing-at-Scale-/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pinkleather221.github.io&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/gi6Ebefh_c8"&gt;
  &lt;/iframe&gt;
 &lt;br&gt;
Everything is working exactly as it shouldn’t
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pinkleather221" rel="noopener noreferrer"&gt;
        pinkleather221
      &lt;/a&gt; / &lt;a href="https://github.com/pinkleather221/Cloud-Architect-Simulator-Deploying-Nothing-at-Scale-" rel="noopener noreferrer"&gt;
        Cloud-Architect-Simulator-Deploying-Nothing-at-Scale-
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Cloud Architect Simulator™ is a fake cloud platform where you deploy apps that don’t exist, scale to infinity with zero users, and optimize everything into nothing. Built for the DEV April Fools Challenge, it’s a playful take on over-engineered systems.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Cloud-Architect-Simulator-Deploying-Nothing-at-Scale-&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Cloud Architect Simulator™ is a fake cloud platform where you deploy apps that don’t exist, scale to infinity with zero users, and optimize everything into nothing. Built for the DEV April Fools Challenge, it’s a playful take on over-engineered systems.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pinkleather221/Cloud-Architect-Simulator-Deploying-Nothing-at-Scale-" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
It’s a simple HTML, CSS and JavaScript setup nothing fancy, just pure intentional nonsense layered on top of clean structure.

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I kept the stack intentionally simple:&lt;br&gt;
HTML for structure&lt;br&gt;
CSS for styling (dark mode because we’re engineers obviously)&lt;br&gt;
Vanilla JavaScript for all the chaotic interactions&lt;br&gt;
Most of the logic is just fake state changes, random values and delayed responses to make it feel real.&lt;br&gt;
The fun part was designing interactions that look legitimate at first… and then slowly fall apart.&lt;br&gt;
For example:&lt;br&gt;
Fake deployment logs that end in “No application found”&lt;br&gt;
Billing that spikes randomly then pretends nothing happened&lt;br&gt;
AI optimization that aggressively removes everything&lt;br&gt;
It’s basically a real cloud dashboard… if it had an existential crisis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;I had to pay respect to the legendary 418 I’m a teapot energy.&lt;br&gt;
The “Brew Coffee API” feature is my tribute it goes through a whole dramatic process before ultimately refusing to cooperate because… it’s a teapot.&lt;br&gt;
It felt very on-brand for a project that commits fully to doing the wrong thing in the most elaborate way possible.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
