<?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: Faith Njenga</title>
    <description>The latest articles on DEV Community by Faith Njenga (@ms_njenga).</description>
    <link>https://dev.to/ms_njenga</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%2F3952258%2Fcdee308c-1da8-4d47-b865-37630a414717.jpeg</url>
      <title>DEV Community: Faith Njenga</title>
      <link>https://dev.to/ms_njenga</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ms_njenga"/>
    <language>en</language>
    <item>
      <title>SQL Is Surviving, Franklin: Now Rows Are Competing</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Tue, 15 Sep 2026 13:33:06 +0000</pubDate>
      <link>https://dev.to/ms_njenga/sql-is-surviving-franklin-now-rows-are-competing-4kma</link>
      <guid>https://dev.to/ms_njenga/sql-is-surviving-franklin-now-rows-are-competing-4kma</guid>
      <description>&lt;p&gt;If you have survived &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, subqueries, and CTEs, congratulations.&lt;/p&gt;

&lt;p&gt;Franklin has now discovered &lt;strong&gt;window functions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And window functions are where SQL starts doing something that feels a little suspicious.&lt;/p&gt;

&lt;p&gt;They can look at other rows &lt;strong&gt;without collapsing the rows you already have&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An employee's salary and their department's average salary&lt;/li&gt;
&lt;li&gt;A student's score and their rank&lt;/li&gt;
&lt;li&gt;A month's sales and the running total&lt;/li&gt;
&lt;li&gt;Today's sales and yesterday's sales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in the same result.&lt;/p&gt;

&lt;p&gt;Franklin has questions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Wait. SQL can look at other rows and still give me my original row?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, Franklin.&lt;/p&gt;

&lt;p&gt;Welcome to window functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, What Problem Are We Actually Solving?
&lt;/h2&gt;

&lt;p&gt;Let's start with a tiny table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;salary&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;Data&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now imagine your manager asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me every employee, their salary, and the average salary of their department."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You might think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;GROUP BY&lt;/code&gt; has entered the chat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you'd be right. Sort of.&lt;/p&gt;

&lt;p&gt;We could calculate the department averages:&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;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;avg_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;avg_salary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Great.&lt;/p&gt;

&lt;p&gt;Except...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where did the employees go?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Amina disappeared.&lt;/p&gt;

&lt;p&gt;Brian disappeared.&lt;/p&gt;

&lt;p&gt;Carol disappeared.&lt;/p&gt;

&lt;p&gt;David disappeared.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GROUP BY&lt;/code&gt; grouped the rows together to calculate one result per department.&lt;/p&gt;

&lt;p&gt;But our question wanted both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The individual employee&lt;/li&gt;
&lt;li&gt;Information about the employee's group&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where window functions walk in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet &lt;code&gt;OVER()&lt;/code&gt;: The Part That Makes the Window
&lt;/h2&gt;

&lt;p&gt;Before we start throwing &lt;code&gt;PARTITION BY&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt;, and other SQL vocabulary at Franklin, let's understand the most important piece:&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;OVER&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;OVER()&lt;/code&gt; tells SQL:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Perform this calculation across a set of rows, but keep the individual rows in the result."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;employee&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;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="n"&gt;OVER&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result could look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;th&gt;average_salary&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;80000&lt;/td&gt;
&lt;td&gt;85000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;85000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;td&gt;85000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;td&gt;85000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's break this down.&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;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Calculate the average salary."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&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;OVER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Calculate it across the rows available to this query, but don't combine those rows into one."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the big difference.&lt;/p&gt;

&lt;p&gt;Without a window function, an aggregate like &lt;code&gt;AVG()&lt;/code&gt; can give us one overall result.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;AVG() OVER()&lt;/code&gt;, we can show the average &lt;strong&gt;alongside every individual row&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of &lt;code&gt;OVER()&lt;/code&gt; as the doorway into window-function territory.&lt;/p&gt;

&lt;p&gt;Once we step inside, we can tell SQL more specifically which rows it should consider.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;PARTITION BY&lt;/code&gt;: Franklin, Pick Your Group
&lt;/h2&gt;

&lt;p&gt;What if we don't want the average salary for everyone?&lt;/p&gt;

&lt;p&gt;What if we want the average salary &lt;strong&gt;within each department&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;PARTITION BY&lt;/code&gt; comes in.&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;employee&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;salary&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="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;department&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;department_avg&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 result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;th&gt;department_avg&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;Data&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;HR&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's break down the important part:&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;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells SQL:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Separate the rows into groups based on department, but don't collapse them."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So SQL can mentally create:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&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;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;HR&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;90000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then &lt;code&gt;AVG()&lt;/code&gt; works within each group.&lt;/p&gt;

&lt;p&gt;This is one of the most important differences to remember:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;GROUP BY&lt;/code&gt; collapses rows into groups.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;PARTITION BY&lt;/code&gt; creates groups for the window calculation while keeping the original rows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Franklin can breathe again.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ORDER BY&lt;/code&gt;: Now Franklin Needs an Order
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PARTITION BY&lt;/code&gt; answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which rows belong together?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes we also need to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"In what order should SQL consider these rows?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where &lt;code&gt;ORDER BY&lt;/code&gt; inside &lt;code&gt;OVER()&lt;/code&gt; comes in.&lt;/p&gt;

&lt;p&gt;For example, suppose we want to rank employees by salary.&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;employee&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;salary&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;department&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;salary&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;salary_rank&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;Here:&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;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rank employees separately within each department."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And:&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;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Put the highest salary first."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;code&gt;OVER()&lt;/code&gt; is the container, while &lt;code&gt;PARTITION BY&lt;/code&gt; and &lt;code&gt;ORDER BY&lt;/code&gt; give SQL more instructions about the rows involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ranking: Apparently, Everyone Needs a Position
&lt;/h2&gt;

&lt;p&gt;Now let's look at three ranking functions.&lt;/p&gt;

&lt;p&gt;We'll use this table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&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;100000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ROW_NUMBER()&lt;/code&gt;: Everyone Gets a Number
&lt;/h3&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;employee&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;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;salary&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&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;Result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;th&gt;row_number&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;100000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;70000&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;Notice something important.&lt;/p&gt;

&lt;p&gt;Amina and Brian have the same salary, but they still receive different numbers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ROW_NUMBER()&lt;/code&gt; gives every row a unique position.&lt;/p&gt;

&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I don't care about ties. Just number every row."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;RANK()&lt;/code&gt;: Ties Are Allowed
&lt;/h3&gt;

&lt;p&gt;Now let's use &lt;code&gt;RANK()&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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;employee&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;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;salary&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;salary_rank&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;Result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;th&gt;salary_rank&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;100000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;70000&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;Amina and Brian both receive rank 1 because they have the same salary.&lt;/p&gt;

&lt;p&gt;But notice what happened next.&lt;/p&gt;

&lt;p&gt;There is no rank 2.&lt;/p&gt;

&lt;p&gt;The next person gets rank 3.&lt;/p&gt;

&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Ties get the same rank, and the next rank can have a gap."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;DENSE_RANK()&lt;/code&gt;: Ties Without the Gap
&lt;/h3&gt;

&lt;p&gt;Now:&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;employee&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;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;salary&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;salary_rank&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;Result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;th&gt;salary_rank&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;100000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian&lt;/td&gt;
&lt;td&gt;100000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carol&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;David&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Amina and Brian still tie for first.&lt;/p&gt;

&lt;p&gt;But this time, Carol gets rank 2.&lt;/p&gt;

&lt;p&gt;No gap.&lt;/p&gt;

&lt;p&gt;So Franklin's ranking dictionary is:&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;What it does&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 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;Ties get the same rank, and gaps can appear&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;Ties get the same rank, but no gaps appear&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three functions.&lt;/p&gt;

&lt;p&gt;Three slightly different personalities.&lt;/p&gt;

&lt;p&gt;Choose based on what the question actually asks.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;LAG()&lt;/code&gt; and &lt;code&gt;LEAD()&lt;/code&gt;: Looking Behind and Ahead
&lt;/h2&gt;

&lt;p&gt;Now suppose we have monthly sales:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And someone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How much did sales change compared with the previous month?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could try to join the table to itself.&lt;/p&gt;

&lt;p&gt;Or we could use &lt;code&gt;LAG()&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;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sales&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;sales&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="k"&gt;month&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_month&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;th&gt;previous_month&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;LAG()&lt;/code&gt; looks backward.&lt;/p&gt;

&lt;p&gt;For February, it finds January.&lt;/p&gt;

&lt;p&gt;For March, it finds February.&lt;/p&gt;

&lt;p&gt;For April, it finds March.&lt;/p&gt;

&lt;p&gt;The first row has nothing before it, so SQL gives us &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;LEAD()&lt;/code&gt; does the opposite.&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;LEAD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sales&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="k"&gt;month&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;LEAD()&lt;/code&gt; looks at the row after the current row.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LAG()&lt;/code&gt; → "What happened before me?"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LEAD()&lt;/code&gt; → "What happens after me?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Franklin is now looking into the past and the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running Totals: When &lt;code&gt;SUM()&lt;/code&gt; Gets a Memory
&lt;/h2&gt;

&lt;p&gt;Now someone asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me the sales for each month and the total sales accumulated so far."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can write:&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="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sales&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;sales&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="k"&gt;month&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;running_total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the result can be:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;th&gt;running_total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But wait.&lt;/p&gt;

&lt;p&gt;Franklin has a very reasonable question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hold on. I only wrote &lt;code&gt;SUM(sales)&lt;/code&gt; and &lt;code&gt;ORDER BY month&lt;/code&gt;. How did SQL know that February should include January, and March should include January and February?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Excellent question.&lt;/p&gt;

&lt;p&gt;There is something happening behind the scenes.&lt;/p&gt;

&lt;p&gt;When an aggregate window function such as &lt;code&gt;SUM()&lt;/code&gt; has an &lt;code&gt;ORDER BY&lt;/code&gt; but no frame explicitly specified, the database uses a &lt;strong&gt;default window frame&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For this simple example, that gives us the running-total behaviour we want.&lt;/p&gt;

&lt;p&gt;Conceptually, SQL is looking at:&lt;br&gt;
&lt;/p&gt;

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

February
→ January + February

March
→ January + February + March

April
→ January + February + March + April

May
→ January + February + March + April + May
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100
100 + 200 = 300
100 + 200 + 300 = 600
100 + 200 + 300 + 400 = 1000
100 + 200 + 300 + 400 + 500 = 1500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can make those boundaries explicit:&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="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sales&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;sales&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="k"&gt;month&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;UNBOUNDED&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;running_total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;th&gt;running_total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This version tells SQL exactly what we mean:&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="n"&gt;UNBOUNDED&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;In human language:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Start from the very first row and keep including rows until the row I'm currently calculating."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And now Franklin asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Okay... what exactly are &lt;code&gt;ROWS&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, &lt;code&gt;UNBOUNDED PRECEDING&lt;/code&gt;, and &lt;code&gt;CURRENT ROW&lt;/code&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Franklin.&lt;/p&gt;

&lt;p&gt;You just opened the window frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet the Window Frame: Franklin, We Need Boundaries
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;window&lt;/strong&gt; is the set of rows that a window function can work with.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;window frame&lt;/strong&gt; goes one step further.&lt;/p&gt;

&lt;p&gt;It tells SQL:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"From that window, these are the exact rows I want you to use for this calculation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where you might see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CURRENT ROW&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRECEDING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FOLLOWING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNBOUNDED PRECEDING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UNBOUNDED FOLLOWING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ROWS BETWEEN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They sound complicated.&lt;/p&gt;

&lt;p&gt;They're really just instructions about where to look.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;CURRENT ROW&lt;/code&gt;: Where Am I?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CURRENT ROW&lt;/code&gt; means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The row I'm currently calculating."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If SQL is calculating the result for March:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January
February
March     &amp;lt;- CURRENT ROW
April
May
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;March is the current row.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;PRECEDING&lt;/code&gt;: Look Behind Me
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PRECEDING&lt;/code&gt; means rows before the current row.&lt;/p&gt;

&lt;p&gt;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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Go back two rows."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we're on March:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January   &amp;lt;- 2 PRECEDING
February  &amp;lt;- 1 PRECEDING
March     &amp;lt;- CURRENT ROW
April
May
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;FOLLOWING&lt;/code&gt;: Look Ahead
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;FOLLOWING&lt;/code&gt; means rows after the current row.&lt;/p&gt;

&lt;p&gt;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="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;FOLLOWING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Look two rows ahead."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we're on March:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January
February
March     &amp;lt;- CURRENT ROW
April     &amp;lt;- 1 FOLLOWING
May       &amp;lt;- 2 FOLLOWING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Franklin has now learned that SQL can look backward and forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;ROWS BETWEEN&lt;/code&gt;: Tell SQL Exactly Where to Look
&lt;/h2&gt;

&lt;p&gt;Suppose we want a &lt;strong&gt;3-month moving average&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For each month, we want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Current month + the two months before it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can write:&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="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sales&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;sales&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="k"&gt;month&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;moving_avg&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's translate the important part.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Define the exact rows I want to use."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Start two rows before me."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&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;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Stop at the row I'm currently calculating."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So for March, the frame is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January   &amp;lt;- 2 PRECEDING
February  &amp;lt;- 1 PRECEDING
March     &amp;lt;- CURRENT ROW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those three rows are used to calculate March's moving average.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;th&gt;moving_avg&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that January doesn't have two previous rows.&lt;/p&gt;

&lt;p&gt;SQL doesn't invent them.&lt;/p&gt;

&lt;p&gt;It simply uses the rows that actually exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;UNBOUNDED PRECEDING&lt;/code&gt;: Start From the Beginning
&lt;/h2&gt;

&lt;p&gt;Now imagine we want a running total.&lt;/p&gt;

&lt;p&gt;Instead of saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Go back two rows."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Go all the way back to the first row."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's:&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;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can write:&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="k"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sales&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;sales&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="k"&gt;month&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;UNBOUNDED&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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;running_total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In plain English:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Start at the very first row and keep going until the row I'm currently calculating."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So when we're calculating April:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January   &amp;lt;- start here
February
March
April     &amp;lt;- CURRENT ROW
May
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SQL adds January through April.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;month&lt;/th&gt;
&lt;th&gt;sales&lt;/th&gt;
&lt;th&gt;running_total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;January&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;February&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;March&lt;/td&gt;
&lt;td&gt;300&lt;/td&gt;
&lt;td&gt;600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;April&lt;/td&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;May&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;1500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's why the running total keeps growing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;UNBOUNDED FOLLOWING&lt;/code&gt;: Go All the Way to the End
&lt;/h2&gt;

&lt;p&gt;There is also:&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;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;FOLLOWING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Keep going until the last row."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;FOLLOWING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Start at my current row and continue all the way to the end."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we're on March:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;January
February
March     &amp;lt;- CURRENT ROW
April
May       &amp;lt;- UNBOUNDED FOLLOWING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;March + April + May
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to memorize every possible combination right away.&lt;/p&gt;

&lt;p&gt;Start with the vocabulary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Franklin's Window Frame Dictionary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SQL&lt;/th&gt;
&lt;th&gt;Human translation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CURRENT ROW&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The row I'm on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1 PRECEDING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One row before me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2 PRECEDING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two rows before me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1 FOLLOWING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One row after me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2 FOLLOWING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Two rows after me&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNBOUNDED PRECEDING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start from the beginning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNBOUNDED FOLLOWING&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Go all the way to the end&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And:&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="n"&gt;A&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;basically means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use the rows from A through B for this calculation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the idea you want to carry with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;WHERE&lt;/code&gt; Comes First: Sorry, Window Functions
&lt;/h2&gt;

&lt;p&gt;Here's another rule that actually matters.&lt;/p&gt;

&lt;p&gt;Suppose we calculate employee rankings:&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;employee&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;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;salary&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;salary_rank&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;Now Franklin says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Cool. Give me only employees ranked number 1."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Naturally, he tries:&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;employee&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;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;salary&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;salary_rank&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_rank&lt;/span&gt; &lt;span class="o"&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;SQL says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Nope."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because, in the simplified logical processing order, &lt;code&gt;WHERE&lt;/code&gt; is handled before the window function result is available.&lt;/p&gt;

&lt;p&gt;A simplified order looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM
↓
WHERE
↓
GROUP BY
↓
HAVING
↓
SELECT
↓
WINDOW FUNCTIONS
↓
ORDER BY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when SQL is evaluating &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;salary_rank&lt;/code&gt; isn't available yet.&lt;/p&gt;

&lt;p&gt;The solution is to give SQL another query layer.&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_employees&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;employee&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;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;salary&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;salary_rank&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;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;ranked_employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary_rank&lt;/span&gt; &lt;span class="o"&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;The CTE calculates the rank first.&lt;/p&gt;

&lt;p&gt;Then the outer query receives that result.&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;WHERE&lt;/code&gt; can filter it.&lt;/p&gt;

&lt;p&gt;Franklin survives.&lt;/p&gt;

&lt;p&gt;Again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can Window Functions Be Nested?
&lt;/h2&gt;

&lt;p&gt;Franklin has gained confidence.&lt;/p&gt;

&lt;p&gt;Perhaps too much confidence.&lt;/p&gt;

&lt;p&gt;He tries this:&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;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;salary&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Window functions cannot be directly nested inside another window function.&lt;/p&gt;

&lt;p&gt;If you need to use the result of one window calculation in another calculation, use another query layer.&lt;/p&gt;

&lt;p&gt;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;WITH&lt;/span&gt; &lt;span class="n"&gt;ranked&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;employee&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;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;salary&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;salary_rank&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;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;employee&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;salary_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;ranked&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it this way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One query layer calculates. Another query layer uses the result.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is another place where CTEs and subqueries become useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can Window Functions Work With &lt;code&gt;GROUP BY&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;This is another common misconception.&lt;/p&gt;

&lt;p&gt;Window functions and &lt;code&gt;GROUP BY&lt;/code&gt; can absolutely appear in the same query.&lt;/p&gt;

&lt;p&gt;Suppose we first calculate the average salary for each 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="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;avg_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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have one row per department.&lt;/p&gt;

&lt;p&gt;We can rank those department averages:&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;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;avg_salary&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="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;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;department_rank&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important idea is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;GROUP BY&lt;/code&gt; determines the grouped rows first, and the window function can then work with those resulting rows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So don't memorize:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Window functions and &lt;code&gt;GROUP BY&lt;/code&gt; don't mix."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's not true.&lt;/p&gt;

&lt;p&gt;Instead, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What rows exist at the point where my window function is being calculated?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question will save you from a lot of SQL confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Franklin's Window Function Survival Guide
&lt;/h2&gt;

&lt;p&gt;Before Franklin walks away pretending he understood everything on the first try, let's recap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Need to keep individual rows?
&lt;/h3&gt;

&lt;p&gt;Think:&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;OVER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need calculations within groups?
&lt;/h3&gt;

&lt;p&gt;Think:&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;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need the rows considered in a particular order?
&lt;/h3&gt;

&lt;p&gt;Think:&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;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need rankings?
&lt;/h3&gt;

&lt;p&gt;Think:&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;ROW_NUMBER&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;DENSE_RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need the previous row?
&lt;/h3&gt;

&lt;p&gt;Think:&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;LAG&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need the next row?
&lt;/h3&gt;

&lt;p&gt;Think:&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;LEAD&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need a running total?
&lt;/h3&gt;

&lt;p&gt;Think:&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;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="p"&gt;...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need a moving calculation?
&lt;/h3&gt;

&lt;p&gt;Think:&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="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need to look backward?
&lt;/h3&gt;

&lt;p&gt;Think:&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;PRECEDING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need to look forward?
&lt;/h3&gt;

&lt;p&gt;Think:&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;FOLLOWING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need to start from the first row?
&lt;/h3&gt;

&lt;p&gt;Think:&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;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need to go to the last row?
&lt;/h3&gt;

&lt;p&gt;Think:&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;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;FOLLOWING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need to filter a window-function result?
&lt;/h3&gt;

&lt;p&gt;Don't put it directly in &lt;code&gt;WHERE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTE or subquery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Need one window function to use another window function's result?
&lt;/h3&gt;

&lt;p&gt;Don't nest them directly.&lt;/p&gt;

&lt;p&gt;Use another query layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What Exactly Did We Survive?
&lt;/h2&gt;

&lt;p&gt;Window functions aren't really about doing "complicated calculations."&lt;/p&gt;

&lt;p&gt;They're about answering questions that involve &lt;strong&gt;relationships between rows&lt;/strong&gt; without throwing those rows away.&lt;/p&gt;

&lt;p&gt;You can ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Who is the highest-paid employee in each department?"&lt;/p&gt;

&lt;p&gt;"How does this month's sales compare with last month's?"&lt;/p&gt;

&lt;p&gt;"What's the running total so far?"&lt;/p&gt;

&lt;p&gt;"What's the three-month moving average?"&lt;/p&gt;

&lt;p&gt;"Where does each student rank in their class?"&lt;/p&gt;

&lt;p&gt;"What was the previous transaction?"&lt;/p&gt;

&lt;p&gt;"What comes next?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And suddenly SQL isn't just asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What rows do you want?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"How should these rows understand each other?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the magic of window functions.&lt;/p&gt;

&lt;p&gt;Franklin came in thinking SQL only knew how to look at one row at a time.&lt;/p&gt;

&lt;p&gt;He leaves knowing SQL can look across groups, backward, forward, and through carefully defined frames.&lt;/p&gt;

&lt;p&gt;He just shouldn't try to put a window function inside another window function.&lt;/p&gt;

&lt;p&gt;SQL has boundaries.&lt;/p&gt;

&lt;p&gt;Even Franklin needs some.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;We've survived:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GROUP BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Joins&lt;/li&gt;
&lt;li&gt;Subqueries&lt;/li&gt;
&lt;li&gt;CTEs&lt;/li&gt;
&lt;li&gt;Window functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And somehow, Franklin is still here.&lt;/p&gt;

&lt;p&gt;So apparently, surviving SQL was never the finish line.&lt;/p&gt;

&lt;p&gt;It was just the beginning.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>sql</category>
    </item>
    <item>
      <title>SQL Is Surviving, Franklin: When One Query Isn't Enough</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Mon, 14 Sep 2026 04:47:42 +0000</pubDate>
      <link>https://dev.to/ms_njenga/sql-is-surviving-franklin-when-one-query-isnt-enough-20ld</link>
      <guid>https://dev.to/ms_njenga/sql-is-surviving-franklin-when-one-query-isnt-enough-20ld</guid>
      <description>&lt;p&gt;Franklin survived SQL JOINs.&lt;/p&gt;

&lt;p&gt;Barely.&lt;/p&gt;

&lt;p&gt;Last time, we made tables talk to each other. We connected students to classes, classes to teachers, and somehow convinced Franklin that duplicate rows were not a database malfunction.&lt;/p&gt;

&lt;p&gt;He was beginning to feel confident.&lt;/p&gt;

&lt;p&gt;Then someone asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which students scored above the average?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Franklin smiled.&lt;/p&gt;

&lt;p&gt;Easy.&lt;/p&gt;

&lt;p&gt;He opened his laptop and started typing.&lt;/p&gt;

&lt;p&gt;Then he stopped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wait.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is the average?&lt;/p&gt;

&lt;p&gt;He needs to calculate the average &lt;strong&gt;before&lt;/strong&gt; he can compare the students' scores to it.&lt;/p&gt;

&lt;p&gt;And suddenly, &lt;code&gt;SELECT&lt;/code&gt; doesn't feel so simple anymore.&lt;/p&gt;

&lt;p&gt;Because sometimes...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;one query isn't enough.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Welcome back to SQL survival.&lt;/p&gt;

&lt;p&gt;Today, we're talking about &lt;strong&gt;subqueries and CTEs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Previously in SQL...
&lt;/h2&gt;

&lt;p&gt;In the first article, we survived the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DDL&lt;/li&gt;
&lt;li&gt;DML&lt;/li&gt;
&lt;li&gt;DQL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;INSERT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UPDATE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then came JOINs.&lt;/p&gt;

&lt;p&gt;We learned how to combine information from multiple tables using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also learned about primary keys, foreign keys, aliases, and relationships.&lt;/p&gt;

&lt;p&gt;Basically, Franklin learned how to get tables to communicate.&lt;/p&gt;

&lt;p&gt;But now we have a different problem.&lt;/p&gt;

&lt;p&gt;Sometimes the information we need isn't simply sitting in another table.&lt;/p&gt;

&lt;p&gt;Sometimes we need to &lt;strong&gt;calculate something first&lt;/strong&gt;, then use that result to answer another question.&lt;/p&gt;

&lt;p&gt;That's where subqueries come in.&lt;/p&gt;




&lt;h2&gt;
  
  
  So... What Is a Subquery?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;subquery&lt;/strong&gt; is simply a query inside another query.&lt;/p&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;A query...&lt;/p&gt;

&lt;p&gt;inside another query.&lt;/p&gt;

&lt;p&gt;SQL looked at one query and thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You know what would make this more interesting?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another query.&lt;/p&gt;

&lt;p&gt;For example, suppose Greenwood Academy has this table:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;scores&lt;/code&gt;
&lt;/h3&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;student_name&lt;/th&gt;
&lt;th&gt;subject&lt;/th&gt;
&lt;th&gt;score&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;Brian&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Jane&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the principal asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which students scored above the average?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We could first calculate the average:&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="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose SQL gives us:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now we need:&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there's a problem.&lt;/p&gt;

&lt;p&gt;We don't want to manually type &lt;code&gt;78&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What if the data changes?&lt;/p&gt;

&lt;p&gt;What if another student gets added?&lt;/p&gt;

&lt;p&gt;What if the average becomes &lt;code&gt;79.4&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;We want SQL to calculate the average &lt;strong&gt;for us&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So we put one query inside another.&lt;/p&gt;




&lt;h2&gt;
  
  
  Our First Subquery
&lt;/h2&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look closely.&lt;/p&gt;

&lt;p&gt;We have:&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="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;inside:&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;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 inner query calculates the average.&lt;/p&gt;

&lt;p&gt;The outer query finds students whose scores are higher than that average.&lt;/p&gt;

&lt;p&gt;So conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              INNER QUERY
                   ↓
          Calculate the average
                   ↓
                 78
                   ↓
              OUTER QUERY
                   ↓
       Find scores greater than 78
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a subquery.&lt;/p&gt;

&lt;p&gt;Franklin has officially taught SQL to ask itself a question.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Does SQL Execute This?
&lt;/h2&gt;

&lt;p&gt;At a simplified level, you can think of it like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1
&lt;/h3&gt;

&lt;p&gt;SQL runs:&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="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;SQL effectively uses that result here:&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;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3
&lt;/h3&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;th&gt;score&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;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And Franklin looks at the result like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Oh."&lt;/p&gt;

&lt;p&gt;"That actually worked."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Subqueries in &lt;code&gt;WHERE&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the most common places you'll see a subquery is inside a &lt;code&gt;WHERE&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;Let's try another example.&lt;/p&gt;

&lt;p&gt;Suppose we want students who scored the &lt;strong&gt;highest score&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we could find the highest score:&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="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose the result is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then we could find the student:&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;91&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But again, we can let SQL do both jobs:&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&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;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&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 finds the highest score.&lt;/p&gt;

&lt;p&gt;The outer query finds the student who achieved it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Inner Query vs The Outer Query
&lt;/h2&gt;

&lt;p&gt;This terminology is worth remembering.&lt;/p&gt;

&lt;p&gt;In:&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&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 inside the parentheses is the:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;subquery / inner query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The query surrounding it is the:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;outer query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of it like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OUTER QUERY
┌───────────────────────────────┐
│                               │
│   WHERE score &amp;gt;               │
│          ↓                    │
│      ┌───────────────┐        │
│      │  SUBQUERY     │        │
│      │               │        │
│      │ AVG(score)    │        │
│      └───────────────┘        │
│                               │
└───────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A query inside a query.&lt;/p&gt;

&lt;p&gt;SQL nesting dolls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Subqueries With &lt;code&gt;IN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Subqueries aren't limited to returning one value.&lt;/p&gt;

&lt;p&gt;Sometimes the inner query returns &lt;strong&gt;multiple values&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;IN&lt;/code&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;Suppose we have:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;students&lt;/code&gt;
&lt;/h3&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;student_name&lt;/th&gt;
&lt;th&gt;class_id&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;Brian&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;classes&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;class_id&lt;/th&gt;
&lt;th&gt;class_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;Form 4A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Form 3B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Form 2C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now imagine we want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me students who are in Form 4A or Form 3B."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The inner query can find the relevant class IDs:&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;class_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;class_name&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 4A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 3B'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives us:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then the outer query can use those values:&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;student_name&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;class_id&lt;/span&gt; &lt;span class="k"&gt;IN&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;class_id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;class_name&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 4A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 3B'&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 subquery returns multiple values:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And the outer query asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is this student's &lt;code&gt;class_id&lt;/code&gt; in that list?"&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;IN&lt;/code&gt; vs &lt;code&gt;=&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is an important distinction.&lt;/p&gt;

&lt;p&gt;If your subquery returns &lt;strong&gt;one value&lt;/strong&gt;, you might use:&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="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&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;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if your subquery can return &lt;strong&gt;multiple values&lt;/strong&gt;, you can use:&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;IN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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;WHERE&lt;/span&gt; &lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="k"&gt;IN&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;class_id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;class_name&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 4A'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Form 3B'&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;Think:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=    → I expect one value

IN   → I can work with a list of values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this distinction matters.&lt;/p&gt;

&lt;p&gt;If your subquery returns multiple rows and you use &lt;code&gt;=&lt;/code&gt;, SQL may complain.&lt;/p&gt;

&lt;p&gt;And Franklin does not need another reason to panic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Subqueries in &lt;code&gt;FROM&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Here's where things get a little more interesting.&lt;/p&gt;

&lt;p&gt;A subquery can also appear inside the &lt;code&gt;FROM&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&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="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the subquery creates a temporary result that the outer query can work with.&lt;/p&gt;

&lt;p&gt;You can think of it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Run this query first, then treat its result like a table."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, suppose we want students who scored above 80.&lt;/p&gt;

&lt;p&gt;We could create a temporary 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="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use it:&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="o"&gt;*&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="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&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="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;high_scorers&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;AS high_scorers&lt;/code&gt; part gives the temporary result a name.&lt;/p&gt;

&lt;p&gt;That's called an &lt;strong&gt;alias&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And yes, SQL is once again asking you to name things.&lt;/p&gt;

&lt;p&gt;SQL loves naming things.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Would We Do That?
&lt;/h2&gt;

&lt;p&gt;You might be wondering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why not just write the original query?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good question.&lt;/p&gt;

&lt;p&gt;For a simple example like this, there isn't much benefit.&lt;/p&gt;

&lt;p&gt;The point is to understand the concept.&lt;/p&gt;

&lt;p&gt;Subqueries in &lt;code&gt;FROM&lt;/code&gt; become more useful when the inner query performs some calculation or transformation that the outer query needs to work with.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;class_id&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;score&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_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine we want to work with those class averages in another query.&lt;/p&gt;

&lt;p&gt;A subquery can give us that intermediate result.&lt;/p&gt;

&lt;p&gt;This is where queries can start getting complicated.&lt;/p&gt;

&lt;p&gt;And eventually, Franklin starts staring at his screen like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Who wrote this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He did.&lt;/p&gt;

&lt;p&gt;Three hours ago.&lt;/p&gt;




&lt;h2&gt;
  
  
  Subqueries in &lt;code&gt;SELECT&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Yes.&lt;/p&gt;

&lt;p&gt;We can even put a subquery inside the &lt;code&gt;SELECT&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&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;score&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;scores&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_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can produce something like:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;th&gt;score&lt;/th&gt;
&lt;th&gt;average_score&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;85&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jane&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The average is calculated by the subquery and displayed alongside each student.&lt;/p&gt;

&lt;p&gt;Now, for a simple example like this, there are often better ways to solve the problem.&lt;/p&gt;

&lt;p&gt;But it's useful to understand that a subquery can appear in different parts of a SQL statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then Things Get Complicated
&lt;/h2&gt;

&lt;p&gt;Subqueries are powerful.&lt;/p&gt;

&lt;p&gt;But you can absolutely take them too far.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt; &lt;span class="p"&gt;...&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="p"&gt;...&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="p"&gt;...&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="p"&gt;...&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;something&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;something_else&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;another_thing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some point, Franklin isn't writing SQL anymore.&lt;/p&gt;

&lt;p&gt;He's opening a portal.&lt;/p&gt;

&lt;p&gt;This is one reason readability matters.&lt;/p&gt;

&lt;p&gt;A query can be technically correct and still be painful for another human being to understand.&lt;/p&gt;

&lt;p&gt;And that brings us to our next character.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter: The CTE
&lt;/h2&gt;

&lt;p&gt;CTE stands for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Table Expression&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't let the name scare you.&lt;/p&gt;

&lt;p&gt;A CTE is essentially a named temporary result that you define &lt;strong&gt;before&lt;/strong&gt; the main query.&lt;/p&gt;

&lt;p&gt;You create it using:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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;WITH&lt;/span&gt; &lt;span class="n"&gt;average_score&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;score&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;average_score&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, that might look like more SQL than our original solution.&lt;/p&gt;

&lt;p&gt;And technically, it is.&lt;/p&gt;

&lt;p&gt;But the structure is much easier to understand.&lt;/p&gt;

&lt;p&gt;We're saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"First, calculate the average score."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Now use that result to find students who scored above it."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Breaking Down a CTE
&lt;/h2&gt;

&lt;p&gt;Let's separate the pieces.&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;average_score&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;score&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a CTE called:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Inside it, we calculate:&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;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and give that result the name:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then comes the main 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="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;average_score&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH
   ↓
Create a temporary named result
   ↓
average_score
   ↓
Use it in the main query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's much easier to read when your query becomes more complicated.&lt;/p&gt;




&lt;h2&gt;
  
  
  CTEs Are Like Giving Your Query Steps
&lt;/h2&gt;

&lt;p&gt;This is probably the easiest way to think about them.&lt;/p&gt;

&lt;p&gt;Instead of writing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"SQL, do all of this giant thing and good luck."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"First, do this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Now do this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Finally, give me this."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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;WITH&lt;/span&gt; &lt;span class="n"&gt;average_score&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;score&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;average_score&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 now has a visible structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1 → Calculate average

Step 2 → Compare student scores to average

Step 3 → Return students above average
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Franklin can breathe again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple CTEs
&lt;/h2&gt;

&lt;p&gt;Now we're getting somewhere.&lt;/p&gt;

&lt;p&gt;You can define more than one CTE.&lt;/p&gt;

&lt;p&gt;Suppose Greenwood Academy wants to calculate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The average score for each class&lt;/li&gt;
&lt;li&gt;Then find classes whose average is above 80 as the top students&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We could start with:&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;class_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;class_id&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;score&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_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;class_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;class_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;average_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;class_averages&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;average_score&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CTE handles the first task:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The main query handles the second:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find averages above 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where CTEs start becoming really useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple CTEs Can Build on Each Other
&lt;/h2&gt;

&lt;p&gt;You can also have one CTE build on another.&lt;/p&gt;

&lt;p&gt;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;WITH&lt;/span&gt; &lt;span class="n"&gt;student_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;student_id&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;score&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_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_id&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;top_students&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;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;average_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;student_averages&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;average_score&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="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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;top_students&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at what happened.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;calculates each student's average.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;uses that result.&lt;/p&gt;

&lt;p&gt;Then the final query retrieves the students we want.&lt;/p&gt;

&lt;p&gt;This is much easier to follow than trying to cram everything into one enormous statement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Subquery vs CTE
&lt;/h2&gt;

&lt;p&gt;Now comes the question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"So which one should I use?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"CTEs are always better."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That would be too easy.&lt;/p&gt;

&lt;p&gt;Both have their place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a subquery when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;the logic is simple&lt;/li&gt;
&lt;li&gt;the result is only needed once&lt;/li&gt;
&lt;li&gt;the query remains readable&lt;/li&gt;
&lt;li&gt;you don't need to reuse the intermediate result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's perfectly reasonable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a CTE when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;the query has multiple logical steps&lt;/li&gt;
&lt;li&gt;you want to make the query easier to read&lt;/li&gt;
&lt;li&gt;an intermediate result is reused&lt;/li&gt;
&lt;li&gt;the query is becoming difficult to understand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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;WITH&lt;/span&gt; &lt;span class="n"&gt;student_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;student_id&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;score&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_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_id&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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;student_averages&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;average_score&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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Always use CTEs."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Choose the structure that makes your logic easiest to understand and maintain.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A Realistic Greenwood Academy Problem
&lt;/h2&gt;

&lt;p&gt;Let's give Franklin an actual assignment.&lt;/p&gt;

&lt;p&gt;The principal walks in and says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find all students whose average score is higher than the overall average score."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we're dealing with two different calculations.&lt;/p&gt;

&lt;p&gt;We need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each student's average&lt;/li&gt;
&lt;li&gt;The overall average&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then we compare them.&lt;/p&gt;

&lt;p&gt;Franklin starts sweating.&lt;/p&gt;

&lt;p&gt;But we can break it down.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Calculate each student's average
&lt;/h3&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;student_id&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;score&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;student_average&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_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 gives us something like:&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;student_average&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;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Step 2: Calculate the overall average
&lt;/h3&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="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&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;scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose the result is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now we need to compare the two.&lt;/p&gt;

&lt;p&gt;This is where a CTE makes the logic easier to follow.&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;student_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;student_id&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;score&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;student_average&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_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;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;student_average&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;student_averages&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;student_average&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me students whose average is greater than the overall average."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a much more interesting question than simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Who scored above 80?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this is the kind of problem where subqueries and CTEs start becoming genuinely useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Correlated Subqueries?
&lt;/h2&gt;

&lt;p&gt;Now we're stepping into slightly more advanced territory.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;correlated subquery&lt;/strong&gt; is a subquery that depends on a value from the outer query.&lt;/p&gt;

&lt;p&gt;Unlike our earlier subqueries, the inner query isn't completely independent.&lt;/p&gt;

&lt;p&gt;For example, imagine we have:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;employees&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;employee_id&lt;/th&gt;
&lt;th&gt;employee_name&lt;/th&gt;
&lt;th&gt;department_id&lt;/th&gt;
&lt;th&gt;salary&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;Brian&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;95000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;85000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find employees earning more than the average salary in their own department."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now the average depends on the employee's department.&lt;/p&gt;

&lt;p&gt;We could write:&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;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_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;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="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;e2&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;e2&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e2&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;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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice this:&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;WHERE&lt;/span&gt; &lt;span class="n"&gt;e2&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inner query refers to the outer query's &lt;code&gt;e.department_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's what makes it &lt;strong&gt;correlated&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The inner query needs information from the current row of the outer query.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Correlated Subqueries Matter
&lt;/h2&gt;

&lt;p&gt;The previous example asks a more specific question.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Who earns more than the average salary?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Who earns more than the average salary &lt;strong&gt;for their own department&lt;/strong&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's an important difference.&lt;/p&gt;

&lt;p&gt;The comparison changes depending on the employee.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Brian
  ↓
Find average salary in Brian's department
  ↓
Compare Brian's salary

Mercy
  ↓
Find average salary in Mercy's department
  ↓
Compare Mercy's salary

Kevin
  ↓
Find average salary in Kevin's department
  ↓
Compare Kevin's salary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The subquery is connected to the current row.&lt;/p&gt;

&lt;p&gt;That's the idea behind correlation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Don't Use a CTE Just Because You Can
&lt;/h2&gt;

&lt;p&gt;Now that you've discovered CTEs, you may feel tempted to put &lt;code&gt;WITH&lt;/code&gt; before everything.&lt;/p&gt;

&lt;p&gt;Please don't.&lt;/p&gt;

&lt;p&gt;This:&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;students&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="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="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="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;doesn't make you look advanced.&lt;/p&gt;

&lt;p&gt;It just makes Franklin tired.&lt;/p&gt;

&lt;p&gt;If a simple query solves the problem clearly, use the simple query.&lt;/p&gt;

&lt;p&gt;The goal isn't to use the most complicated SQL possible.&lt;/p&gt;

&lt;p&gt;The goal is to write SQL that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;correct&lt;/li&gt;
&lt;li&gt;readable&lt;/li&gt;
&lt;li&gt;maintainable&lt;/li&gt;
&lt;li&gt;appropriate for the problem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fancy SQL is not automatically good SQL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Subquery Mistakes
&lt;/h2&gt;

&lt;p&gt;Let's talk about the ways Franklin can still suffer.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Returning multiple rows when one value is expected
&lt;/h3&gt;

&lt;p&gt;Suppose you write:&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;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&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;score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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 may return multiple scores.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;=&lt;/code&gt; expects one value.&lt;/p&gt;

&lt;p&gt;That's a problem.&lt;/p&gt;

&lt;p&gt;If the subquery returns multiple values, you might need something like:&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;IN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Forgetting the alias
&lt;/h3&gt;

&lt;p&gt;When using a subquery in &lt;code&gt;FROM&lt;/code&gt;, you generally need to give the derived table an alias.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&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="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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;student_scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;is the alias.&lt;/p&gt;

&lt;p&gt;Don't leave your temporary result wandering around without a name.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Making the query impossible to read
&lt;/h3&gt;

&lt;p&gt;This is probably the biggest danger.&lt;/p&gt;

&lt;p&gt;You can technically nest queries inside queries inside queries.&lt;/p&gt;

&lt;p&gt;But just because SQL lets you doesn't mean you should.&lt;/p&gt;

&lt;p&gt;If your query looks like something Franklin wrote at 3:47 a.m. with one eye open...&lt;/p&gt;

&lt;p&gt;stop.&lt;/p&gt;

&lt;p&gt;Consider breaking the logic into a CTE.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Forgetting what your subquery returns
&lt;/h3&gt;

&lt;p&gt;Before using a subquery, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does it return one value?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does it return multiple rows?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does it return an entire table-like result?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That determines how you can use it.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One value
→ =, &amp;gt;, &amp;lt;, etc.

Multiple values
→ IN, ANY, ALL, etc.

Table-like result
→ FROM / JOIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding the shape of your result is just as important as writing the syntax.&lt;/p&gt;




&lt;h2&gt;
  
  
  Subqueries vs JOINs
&lt;/h2&gt;

&lt;p&gt;This is where people often get confused.&lt;/p&gt;

&lt;p&gt;You might look at a problem and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Should I use a JOIN or a subquery?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer depends on what you're trying to accomplish.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;JOIN&lt;/strong&gt; is primarily about combining related data from different tables.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&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;class_name&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;AS&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="o"&gt;=&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;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're bringing related information together.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;subquery&lt;/strong&gt; is often about using the result of one query to help answer another question.&lt;/p&gt;

&lt;p&gt;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;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're using one calculation to filter another result.&lt;/p&gt;

&lt;p&gt;So think:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JOIN
→ "Bring these related tables together."

SUBQUERY
→ "Use this query's result to answer another question."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes there are multiple valid ways to solve the same problem.&lt;/p&gt;

&lt;p&gt;That's normal.&lt;/p&gt;

&lt;p&gt;SQL gives you options.&lt;/p&gt;

&lt;p&gt;Franklin gives himself headaches by trying all of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Mental Model
&lt;/h2&gt;

&lt;p&gt;When you see a SQL question, ask yourself:&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need information from another table?
&lt;/h3&gt;

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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Do I need the result of another query?
&lt;/h3&gt;

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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is the logic becoming a sequence of steps?
&lt;/h3&gt;

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

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

&lt;/div&gt;



&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show students and their class names."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Show students who scored above the average."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"Calculate student averages, filter them, then compare them with another result."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CTE + possibly a subquery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That mental distinction will save you a lot of confusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Franklin's Survival Guide
&lt;/h2&gt;

&lt;p&gt;Let's make this painfully simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subquery
&lt;/h3&gt;

&lt;p&gt;A query inside another 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="k"&gt;SELECT&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;score&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;scores&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One query needs another query's answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;IN&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Useful when the subquery returns multiple values.&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;WHERE&lt;/span&gt; &lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="k"&gt;IN&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;class_id&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Is my value somewhere in this result?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  CTE
&lt;/h3&gt;

&lt;p&gt;A named temporary result created with &lt;code&gt;WITH&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="n"&gt;average_scores&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;score&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;avg_score&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;scores&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="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;average_scores&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Let's give this step a name.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Correlated subquery
&lt;/h3&gt;

&lt;p&gt;A subquery that depends on the outer query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Outer row
   ↓
Inner query uses information from that row
   ↓
Result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The inner query needs to know what the outer query is currently looking at.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;So...&lt;/p&gt;

&lt;p&gt;Franklin can now make tables talk.&lt;/p&gt;

&lt;p&gt;He can JOIN them.&lt;/p&gt;

&lt;p&gt;He can filter them.&lt;/p&gt;

&lt;p&gt;And apparently, he can now make SQL &lt;strong&gt;ask itself questions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We've gone from:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    ...
WHERE something &amp;gt; (
    SELECT ...
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH something AS (
    SELECT ...
)
SELECT ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is a pretty big leap from where we started.&lt;/p&gt;

&lt;p&gt;But the most important thing isn't memorizing every variation of subqueries and CTEs.&lt;/p&gt;

&lt;p&gt;It's learning how to break a problem down.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What do I need first?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What do I need to do with that result?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That little habit will take you surprisingly far.&lt;/p&gt;

&lt;p&gt;Because SQL isn't really about memorizing hundreds of commands.&lt;/p&gt;

&lt;p&gt;It's about learning how to turn a question into a series of smaller questions...&lt;/p&gt;

&lt;p&gt;and then convincing the database to answer them.&lt;/p&gt;

&lt;p&gt;Franklin is beginning to understand.&lt;/p&gt;

&lt;p&gt;He's no longer just surviving SQL.&lt;/p&gt;

&lt;p&gt;He's starting to &lt;strong&gt;think in SQL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Which is great.&lt;/p&gt;

&lt;p&gt;Until someone introduces him to window functions.&lt;/p&gt;

&lt;p&gt;Because apparently, the rows need to start competing now.&lt;/p&gt;

&lt;p&gt;But that's a problem for another day. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SQL Is Surviving, Franklin: When Tables Start Talking</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Tue, 08 Sep 2026 13:09:53 +0000</pubDate>
      <link>https://dev.to/ms_njenga/sql-is-surviving-franklin-when-tables-start-talking-3oom</link>
      <guid>https://dev.to/ms_njenga/sql-is-surviving-franklin-when-tables-start-talking-3oom</guid>
      <description>&lt;p&gt;So, you survived the basics of SQL.&lt;/p&gt;

&lt;p&gt;You learned how to create tables, insert data, retrieve records, update rows, and hopefully avoid accidentally updating the entire database.&lt;/p&gt;

&lt;p&gt;Because if you’ve ever run a &lt;code&gt;DELETE&lt;/code&gt; statement without a &lt;code&gt;WHERE&lt;/code&gt; clause and survived the experience, congratulations. You’ve already developed character.&lt;/p&gt;

&lt;p&gt;But eventually, SQL gives you another problem: &lt;strong&gt;“Cool. Now get information from multiple tables.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And suddenly, &lt;code&gt;SELECT * FROM students;&lt;/code&gt; doesn’t feel so powerful anymore.&lt;/p&gt;

&lt;p&gt;Welcome to JOINs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Previously in SQL…
&lt;/h2&gt;

&lt;p&gt;In Part 1, we looked at the basic categories of SQL commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DDL&lt;/strong&gt; - Data Definition Language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DML&lt;/strong&gt; - Data Manipulation Language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DQL&lt;/strong&gt; - Data Query Language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We also worked with individual tables. But real databases rarely keep everything in one giant table.&lt;/p&gt;

&lt;p&gt;Imagine trying to store an entire school database in one table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;th&gt;class&lt;/th&gt;
&lt;th&gt;teacher&lt;/th&gt;
&lt;th&gt;subject&lt;/th&gt;
&lt;th&gt;score&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;Form 4A&lt;/td&gt;
&lt;td&gt;Mr. Kamau&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;Form 4A&lt;/td&gt;
&lt;td&gt;Mr. Kamau&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;Form 3B&lt;/td&gt;
&lt;td&gt;Ms. Achieng&lt;/td&gt;
&lt;td&gt;Biology&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At first, this looks convenient. Until you realize that you’re repeating class, teacher, subject, and student information over and over again.&lt;/p&gt;

&lt;p&gt;That’s where relational databases come in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Do We Have Multiple Tables?
&lt;/h2&gt;

&lt;p&gt;A good database tries to avoid unnecessary repetition. Instead of putting everything into one massive table, we separate related information.&lt;/p&gt;

&lt;p&gt;Let’s say our school is called &lt;strong&gt;Greenwood Academy&lt;/strong&gt;. We could split our data into three clean tables:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;students&lt;/code&gt;
&lt;/h3&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;student_name&lt;/th&gt;
&lt;th&gt;class_id&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;Brian&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;classes&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;class_id&lt;/th&gt;
&lt;th&gt;class_name&lt;/th&gt;
&lt;th&gt;teacher_id&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;Form 4A&lt;/td&gt;
&lt;td&gt;501&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Form 3B&lt;/td&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;teachers&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;teacher_id&lt;/th&gt;
&lt;th&gt;teacher_name&lt;/th&gt;
&lt;th&gt;subject&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;501&lt;/td&gt;
&lt;td&gt;Mr. Kamau&lt;/td&gt;
&lt;td&gt;Mathematics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;502&lt;/td&gt;
&lt;td&gt;Ms. Achieng&lt;/td&gt;
&lt;td&gt;Biology&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the information is nicely separated. But there’s a problem. If someone asks: &lt;em&gt;“Show me each student’s name, their class, and their teacher.”&lt;/em&gt; - no single table contains all three pieces of information.&lt;/p&gt;

&lt;p&gt;So how do we bring them together? You guessed it. &lt;strong&gt;JOINs&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before JOINs: Primary Keys and Foreign Keys
&lt;/h2&gt;

&lt;p&gt;Before we start joining tables like we’re collecting Pokémon, we need to understand what connects them. That connection usually comes from &lt;strong&gt;primary keys&lt;/strong&gt; and &lt;strong&gt;foreign keys&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primary Key
&lt;/h3&gt;

&lt;p&gt;A primary key uniquely identifies each record in a table. For example, &lt;code&gt;student_id&lt;/code&gt; might uniquely identify every student. No two students should ever share the same &lt;code&gt;student_id&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Foreign Key
&lt;/h3&gt;

&lt;p&gt;A foreign key is a column that refers to a primary key in another table. &lt;/p&gt;

&lt;p&gt;For example, look at the relationship between our &lt;code&gt;students&lt;/code&gt; and &lt;code&gt;classes&lt;/code&gt; tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;students                 classes
-----------              -----------
student_id               class_id  &amp;lt;--- (Primary Key)
student_name                ↑
class_id    ────────────────┘ 
   │
   └─&amp;gt; (Foreign Key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the exact relationship SQL uses to connect the tables. Think of it as the database saying: &lt;em&gt;“I don’t have the whole story here, but I know exactly where the rest of it lives.”&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  So… What Exactly Is a JOIN?
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;JOIN&lt;/code&gt; allows you to combine rows from two or more tables based on a related 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;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re saying: &lt;em&gt;“Take the students table, find the class that belongs to each student, and bring the information together side-by-side.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The magic happens right here: &lt;code&gt;ON students.class_id = classes.class_id&lt;/code&gt;. That is the structural bridge we are using to link them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Meet the JOIN Family
&lt;/h2&gt;

&lt;p&gt;SQL has several types of JOINs. The main ones you’ll encounter are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s break down how they behave differently.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. INNER JOIN
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;INNER JOIN&lt;/code&gt; returns only the records that have a match in &lt;strong&gt;both&lt;/strong&gt; tables. Think of it as: &lt;em&gt;“Only show me people who are actually on both guest lists.”&lt;/em&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;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine our dataset looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Students Table:&lt;/strong&gt;&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;student_name&lt;/th&gt;
&lt;th&gt;class_id&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;Brian&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;999&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classes Table:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;class_id&lt;/th&gt;
&lt;th&gt;class_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;Form 4A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Form 3B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sarah’s &lt;code&gt;class_id&lt;/code&gt; is &lt;code&gt;999&lt;/code&gt;. Since there is no class with a &lt;code&gt;class_id&lt;/code&gt; of &lt;code&gt;999&lt;/code&gt; in the classes table, Sarah disappears from the results entirely. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;INNER JOIN&lt;/code&gt; basically says: &lt;em&gt;“If we can’t find a perfect match, drop the row.”&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. LEFT JOIN
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; returns &lt;strong&gt;all records from the left table&lt;/strong&gt;, and the matching records from the right table. If there is no match, SQL fills the right-side columns with &lt;code&gt;NULL&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;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the same data from before, Sarah would now appear in our results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;student_name&lt;/th&gt;
&lt;th&gt;class_name&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;Form 4A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mercy&lt;/td&gt;
&lt;td&gt;Form 4A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kevin&lt;/td&gt;
&lt;td&gt;Form 3B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sarah&lt;/td&gt;
&lt;td&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because the &lt;code&gt;students&lt;/code&gt; table is written first (on the left side of the JOIN keyword), the &lt;code&gt;LEFT JOIN&lt;/code&gt; mandates: &lt;em&gt;“I don’t care if you find a match or not. Everybody from my table is coming along.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is incredibly useful when you want to look for missing or unlinked records. 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;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are specifically filtering for students who don’t have a matching class. SQL has officially entered detective mode.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. RIGHT JOIN
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt; is the exact opposite of &lt;code&gt;LEFT JOIN&lt;/code&gt;. It keeps all records from the right table, even if there isn’t a matching record in the left table.&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;students&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_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 states: &lt;em&gt;“Every class stays. If a student belongs to it, bring them along. If a class has zero students, display the class name anyway and fill the student column with &lt;code&gt;NULL&lt;/code&gt;.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That said, &lt;code&gt;RIGHT JOIN&lt;/code&gt; isn’t used as often in the real world. Why? Because you can always rewrite it as a &lt;code&gt;LEFT JOIN&lt;/code&gt; simply by flipping the order of your tables in the 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="k"&gt;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&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;students&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same idea, different direction. Keeping everything as a &lt;code&gt;LEFT JOIN&lt;/code&gt; usually makes scripts much easier to read from top to bottom.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. FULL OUTER JOIN
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; goes completely all-in. It keeps matching records, unmatched records from the left table, &lt;strong&gt;and&lt;/strong&gt; unmatched records from the right table.&lt;/p&gt;

&lt;p&gt;Essentially: &lt;em&gt;“Bring everybody to the party. We’ll figure out the missing pieces later.”&lt;/em&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;SELECT&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_name&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;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;
    &lt;span class="k"&gt;ON&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;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_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 query will simultaneously surface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Students assigned to classes.&lt;/li&gt;
&lt;li&gt;Students without classes (like Sarah).&lt;/li&gt;
&lt;li&gt;Dynamic classes that currently have no students assigned to them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; Not every database system supports &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; directly out of the box. For example, MySQL does not support it natively (you have to fake it using a &lt;code&gt;UNION&lt;/code&gt; of a Left and Right Join), whereas engines like PostgreSQL do. Always verify which database system you're building for.&lt;/p&gt;




&lt;h2&gt;
  
  
  JOIN Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;If you forget everything else, keep this quick mental framework handy:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JOIN Type&lt;/th&gt;
&lt;th&gt;What does it keep?&lt;/th&gt;
&lt;th&gt;Quick Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only matching records&lt;/td&gt;
&lt;td&gt;The intersection only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything from the left table + matches&lt;/td&gt;
&lt;td&gt;Left table gets full VIP treatment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything from the right table + matches&lt;/td&gt;
&lt;td&gt;Right table gets full VIP treatment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything from both tables&lt;/td&gt;
&lt;td&gt;Universal invite list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The ON Clause: Where the Magic Happens
&lt;/h2&gt;

&lt;p&gt;You’ve probably noticed this piece of syntax repeating: &lt;code&gt;ON students.class_id = classes.class_id&lt;/code&gt;.&lt;br&gt;
The &lt;code&gt;ON&lt;/code&gt; clause tells SQL exactly how the tables are related.&lt;/p&gt;


&lt;h2&gt;
  
  
  JOINing Three Tables
&lt;/h2&gt;

&lt;p&gt;What if we want to bridge all three of our tables together to find out which &lt;strong&gt;teacher&lt;/strong&gt; is teaching which &lt;strong&gt;student&lt;/strong&gt;? &lt;/p&gt;

&lt;p&gt;You aren't limited to joining just two tables at a time. You can chain JOIN statements sequentially to build broader horizons. SQL will process them linearly, using the cumulative dataset from the first join to connect to the next table.&lt;/p&gt;

&lt;p&gt;Here is how you link &lt;code&gt;students&lt;/code&gt; to &lt;code&gt;classes&lt;/code&gt;, and then connect those &lt;code&gt;classes&lt;/code&gt; to their respective &lt;code&gt;teachers&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;SELECT&lt;/span&gt; 
&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;student_name&lt;/span&gt;&lt;span class="p"&gt;,&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;class_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;teacher_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;subject&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;sINNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="n"&gt;cON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="o"&gt;=&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;class_idINNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;teachers&lt;/span&gt; &lt;span class="n"&gt;tON&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;teacher_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;teacher_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pro-Tip: Table Aliasing
&lt;/h3&gt;

&lt;p&gt;Notice the letters s, c, and t right after the table names? Those are aliases. They save you from having to type out long table names like students. student_name repeatedly. By declaring FROM students s, you tell SQL: "For the rest of this query, I'll just use s as a shorthand for this table." It keeps your multi-table joins beautifully organized and readable!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Surviving SQL Is Winning, Franklin</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Mon, 27 Jul 2026 08:28:43 +0000</pubDate>
      <link>https://dev.to/ms_njenga/surviving-sql-is-winning-franklin-25kn</link>
      <guid>https://dev.to/ms_njenga/surviving-sql-is-winning-franklin-25kn</guid>
      <description>&lt;p&gt;I was given a PostgreSQL assignment.&lt;/p&gt;

&lt;p&gt;The brief said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are now the DBA for Greenwood Academy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No handover meeting. No existing documentation. No senior DBA saying, “Here is what happened last quarter.”&lt;/p&gt;

&lt;p&gt;Just a school, some messy data, and a list of tasks.&lt;/p&gt;

&lt;p&gt;So I opened &lt;strong&gt;DBeaver&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For anyone new to this: I was using DBeaver to connect to &lt;strong&gt;PostgreSQL&lt;/strong&gt; and run my SQL queries. PostgreSQL is the database system. DBeaver is the tool I used to work with it.&lt;/p&gt;

&lt;p&gt;I wrote the instructions in DBeaver.&lt;/p&gt;

&lt;p&gt;PostgreSQL did the actual work.&lt;/p&gt;

&lt;p&gt;And, as I would soon discover, PostgreSQL was very committed to doing exactly what I told it to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Has Departments
&lt;/h2&gt;

&lt;p&gt;Before touching Greenwood Academy, I needed to understand the different types of SQL commands.&lt;/p&gt;

&lt;p&gt;SQL commands are grouped according to what they do. Think of them as different departments in the same organisation. They all work with the database, but they have different jobs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Full Name&lt;/th&gt;
&lt;th&gt;Main Job&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DDL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data Definition Language&lt;/td&gt;
&lt;td&gt;Creates and changes database structures&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;, &lt;code&gt;TRUNCATE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data Manipulation Language&lt;/td&gt;
&lt;td&gt;Adds, changes, and removes data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data Query Language&lt;/td&gt;
&lt;td&gt;Retrieves data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DCL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data Control Language&lt;/td&gt;
&lt;td&gt;Manages permissions and access&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GRANT&lt;/code&gt;, &lt;code&gt;REVOKE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TCL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transaction Control Language&lt;/td&gt;
&lt;td&gt;Manages database transactions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COMMIT&lt;/code&gt;, &lt;code&gt;ROLLBACK&lt;/code&gt;, &lt;code&gt;SAVEPOINT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The quick version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DDL → Build the structure
DML → Change the data
DQL → Ask questions
DCL → Control access
TCL → Decide whether changes stay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will not use every command in this article. Greenwood Academy did not require me to manage database permissions or recover from a transaction disaster.&lt;/p&gt;

&lt;p&gt;This assignment focused mainly on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DDL → CREATE, ALTER
DML → INSERT, UPDATE, DELETE
DQL → SELECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also use &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;AND&lt;/code&gt;, &lt;code&gt;OR&lt;/code&gt;, &lt;code&gt;BETWEEN&lt;/code&gt;, &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;NOT IN&lt;/code&gt;, &lt;code&gt;LIKE&lt;/code&gt;, &lt;code&gt;COUNT(*)&lt;/code&gt;, and &lt;code&gt;CASE WHEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, with the departments introduced, it was time to start with the construction department.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION A: DDL - Building Greenwood Academy
&lt;/h2&gt;

&lt;p&gt;DDL stands for &lt;strong&gt;Data Definition Language&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;DDL is about the structure of the database.&lt;/p&gt;

&lt;p&gt;Before asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which students are in Form 3?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we need somewhere to store the students.&lt;/p&gt;

&lt;p&gt;Before storing exam results, we need somewhere to put the results.&lt;/p&gt;

&lt;p&gt;So the first task was to create a schema:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A schema helps organise database objects. I wanted the Greenwood Academy tables grouped together instead of scattered around the database like files named:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final.sql
final2.sql
final_final.sql
final_final_use_this_one.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I set the schema as the working location:&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;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;Now PostgreSQL knew where I wanted to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Tables
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;students&lt;/code&gt; table needed a unique ID, names, gender, date of birth, class, and city:&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;INT&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 &lt;code&gt;student_id&lt;/code&gt; is the &lt;code&gt;PRIMARY KEY&lt;/code&gt;, meaning each student must have a unique identifier.&lt;/p&gt;

&lt;p&gt;Because names are not always enough.&lt;/p&gt;

&lt;p&gt;You can have:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;But their IDs should still be different.&lt;/p&gt;

&lt;p&gt;The database does not care which Brian sits near the window. The database wants the ID.&lt;/p&gt;

&lt;p&gt;Next came the subjects table:&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;INT&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; constraint on &lt;code&gt;subject_name&lt;/code&gt; helps prevent duplicate subject names.&lt;/p&gt;

&lt;p&gt;Because we probably do not need:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;unless Mathematics has somehow become three different departments.&lt;/p&gt;

&lt;p&gt;Finally, the exam results:&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;INT&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="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;2&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;Instead of repeatedly storing the student's full name and every other detail in every exam record, we use IDs.&lt;/p&gt;

&lt;p&gt;That keeps the data more organised and saves us from trying to determine whether these are four different people:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Amina Wanjiku
Amina Wanjiku 
AMINA WANJIKU
Amina Wanjiku N.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes the database problem is not the database.&lt;/p&gt;

&lt;p&gt;Sometimes it is the person who entered the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then the Requirements Changed
&lt;/h3&gt;

&lt;p&gt;Once the tables were created, someone realised the students table needed a phone number.&lt;/p&gt;

&lt;p&gt;So we added one:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the &lt;code&gt;credits&lt;/code&gt; column needed a new name:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the phone number was no longer needed:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Added.&lt;/p&gt;

&lt;p&gt;Renamed.&lt;/p&gt;

&lt;p&gt;Removed.&lt;/p&gt;

&lt;p&gt;The database had barely settled down.&lt;/p&gt;

&lt;p&gt;This is a realistic introduction to working with requirements. Someone says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We need this column.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You add it.&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Actually, we don't need it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You remove it.&lt;/p&gt;

&lt;p&gt;Then, three months later:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why doesn't the column exist anymore?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why database work requires technical skills and the ability to remain calm while requirements perform a complete Nairobi matatu route change.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION B: DML - Putting Data Into the Tables
&lt;/h2&gt;

&lt;p&gt;DDL built the structure.&lt;/p&gt;

&lt;p&gt;Now we needed data.&lt;/p&gt;

&lt;p&gt;DML stands for &lt;strong&gt;Data Manipulation Language&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The main commands are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT
UPDATE
DELETE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where the database starts becoming useful.&lt;/p&gt;

&lt;p&gt;It is also where you start reading your queries twice.&lt;/p&gt;

&lt;p&gt;Maybe three times.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;INSERT&lt;/code&gt;: Adding the Data
&lt;/h2&gt;

&lt;p&gt;The first student:&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;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;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="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the remaining students were inserted.&lt;/p&gt;

&lt;p&gt;The data included students from Nairobi, Mombasa, Kisumu, Nakuru, and Eldoret.&lt;/p&gt;

&lt;p&gt;Then came the subjects.&lt;/p&gt;

&lt;p&gt;Then the exam results.&lt;/p&gt;

&lt;p&gt;At this point, the database had data.&lt;/p&gt;

&lt;p&gt;Which meant it was time for the data to be wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Esther Moved From Nakuru to Nairobi
&lt;/h2&gt;

&lt;p&gt;Esther Akinyi had moved from Nakuru to Nairobi.&lt;/p&gt;

&lt;p&gt;The database still had her old city.&lt;/p&gt;

&lt;p&gt;So:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;WHERE&lt;/code&gt; clause tells PostgreSQL exactly which student to update.&lt;/p&gt;

&lt;p&gt;Student number 5.&lt;/p&gt;

&lt;p&gt;Esther.&lt;/p&gt;

&lt;p&gt;Not everyone.&lt;/p&gt;

&lt;p&gt;Now look at this:&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That updates every student.&lt;/p&gt;

&lt;p&gt;Amina moves to Nairobi.&lt;/p&gt;

&lt;p&gt;Brian moves to Nairobi.&lt;/p&gt;

&lt;p&gt;Cynthia moves to Nairobi.&lt;/p&gt;

&lt;p&gt;Everyone moves to Nairobi.&lt;/p&gt;

&lt;p&gt;Mombasa is finished.&lt;/p&gt;

&lt;p&gt;Nakuru is finished.&lt;/p&gt;

&lt;p&gt;Eldoret is now a historical concept.&lt;/p&gt;

&lt;p&gt;This is why a missing &lt;code&gt;WHERE&lt;/code&gt; clause can ruin your career.&lt;/p&gt;

&lt;p&gt;The database does not stop and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Are you sure you meant all 10 students?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It does exactly what you wrote.&lt;/p&gt;

&lt;p&gt;Which is admirable.&lt;/p&gt;

&lt;p&gt;Until you realise what you wrote.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 49 That Was Actually a 59
&lt;/h2&gt;

&lt;p&gt;Result number 5 had the wrong mark.&lt;/p&gt;

&lt;p&gt;The database said &lt;code&gt;49&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The correct mark was &lt;code&gt;59&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One record.&lt;/p&gt;

&lt;p&gt;One correction.&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Now imagine this:&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every exam result is now 59.&lt;/p&gt;

&lt;p&gt;The student who scored 95?&lt;/p&gt;

&lt;p&gt;59.&lt;/p&gt;

&lt;p&gt;The student who scored 78?&lt;/p&gt;

&lt;p&gt;59.&lt;/p&gt;

&lt;p&gt;The student who scored 49?&lt;/p&gt;

&lt;p&gt;Also 59.&lt;/p&gt;

&lt;p&gt;We have not corrected the data.&lt;/p&gt;

&lt;p&gt;We have created a very strange grading policy.&lt;/p&gt;

&lt;p&gt;This is the moment you start appreciating the humble &lt;code&gt;WHERE&lt;/code&gt; clause.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Canceled Exam
&lt;/h2&gt;

&lt;p&gt;Result number 9 had been canceled:&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;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;One result removed.&lt;/p&gt;

&lt;p&gt;The dangerous version:&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That deletes every exam result.&lt;/p&gt;

&lt;p&gt;No warning.&lt;/p&gt;

&lt;p&gt;No popup.&lt;/p&gt;

&lt;p&gt;No person from IT appearing beside you asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What exactly are you doing?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The database trusts you.&lt;/p&gt;

&lt;p&gt;Perhaps too literally.&lt;/p&gt;

&lt;p&gt;A good habit is to check the row before changing or deleting it:&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="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;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;If the correct row appears, then delete it.&lt;/p&gt;

&lt;p&gt;The same principle applies to updates:&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="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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, inspect.&lt;/p&gt;

&lt;p&gt;Then, change.&lt;/p&gt;

&lt;p&gt;Because fixing one student's city is a normal database task.&lt;/p&gt;

&lt;p&gt;Explaining why the entire school now lives in Nairobi is a different kind of meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION C: DQL - Asking the Database Questions
&lt;/h2&gt;

&lt;p&gt;Now we get to &lt;code&gt;SELECT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;DQL stands for &lt;strong&gt;Data Query Language&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The database has the data.&lt;/p&gt;

&lt;p&gt;We ask questions.&lt;/p&gt;

&lt;p&gt;To find Form 4 students:&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="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;To find 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="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;To find 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="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="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;To find female students:&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="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;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'F'&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 the basic idea behind &lt;code&gt;WHERE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You describe what you want.&lt;/p&gt;

&lt;p&gt;The database checks the rows.&lt;/p&gt;

&lt;p&gt;It returns what matches.&lt;/p&gt;

&lt;p&gt;No manually scanning through hundreds of spreadsheet rows and hoping you did not accidentally leave a filter active from yesterday.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;AND&lt;/code&gt;: When Both Conditions Must Be True
&lt;/h2&gt;

&lt;p&gt;The assignment asked us to find students who were in Form 3 &lt;strong&gt;and&lt;/strong&gt; from Nairobi:&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="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;Both conditions must be true.&lt;/p&gt;

&lt;p&gt;A Form 3 student from Kisumu does not qualify.&lt;/p&gt;

&lt;p&gt;A Nairobi student in Form 4 does not qualify.&lt;/p&gt;

&lt;p&gt;You can add more conditions:&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="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;gender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'F'&lt;/span&gt;
&lt;span class="k"&gt;AND&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;Now all three conditions must be true.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;OR&lt;/code&gt;: When Either Condition Works
&lt;/h2&gt;

&lt;p&gt;Now we want students in Form 2 or Form 4:&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="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;p&gt;Form 2?&lt;/p&gt;

&lt;p&gt;Include.&lt;/p&gt;

&lt;p&gt;Form 4?&lt;/p&gt;

&lt;p&gt;Include.&lt;/p&gt;

&lt;p&gt;Form 3?&lt;/p&gt;

&lt;p&gt;Not this time.&lt;/p&gt;

&lt;p&gt;The difference is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AND → all conditions must be true
OR  → at least one condition must be true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two small words.&lt;/p&gt;

&lt;p&gt;Very different results.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION D: Better Ways to Filter Data
&lt;/h2&gt;

&lt;p&gt;Sometimes the question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this value exactly equal to that value?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes we need a range, a list, or a pattern.&lt;/p&gt;

&lt;p&gt;That is where &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; become useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;BETWEEN&lt;/code&gt;: Working With a Range
&lt;/h2&gt;

&lt;p&gt;To find exam results between 50 and 80:&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="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;p&gt;This includes both boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50 → included
65 → included
80 → included
49 → excluded
81 → excluded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also write:&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;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;50&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;marks&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;p&gt;That works.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;BETWEEN&lt;/code&gt; is cleaner when working with a range.&lt;/p&gt;

&lt;p&gt;It can also be used with 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="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;exam_date&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-15'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="s1"&gt;'2024-03-18'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;IN&lt;/code&gt;: Checking Against a List
&lt;/h2&gt;

&lt;p&gt;Suppose we want students from Nairobi, Mombasa, or Kisumu.&lt;/p&gt;

&lt;p&gt;We could write:&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;WHERE&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;OR&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;'Mombasa'&lt;/span&gt;
&lt;span class="k"&gt;OR&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;'Kisumu'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&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="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;p&gt;&lt;code&gt;IN&lt;/code&gt; is useful when you have a list of acceptable values.&lt;/p&gt;

&lt;p&gt;It is shorter, easier to read, and saves you from writing a query that looks like it is negotiating with itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;NOT IN&lt;/code&gt;: Excluding Values
&lt;/h2&gt;

&lt;p&gt;Now find students who are not in Form 2 or Form 3:&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="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="k"&gt;NOT&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 3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This excludes those two classes.&lt;/p&gt;

&lt;p&gt;Everyone else remains eligible.&lt;/p&gt;

&lt;p&gt;Sometimes the easiest way to describe what you want is to describe what you do not want.&lt;/p&gt;

&lt;p&gt;SQL understands that too.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;LIKE&lt;/code&gt;: Searching for Patterns
&lt;/h2&gt;

&lt;p&gt;Suppose we want students whose first name starts with A or E:&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="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;first_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'A%'&lt;/span&gt;
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'E%'&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;%&lt;/code&gt; is a wildcard.&lt;/p&gt;

&lt;p&gt;It means there can be anything after this.&lt;/p&gt;

&lt;p&gt;The assignment also asked for subjects containing the word &lt;code&gt;Studies&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;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;The &lt;code&gt;%&lt;/code&gt; appears on both sides because the word can occur anywhere in the subject name.&lt;/p&gt;

&lt;p&gt;This is cleaner than manually writing:&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;WHERE&lt;/span&gt; &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Computer Studies'&lt;/span&gt;
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Business Studies'&lt;/span&gt;
&lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;subject_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Social Studies'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then remembering another subject later.&lt;/p&gt;

&lt;p&gt;The wildcard does the searching.&lt;/p&gt;

&lt;p&gt;We do not have to manually list every possible value.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION E: &lt;code&gt;COUNT(*)&lt;/code&gt; - Let the Database Do the Counting
&lt;/h2&gt;

&lt;p&gt;How many students are currently in Form 3?&lt;/p&gt;

&lt;p&gt;We could count them manually.&lt;/p&gt;

&lt;p&gt;But we have a 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;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How many exam results have 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="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="o"&gt;*&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;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;&lt;code&gt;COUNT(*)&lt;/code&gt; counts the number of rows that match the condition.&lt;/p&gt;

&lt;p&gt;This is one of those moments where using a computer is a sensible decision.&lt;/p&gt;

&lt;p&gt;The database is already sitting there with the rows.&lt;/p&gt;

&lt;p&gt;Let it count them.&lt;/p&gt;

&lt;p&gt;You have other things to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  SECTION F: &lt;code&gt;CASE WHEN&lt;/code&gt; - Giving Raw Data Some Meaning
&lt;/h2&gt;

&lt;p&gt;The database stores marks.&lt;/p&gt;

&lt;p&gt;People like categories.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;80 and above → Distinction
60–79        → Merit
40–59        → Pass
Below 40     → Fail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can 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%2F89btidg598cy4s0zzcm1.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%2F89btidg598cy4s0zzcm1.png" alt="Case when merit" width="799" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the query can give us:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Marks&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;Distinction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;Merit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;td&gt;Fail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important part is that we did not change the original marks.&lt;/p&gt;

&lt;p&gt;If the database stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;85
72
59
39
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it still stores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;85
72
59
39
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query simply creates a new label while displaying the results.&lt;/p&gt;

&lt;p&gt;That means we can change the classification rules later without rewriting the original marks.&lt;/p&gt;

&lt;p&gt;The raw data stays as it is.&lt;/p&gt;

&lt;p&gt;The interpretation can change.&lt;/p&gt;

&lt;p&gt;We can do the same thing with student classes:&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%2F13i052cuyd5syrurh0vp.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%2F13i052cuyd5syrurh0vp.png" alt="case when classes" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A student in Form 3 is still in Form 3.&lt;/p&gt;

&lt;p&gt;We are simply creating another way to describe that student.&lt;/p&gt;

&lt;p&gt;No rewriting the original data.&lt;/p&gt;

&lt;p&gt;No creating another spreadsheet.&lt;/p&gt;

&lt;p&gt;The query handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part I Found Most Important
&lt;/h2&gt;

&lt;p&gt;The assignment covered a lot of SQL.&lt;/p&gt;

&lt;p&gt;But the part I kept thinking about was the difference between:&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One updates Esther.&lt;/p&gt;

&lt;p&gt;The other relocates the entire school.&lt;/p&gt;

&lt;p&gt;The same applies to:&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;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;versus:&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;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="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One removes a canceled exam.&lt;/p&gt;

&lt;p&gt;The other removes every exam result.&lt;/p&gt;

&lt;p&gt;SQL is not difficult because the database is trying to trick you.&lt;/p&gt;

&lt;p&gt;SQL is difficult because the database is perfectly willing to do exactly what you told it to do.&lt;/p&gt;

&lt;p&gt;Even when what you told it to do was a terrible idea.&lt;/p&gt;

&lt;p&gt;So before running an &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt;, especially on important data, check what you are about to affect:&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="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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then make the change.&lt;/p&gt;

&lt;p&gt;Same condition.&lt;/p&gt;

&lt;p&gt;First, inspect.&lt;/p&gt;

&lt;p&gt;Then, change.&lt;/p&gt;

&lt;p&gt;This is not advanced database engineering.&lt;/p&gt;

&lt;p&gt;It is simply a good habit.&lt;/p&gt;

&lt;p&gt;And good habits are useful when you are tired, it is 2:00 AM, and somebody has just said:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It is a very small change.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Greenwood Academy Taught Me About SQL
&lt;/h2&gt;

&lt;p&gt;The assignment started with an empty database.&lt;/p&gt;

&lt;p&gt;By the end, I had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created the &lt;code&gt;greenwood_academy&lt;/code&gt; schema.&lt;/li&gt;
&lt;li&gt;Created the &lt;code&gt;students&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;Created the &lt;code&gt;subjects&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;Created the &lt;code&gt;exam_results&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;Added a &lt;code&gt;phone_number&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;Renamed &lt;code&gt;credits&lt;/code&gt; to &lt;code&gt;credit_hours&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Removed the &lt;code&gt;phone_number&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;Inserted student, subject, and exam result records.&lt;/li&gt;
&lt;li&gt;Updated Esther's city from Nakuru to Nairobi.&lt;/li&gt;
&lt;li&gt;Corrected a mark from 49 to 59.&lt;/li&gt;
&lt;li&gt;Deleted a canceled exam result.&lt;/li&gt;
&lt;li&gt;Filtered data using &lt;code&gt;WHERE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Combined conditions using &lt;code&gt;AND&lt;/code&gt; and &lt;code&gt;OR&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;BETWEEN&lt;/code&gt; for ranges.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;IN&lt;/code&gt; and &lt;code&gt;NOT IN&lt;/code&gt; for lists.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;LIKE&lt;/code&gt; with wildcards.&lt;/li&gt;
&lt;li&gt;Counted records using &lt;code&gt;COUNT(*)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Created performance labels using &lt;code&gt;CASE WHEN&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That sounds like a lot.&lt;/p&gt;

&lt;p&gt;But when broken down, SQL was asking me to do a few very direct things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE → Make something
INSERT → Put something in
UPDATE → Change something
DELETE → Remove something
SELECT → Show me something
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part was not simply knowing that these commands exist.&lt;/p&gt;

&lt;p&gt;The difficult part was being precise about what they should affect.&lt;/p&gt;

&lt;p&gt;That is where the &lt;code&gt;WHERE&lt;/code&gt; clause quietly becomes one of the most important things in the entire assignment.&lt;/p&gt;

&lt;p&gt;You can learn the syntax.&lt;/p&gt;

&lt;p&gt;You can memorise the commands.&lt;/p&gt;

&lt;p&gt;But at some point, you have to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What exactly will this query change?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question is probably more useful than memorising another SQL keyword.&lt;/p&gt;

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

&lt;p&gt;I started with an empty PostgreSQL database and a list of questions.&lt;/p&gt;

&lt;p&gt;By the end, I had a better understanding of how the different parts fit together.&lt;/p&gt;

&lt;p&gt;DDL built the structure.&lt;/p&gt;

&lt;p&gt;DML put data into it and changed it.&lt;/p&gt;

&lt;p&gt;DQL let me ask questions about it.&lt;/p&gt;

&lt;p&gt;&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; made those questions more specific.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COUNT(*)&lt;/code&gt; handled the counting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CASE WHEN&lt;/code&gt; let me add useful labels without changing the original data.&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;WHERE&lt;/code&gt; reminded me that SQL is extremely literal.&lt;/p&gt;

&lt;p&gt;The database will not rescue you from your own query.&lt;/p&gt;

&lt;p&gt;It will not stop and say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I think you meant one row.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will execute.&lt;/p&gt;

&lt;p&gt;So I have taken one practical rule away from Greenwood Academy:&lt;/p&gt;

&lt;p&gt;Before I run an &lt;code&gt;UPDATE&lt;/code&gt; or a &lt;code&gt;DELETE&lt;/code&gt;, I want to know exactly which rows I am touching.&lt;/p&gt;

&lt;p&gt;Because fixing Esther's city is a normal database task.&lt;/p&gt;

&lt;p&gt;Moving every student in the school to Nairobi is a different kind of meeting.&lt;/p&gt;

&lt;p&gt;And I am not trying to attend that meeting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Surviving SQL is winning, Franklin.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>dbeaver</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>Commitment Issues: Tracking Changes, Romantic Promises, and Surviving the Git Staging Crate</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Sun, 12 Jul 2026 20:29:09 +0000</pubDate>
      <link>https://dev.to/ms_njenga/commitment-issues-tracking-changes-romantic-promises-and-surviving-the-git-staging-crate-152k</link>
      <guid>https://dev.to/ms_njenga/commitment-issues-tracking-changes-romantic-promises-and-surviving-the-git-staging-crate-152k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Commitment is a funny thing.&lt;/p&gt;

&lt;p&gt;Some people date for years before making it official. Some rush into relationships they regret two weeks later. And then there are those people who keep saying, “Let’s just see where this goes,” until everyone involved is emotionally exhausted.&lt;/p&gt;

&lt;p&gt;Believe it or not, Git has the exact same personality.&lt;/p&gt;

&lt;p&gt;If you’ve never heard of Git before, think of it as a really smart time machine for your code. It remembers every meaningful change you make, lets you revisit older versions when things go wrong, and makes it possible for multiple developers to work on the same project without accidentally overwriting each other’s work. In short, Git is a version control system and one of the most important tools you’ll use as a developer.&lt;/p&gt;

&lt;p&gt;But Git doesn’t commit to every little change you make.&lt;/p&gt;

&lt;p&gt;Every time you edit a file, Git looks at your changes and asks, “So… are we serious, or are we just experimenting?” If you’re not ready to commit, your changes wait patiently in the staging area,the software equivalent of “It’s complicated.” If you are ready, Git wraps everything up into a commit: a permanent promise that says, “Yes, I meant to do this.”&lt;/p&gt;

&lt;p&gt;The problem is that most beginners are taught Git like they’re preparing for a spelling bee.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add
git commit
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Memorize the commands.&lt;br&gt;
Don’t ask questions.&lt;br&gt;
Hope nothing catches fire.&lt;/p&gt;

&lt;p&gt;Then, the first time they accidentally commit the wrong files, forget to stage something important, or stare at git status like it’s judging their life choices, panic sets in.&lt;/p&gt;

&lt;p&gt;Here’s the secret: &lt;strong&gt;Git isn’t complicated because the commands are difficult. It’s confusing because no one explains why they exist.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So in this article, we’re going to stop memorizing magic words and start thinking like Git. We’ll open the mysterious staging crate, decide which changes deserve commitment, rescue a few questionable life decisions, and discover why version control is less about remembering commands and more about making good choices.&lt;/p&gt;

&lt;p&gt;Because in Git-as in relationships not everything you change deserves a lifelong commitment.&lt;/p&gt;


&lt;h2&gt;
  
  
  Episode 1: "So... What Are We?" (&lt;code&gt;git init&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Picture this.&lt;/p&gt;

&lt;p&gt;You've met someone amazing. You've exchanged numbers. You've talked for hours. You've even started imagining your future together.&lt;/p&gt;

&lt;p&gt;But then a friend asks,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Wait... are you two actually together?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Cue the awkward silence.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because until someone makes it official, you're just... existing in each other's lives.&lt;/p&gt;

&lt;p&gt;Your project has the same identity crisis.&lt;/p&gt;

&lt;p&gt;You create a new folder. You add a few files. Maybe you've already written hundreds of lines of code. It looks like a software project, smells like a software project, and behaves like a software project.&lt;/p&gt;

&lt;p&gt;Git, however, looks at it, shrugs, and says,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I've literally never seen this project before."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where this little command changes everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
&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%2F4bwpc8ha6008amvecsng.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%2F4bwpc8ha6008amvecsng.png" alt="gitinit" width="796" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The moment you hit &lt;strong&gt;Enter&lt;/strong&gt;, your project finally introduces itself.&lt;/p&gt;

&lt;p&gt;Git quietly creates a hidden folder called &lt;strong&gt;&lt;code&gt;.git&lt;/code&gt;&lt;/strong&gt; a place you'll almost never open but one you'll rely on every single day.&lt;/p&gt;

&lt;p&gt;Think of it as the relationship archive.&lt;/p&gt;

&lt;p&gt;Every promise you keep (commits), every detour you take (branches), every reunion after a disagreement (merges), and every embarrassing decision you'd rather pretend never happened... Git stores it all in there.&lt;/p&gt;

&lt;p&gt;It's basically that friend who remembers &lt;strong&gt;everything&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Remember that tiny CSS change you made three weeks ago at 2:17 a.m.? Yeah... I still have it."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And if one day you decide to delete the &lt;strong&gt;&lt;code&gt;.git&lt;/code&gt;&lt;/strong&gt; folder?&lt;/p&gt;

&lt;p&gt;Congratulations.&lt;/p&gt;

&lt;p&gt;You didn't delete your project.&lt;/p&gt;

&lt;p&gt;You just convinced Git that the two of you never met.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 2: The Honest Bestie (&lt;code&gt;git status&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Every relationship has that one best friend.&lt;/p&gt;

&lt;p&gt;The one who somehow knows &lt;strong&gt;everything&lt;/strong&gt; before you tell them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Girl... I saw what happened."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"You're leaving out the important part."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Who exactly is this?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They're not being nosy.&lt;/p&gt;

&lt;p&gt;They're just trying to save you from making questionable decisions.&lt;/p&gt;

&lt;p&gt;Git is that best friend.&lt;/p&gt;

&lt;p&gt;Ever since you made things official with &lt;code&gt;git init&lt;/code&gt;, Git has been quietly paying attention to your project. It isn't changing anything or making decisions for you it simply watches, remembers, and waits for your next move.&lt;/p&gt;

&lt;p&gt;So naturally, the first thing you should ask your bestie is,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Okay... what's the situation?"&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&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%2Fpm5m2w1ib2y429gnmyzn.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%2Fpm5m2w1ib2y429gnmyzn.png" alt="gitstatus1" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git takes one look around your project and gives you the unfiltered truth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I noticed you've brought some new people into your life..."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"...care to introduce me?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those red filenames aren't errors.&lt;/p&gt;

&lt;p&gt;They're &lt;strong&gt;untracked files&lt;/strong&gt; files that Git has noticed but hasn't officially been introduced to yet.&lt;/p&gt;

&lt;p&gt;Think of it this way.&lt;/p&gt;

&lt;p&gt;You've started hanging out with new friends, but you've never brought them around your bestie. So when your best friend sees them for the first time, they're naturally like,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Wait... who are these?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Until you make those introductions, Git won't remember those files or include them in your project's history. If they disappear tomorrow, Git can't help you because, as far as it's concerned...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I've never met them."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let's address the one person who always shows up uninvited.&lt;/p&gt;

&lt;p&gt;Take a look at the list again.&lt;/p&gt;

&lt;p&gt;See &lt;strong&gt;&lt;code&gt;.ipynb_checkpoints/&lt;/code&gt;&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;If you use Jupyter Notebooks, you've probably met this little troublemaker. Every time you save your notebook, Jupyter quietly creates checkpoint files behind the scenes as a backup.&lt;/p&gt;

&lt;p&gt;It's useful.&lt;/p&gt;

&lt;p&gt;It's harmless.&lt;/p&gt;

&lt;p&gt;But it has absolutely no business following you to GitHub.&lt;/p&gt;

&lt;p&gt;It's like bringing your best friend to meet your family... and they show up with their cousin, their neighbor, and someone's emotional support hamster.&lt;/p&gt;

&lt;p&gt;Nobody asked for all that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 3: "Before You Make It Official..." (&lt;code&gt;git add&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;You know what happens after you've introduced someone to your best friend?&lt;/p&gt;

&lt;p&gt;The interrogation begins.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"So... do you actually like them?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Are they staying?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Should I bother learning their name?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Is this another one of your 'it's different this time' situations?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your bestie isn't being dramatic.&lt;/p&gt;

&lt;p&gt;They're just trying to figure out who deserves a permanent place in your life.&lt;/p&gt;

&lt;p&gt;Git has reached that exact moment.&lt;/p&gt;

&lt;p&gt;In the last episode, &lt;code&gt;git status&lt;/code&gt; showed Git everyone hanging around your project. It noticed the new files, gave you the side-eye, and asked,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Okay... who exactly are these people?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now it's waiting for your answer.&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;staging area&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;Think of it as the beautifully crafted wooden crate sitting on your desk.&lt;/p&gt;

&lt;p&gt;It's not the commitment box.&lt;/p&gt;

&lt;p&gt;Not yet.&lt;/p&gt;

&lt;p&gt;It's simply where you place the files you're &lt;strong&gt;considering&lt;/strong&gt; committing.&lt;/p&gt;

&lt;p&gt;Inside the crate are envelopes labelled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Maybe later&lt;/li&gt;
&lt;li&gt; Not ready yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;That's because staging isn't commitment.&lt;/p&gt;

&lt;p&gt;It's &lt;strong&gt;intention&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's you telling Git,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"These are the changes I'm thinking about making official."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe you're confident and want to stage everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;--all&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%2Fep7dkdnej45g52ja3o5c.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%2Fep7dkdnej45g52ja3o5c.png" alt="gitadd--all" width="800" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git happily picks up every new file, every modified file, and every deleted file, placing them neatly into the staging crate.&lt;/p&gt;

&lt;p&gt;You'll also see developers use the shorter version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&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%2Fl4z3eklx63orm4bp4wv6.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%2Fl4z3eklx63orm4bp4wv6.png" alt="gitaddA" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same result.&lt;/p&gt;

&lt;p&gt;Just fewer keystrokes.&lt;/p&gt;

&lt;p&gt;Now suppose you're standing inside your project and simply want Git to gather everything from &lt;strong&gt;right here&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's where the famous little dot comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&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%2Fa0g7csfhx31awfbhy161.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%2Fa0g7csfhx31awfbhy161.png" alt="GITADD" width="796" height="65"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That tiny &lt;code&gt;.&lt;/code&gt; simply means,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Start from this folder and bring everything below it."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're already at your project's root directory, &lt;code&gt;git add .&lt;/code&gt; will usually give you the same result as the previous commands.&lt;/p&gt;

&lt;p&gt;Now imagine your bestie peeks inside the staging crate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Okay... these are the people you're serious about?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You nod.&lt;/p&gt;

&lt;p&gt;Your bestie smiles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Cool."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Just remember... putting them in the crate doesn't mean you're engaged."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Exactly.&lt;/p&gt;

&lt;p&gt;Nothing has been committed yet.&lt;/p&gt;

&lt;p&gt;The letters are written.&lt;/p&gt;

&lt;p&gt;The envelopes are packed.&lt;/p&gt;

&lt;p&gt;The crate is full.&lt;/p&gt;

&lt;p&gt;But the promise hasn't been made.&lt;/p&gt;

&lt;p&gt;That... happens in the next episode.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 4: The Plot Twist - "Wait... They're Coming Too?"
&lt;/h2&gt;

&lt;p&gt;You've spent all this time deciding who belongs in the staging crate.&lt;/p&gt;

&lt;p&gt;Your bestie helped.&lt;/p&gt;

&lt;p&gt;The letters are neatly packed.&lt;/p&gt;

&lt;p&gt;Everything looks perfect.&lt;/p&gt;

&lt;p&gt;So, just before making the biggest commitment of your Git journey, you ask one last question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Bestie... can you check the crate one more time?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Git happily obliges.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&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%2Fijgn1skqgi56ph0ewhd4.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%2Fijgn1skqgi56ph0ewhd4.png" alt="gitstatusafteradding" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything is green.&lt;/p&gt;

&lt;p&gt;At first, you're relieved.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Yes! We did it!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then your bestie squints at the list.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"...Hold on."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why is &lt;em&gt;that&lt;/em&gt; coming with us?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You lean over the crate.&lt;/p&gt;

&lt;p&gt;Inside, alongside your Python scripts and notebooks, you spot...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A massive software installer ZIP file.&lt;/li&gt;
&lt;li&gt;An MP3 music file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.ipynb_checkpoints/&lt;/code&gt; quietly pretending it belongs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awkward.&lt;/p&gt;

&lt;p&gt;This is what happens when you stage &lt;strong&gt;everything&lt;/strong&gt; without checking what's inside the crate.&lt;/p&gt;

&lt;p&gt;It's the Git equivalent of sending wedding invitations to everyone in your contacts list...&lt;/p&gt;

&lt;p&gt;...including your ex.&lt;/p&gt;

&lt;p&gt;...your nail-tech.&lt;/p&gt;

&lt;p&gt;...and the plumber who fixed your sink three years ago.&lt;/p&gt;

&lt;p&gt;Technically, they'll all show up.&lt;/p&gt;

&lt;p&gt;But should they?&lt;/p&gt;

&lt;p&gt;Absolutely not.&lt;/p&gt;

&lt;p&gt;If you commit these files and push them to GitHub, your repository becomes unnecessarily large, cloning takes longer, and anyone reviewing your project will immediately wonder,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Did they actually mean to include all of this?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The good news?&lt;/p&gt;

&lt;p&gt;Git understands that we all make questionable decisions.&lt;/p&gt;

&lt;p&gt;It even gives you a graceful way to unpack the entire staging crate without throwing away your actual files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of this command as your best friend standing at the door collecting invitations.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Nope... you're not coming."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"You either."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Definitely not you."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The files stay safely on your computer.&lt;/p&gt;

&lt;p&gt;Git simply removes them from the staging area, giving you the chance to pack the crate again but this time with a little more intention.&lt;/p&gt;

&lt;p&gt;Because here's the thing about commitment...&lt;/p&gt;

&lt;p&gt;Changing your mind &lt;strong&gt;before&lt;/strong&gt; you make the promise is a lot easier than trying to explain yourself afterward.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 5: "So... Are We Official Now?" (&lt;code&gt;git commit&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The crate is packed.&lt;/p&gt;

&lt;p&gt;Your bestie has checked the guest list twice.&lt;/p&gt;

&lt;p&gt;The random ZIP file has been politely uninvited.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.ipynb_checkpoints/&lt;/code&gt; finally got the hint.&lt;/p&gt;

&lt;p&gt;Everything inside the staging crate is exactly where it should be.&lt;/p&gt;

&lt;p&gt;Your bestie looks at you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Last chance."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Are you absolutely sure these are the changes you want to keep forever?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You take one final look.&lt;/p&gt;

&lt;p&gt;You smile.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Yeah... I'm ready."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the moment Git has been waiting for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"First commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a single command, Git carefully closes the staging crate.&lt;/p&gt;

&lt;p&gt;It seals every envelope with wax.&lt;/p&gt;

&lt;p&gt;It writes today's story into its diary.&lt;/p&gt;

&lt;p&gt;And just like that...&lt;/p&gt;

&lt;p&gt;Your changes become part of history.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;commit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of a commit as a photograph.&lt;/p&gt;

&lt;p&gt;Not of what your project &lt;strong&gt;will become&lt;/strong&gt;...&lt;/p&gt;

&lt;p&gt;...but of exactly what it looked like at &lt;strong&gt;this very moment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No matter how many changes you make tomorrow, next week, or six months from now, Git can always return to this snapshot.&lt;/p&gt;

&lt;p&gt;That's why commits are so powerful.&lt;/p&gt;

&lt;p&gt;They're promises to your future self.&lt;/p&gt;

&lt;p&gt;They're your way of saying,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I know this version works."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I know why I made these changes."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"If I ever mess things up, I can always come back here."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And before you start thinking,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Great! My code is finally on GitHub!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not so fast.&lt;/p&gt;

&lt;p&gt;Your commit lives &lt;strong&gt;only on your computer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's like writing a heartfelt love letter, sealing it in an envelope, and placing it safely inside your desk drawer.&lt;/p&gt;

&lt;p&gt;The promise is real.&lt;/p&gt;

&lt;p&gt;It's official.&lt;/p&gt;

&lt;p&gt;But nobody else knows about it...&lt;/p&gt;

&lt;p&gt;Yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 6: The "We Need to Talk" Conversation (&lt;code&gt;git switch&lt;/code&gt;, &lt;code&gt;git diff&lt;/code&gt; &amp;amp; &lt;code&gt;git restore&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Every healthy relationship has one.&lt;/p&gt;

&lt;p&gt;The conversation.&lt;/p&gt;

&lt;p&gt;Not the breakup.&lt;/p&gt;

&lt;p&gt;Not the proposal.&lt;/p&gt;

&lt;p&gt;The one that starts with,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can we talk?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe someone wants to rephrase a promise.&lt;/p&gt;

&lt;p&gt;Maybe someone realizes they could have said something better.&lt;/p&gt;

&lt;p&gt;Maybe they're just trying to figure things out.&lt;/p&gt;

&lt;p&gt;The good news?&lt;/p&gt;

&lt;p&gt;In Git, you're allowed to have those conversations without ruining the original relationship.&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;branches&lt;/strong&gt; are for.&lt;/p&gt;

&lt;p&gt;Think of a branch as an alternate timeline.&lt;/p&gt;

&lt;p&gt;Instead of rewriting history, Git lets you create a safe little universe where you can experiment, make mistakes, and change your mind, all without touching your stable project.&lt;/p&gt;

&lt;p&gt;So let's create one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; remove_promise
&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%2Fbcfeb5yltu3m8uiqj1yn.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%2Fbcfeb5yltu3m8uiqj1yn.png" alt="remove_promise" width="800" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your bestie raises an eyebrow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"So... you're telling me you're about to edit the promise?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Relax.&lt;/p&gt;

&lt;p&gt;We're not changing the original.&lt;/p&gt;

&lt;p&gt;We're just exploring a &lt;em&gt;"What if?"&lt;/em&gt; scenario.&lt;/p&gt;

&lt;p&gt;Inside our &lt;code&gt;index.html&lt;/code&gt;, we tweak the heartfelt letter.&lt;/p&gt;

&lt;p&gt;Nothing dramatic.&lt;/p&gt;

&lt;p&gt;Just enough to make Git notice.&lt;/p&gt;

&lt;p&gt;Before doing anything else, let's ask our honest bestie what they think.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&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%2F4neh8yb6c5xx4i5lmjqt.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%2F4neh8yb6c5xx4i5lmjqt.png" alt="gitstatus" width="800" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git immediately notices the change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I saw that."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"You changed something... but you haven't told me to remember it yet."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's all &lt;code&gt;git status&lt;/code&gt; is doing.&lt;/p&gt;

&lt;p&gt;It's keeping you honest.&lt;/p&gt;

&lt;p&gt;Now imagine you stare at your edit for a minute and suddenly think,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"What was I even trying to say?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We've all been there.&lt;/p&gt;

&lt;p&gt;Fortunately, Git doesn't judge questionable decisions.&lt;/p&gt;

&lt;p&gt;It simply offers an escape route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git restore index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of &lt;code&gt;git restore&lt;/code&gt; as crumpling up the draft letter before anyone reads it.&lt;/p&gt;

&lt;p&gt;The edits disappear.&lt;/p&gt;

&lt;p&gt;Your last committed version remains untouched, as if the conversation never happened.&lt;/p&gt;

&lt;p&gt;But let's pretend we actually like our new wording.&lt;/p&gt;

&lt;p&gt;How do we see &lt;strong&gt;exactly&lt;/strong&gt; what changed?&lt;/p&gt;

&lt;p&gt;That's where &lt;code&gt;git diff&lt;/code&gt; becomes your bestie's favorite gossip tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff index.html
&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%2Fxiuwkcm40ranb9mdiqsq.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%2Fxiuwkcm40ranb9mdiqsq.png" alt="diff" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git opens the receipts.&lt;/p&gt;

&lt;p&gt;No guessing.&lt;/p&gt;

&lt;p&gt;No "I think I changed this."&lt;/p&gt;

&lt;p&gt;Just the facts.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;red&lt;/strong&gt; line, marked with a &lt;code&gt;-&lt;/code&gt;, shows what you're saying goodbye to.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;green&lt;/strong&gt; line, marked with a &lt;code&gt;+&lt;/code&gt;, shows the new version waiting to take its place.&lt;/p&gt;

&lt;p&gt;In our case, we upgraded the promise by adding the word &lt;strong&gt;"always."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One tiny word.&lt;/p&gt;

&lt;p&gt;One huge difference.&lt;/p&gt;

&lt;p&gt;And before we make that promise official, Git gives us one final chance to review every single change.&lt;/p&gt;

&lt;p&gt;Because the strongest relationships and the strongest codebases aren't built by never making mistakes.&lt;/p&gt;

&lt;p&gt;They're built by being able to see exactly what changed before making another commitment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 7: The Reunion (&lt;code&gt;git merge&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Every couple reaches this moment.&lt;/p&gt;

&lt;p&gt;You've had the difficult conversation.&lt;/p&gt;

&lt;p&gt;You've rewritten the letter.&lt;/p&gt;

&lt;p&gt;You've taken some time apart to figure things out.&lt;/p&gt;

&lt;p&gt;Now comes the big question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Can we bring this back into the relationship?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Remember, all of our edits have been living safely on a separate branch. Think of it as taking a weekend getaway to clear your head. The &lt;code&gt;main&lt;/code&gt; branch has been patiently waiting at home, completely untouched.&lt;/p&gt;

&lt;p&gt;Before the reunion can happen, we need to go back home.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your bestie smiles.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Welcome back."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now it's time to bring everything we worked on back into the main story.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge remove_promise
&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%2Fapwzsuj4by6ohcy0cheu.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%2Fapwzsuj4by6ohcy0cheu.png" alt="merging" width="799" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Git takes one look at both branches.&lt;/p&gt;

&lt;p&gt;Then it shrugs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Well... that was easy."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since nobody touched the &lt;code&gt;main&lt;/code&gt; branch while we were away, Git doesn't have to play relationship counselor.&lt;/p&gt;

&lt;p&gt;There are no arguments.&lt;/p&gt;

&lt;p&gt;No awkward conversations.&lt;/p&gt;

&lt;p&gt;No one saying,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"That's not what I meant."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, Git simply moves the &lt;code&gt;main&lt;/code&gt; branch forward until it catches up with your new commit.&lt;/p&gt;

&lt;p&gt;This is called a &lt;strong&gt;fast-forward merge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like coming back from a weekend trip.&lt;/p&gt;

&lt;p&gt;Your family didn't repaint the house.&lt;/p&gt;

&lt;p&gt;Nobody rearranged the furniture.&lt;/p&gt;

&lt;p&gt;Everything is exactly where you left it.&lt;/p&gt;

&lt;p&gt;So fitting back in is effortless.&lt;/p&gt;

&lt;p&gt;Your upgraded promise is now part of the main story, and Git quietly records another successful chapter in its diary.&lt;/p&gt;

&lt;p&gt;Your bestie closes the diary with a satisfied smile.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Perfect."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Everything is finally where it belongs."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You smile back...&lt;/p&gt;

&lt;p&gt;Then your bestie asks one last question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"...So, are you planning to keep this relationship a secret?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because right now, everything we've done exists &lt;strong&gt;only on your computer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Our story is complete.&lt;/p&gt;

&lt;p&gt;Our commits are safe.&lt;/p&gt;

&lt;p&gt;Our branches are merged.&lt;/p&gt;

&lt;p&gt;But the rest of the world still has no idea our project exists.&lt;/p&gt;

&lt;p&gt;Let's change that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Episode 8: Going Public (&lt;code&gt;git push&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;So far, our entire love story has been wonderfully... private.&lt;/p&gt;

&lt;p&gt;We've met Git.&lt;/p&gt;

&lt;p&gt;We've introduced the important files.&lt;/p&gt;

&lt;p&gt;We've packed the staging crate.&lt;/p&gt;

&lt;p&gt;We've made our commitment.&lt;/p&gt;

&lt;p&gt;We've even survived a little relationship drama and reunited stronger than before.&lt;/p&gt;

&lt;p&gt;But there's one tiny problem.&lt;/p&gt;

&lt;p&gt;Everything we've done still lives &lt;strong&gt;only on our laptop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's like getting engaged...&lt;/p&gt;

&lt;p&gt;...and forgetting to tell your family.&lt;/p&gt;

&lt;p&gt;Before we can share our project with the world, Git needs to know &lt;strong&gt;where&lt;/strong&gt; the world is.&lt;/p&gt;

&lt;p&gt;So head over to GitHub, create a repository, and copy its URL.&lt;/p&gt;

&lt;p&gt;Then introduce your local repository to its new 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%2Fvxeqf6jr9fhhjj1rstol.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%2Fvxeqf6jr9fhhjj1rstol.png" alt="shippingtogithub" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll notice we give GitHub a nickname:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;origin&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of it as saving someone's contact in your phone as &lt;strong&gt;"Bestie"&lt;/strong&gt; instead of memorizing their entire phone number.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;origin&lt;/code&gt; is simply a friendly alias for your GitHub repository, making it much easier to communicate with later.&lt;/p&gt;

&lt;p&gt;Now comes the moment we've been building toward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With one command, your local story leaves your laptop for the very first time.&lt;/p&gt;

&lt;p&gt;Your commits travel to GitHub.&lt;/p&gt;

&lt;p&gt;Your repository finally has an online home.&lt;/p&gt;

&lt;p&gt;And your project is no longer just &lt;strong&gt;your&lt;/strong&gt; story.&lt;/p&gt;

&lt;p&gt;It's a story others can now see, clone, learn from, and contribute to.&lt;/p&gt;

&lt;p&gt;Our relationship is officially public.&lt;/p&gt;

&lt;p&gt;But don't get too comfortable...&lt;/p&gt;

&lt;p&gt;Because sharing your work with the world introduces a whole new kind of drama.&lt;/p&gt;

&lt;p&gt;Pull requests.&lt;/p&gt;

&lt;p&gt;Remote repositories.&lt;/p&gt;

&lt;p&gt;Authentication.&lt;/p&gt;

&lt;p&gt;Merge conflicts.&lt;/p&gt;

&lt;p&gt;And teammates who somehow edit the exact same line you were working on.&lt;/p&gt;

&lt;p&gt;We'll save that tea for the next chapter.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>analytics</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>Is It a Crime to Want a Flawless Connection? Mastering Local and Cloud Databases in Power BI</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Tue, 07 Jul 2026 00:34:24 +0000</pubDate>
      <link>https://dev.to/ms_njenga/is-it-a-crime-to-want-a-flawless-connection-mastering-local-and-cloud-databases-in-power-bi-3l6j</link>
      <guid>https://dev.to/ms_njenga/is-it-a-crime-to-want-a-flawless-connection-mastering-local-and-cloud-databases-in-power-bi-3l6j</guid>
      <description>&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%2Fmivu1clvllukc41uoxrm.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%2Fmivu1clvllukc41uoxrm.png" alt="COVER1" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  INTRODUCTION
&lt;/h2&gt;

&lt;p&gt;When you first start learning Power BI, life is relatively simple. You click "Get Data," select a spreadsheet or a CSV file sitting on your desktop, and start building charts. It’s a comfortable bubble. &lt;br&gt;
But as you start working on bigger projects, you quickly realize a hard truth: in the real world, most data doesn't live in loose spreadsheets on your desktop. It lives inside database servers.&lt;/p&gt;

&lt;p&gt;The moment you try to step out of your comfort zone and connect Power BI to a database, the setup process can feel incredibly intimidating. Connecting to a database running right on your own computer is usually pretty smooth. But the second you try to connect to a secure cloud platform like Aiven PostgreSQL, Power BI will often drop a massive roadblock right in your face: &lt;strong&gt;The SSL Certificate Error&lt;/strong&gt;.&lt;br&gt;
Suddenly, you are staring at a scary security alert, wondering why a cloud server won't let you access your data. If you are tired of hitting connection walls and just want your tools to talk to each other, don't panic. You don't need to be a senior database engineer or a coding expert to fix this.&lt;br&gt;
In this beginner-friendly guide, we are going to break down the exact, click-by-click steps to handle simple local connections and show you the ultimate Windows trick to make your computer automatically trust your cloud database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Moving Data into the Database (DBeaver Import)
&lt;/h2&gt;

&lt;p&gt;Before Power BI can turn your data into visual charts, we need to upload our file into our database. We will use a free tool called &lt;code&gt;DBeaver&lt;/code&gt; to act as our database manager. Think of DBeaver as a file explorer, but specifically built for databases. It lets us drag, drop, and view our tables without writing a single line of code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.1: Logging Into Your Database
&lt;/h3&gt;

&lt;p&gt;Whether your database is running quietly on your own laptop or hosted online via Aiven, DBeaver needs to know where to find it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open DBeaver and click the Plug Icon (New Database Connection) in the top-left corner.&lt;/li&gt;
&lt;li&gt; Select PostgreSQL from the list of database types and click Next.&lt;/li&gt;
&lt;li&gt; Look at your database settings (either your local installation details or your Aiven Console dashboard) and fill in the blanks:&lt;/li&gt;
&lt;li&gt;Host: Use &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; for your own computer, or paste the long server link provided by Aiven.Port: Type &lt;code&gt;5432&lt;/code&gt; for local setups, or &lt;code&gt;24306&lt;/code&gt; for Aiven cloud instances.&lt;/li&gt;
&lt;li&gt;Database: Type the name of your target database (the default local one is usually called &lt;code&gt;postgres&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Username &amp;amp; Password: Type in your database login keys.&lt;/li&gt;
&lt;li&gt;Click Test Connection. Once a little box pops up with a green success message, click Finish.&lt;/li&gt;
&lt;/ol&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%2Fcpyd0uey1ewthy1n4d1a.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%2Fcpyd0uey1ewthy1n4d1a.png" alt="local connection" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1.2: Creating a Folder and Importing Your File
&lt;/h3&gt;

&lt;p&gt;To keep our data organized, we don't want to dump our tables into a messy public folder. Let's create a clean, dedicated space called a Schema. In DBeaver’s left sidebar, expand your database connection.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the word Schemas. Select Create New Schema. &lt;/li&gt;
&lt;li&gt;Right-click your brand-new schema folder. Select Import Data. Input file and click Next.&lt;/li&gt;
&lt;li&gt;Click the folder icon, browse your computer, and select the data file you want to upload.
&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%2Fhkc4jk84h5b3jhqp6691.png" alt="folder" width="800" height="450"&gt;
&lt;/li&gt;
&lt;li&gt;Click Next through the settings, DBeaver will automatically look at your file and fit the right column structures for you. Click Proceed to execute the upload.
&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%2F852k881weqhf4ykfg2xr.png" alt="before importing" width="800" height="450"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 2: The Easy Road (Connecting to a Local Database)
&lt;/h2&gt;

&lt;p&gt;Now, let's open Power BI Desktop. We will start with the easiest, most stress-free connection scenario: linking up to a database running natively right on your own machine (localhost). Because this traffic never leaves your computer, Windows automatically trusts it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch Power BI Desktop and open a blank report canvas.&lt;/li&gt;
&lt;li&gt;On the top Home ribbon, click the &lt;strong&gt;Get Data button -&amp;gt; select More -&amp;gt; choose PostgreSQL database.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the Server input box, type your database location using a &lt;code&gt;host:port&lt;/code&gt; (for a local setup, this is usually &lt;code&gt;localhost:5432&lt;/code&gt;or &lt;code&gt;127.0.0.1:5432&lt;/code&gt;). Type your database name in the box below it.
&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%2Fjshmx3u98kuaj8stfppc.png" alt="database" width="800" height="450"&gt;
&lt;/li&gt;
&lt;li&gt;Choose Import as your data connectivity mode (this pulls a copy of the data into Power BI so your charts load incredibly fast) and click OK.&lt;/li&gt;
&lt;li&gt;Switch to the Database credentials tab on the left sidebar, enter your database username and password, and click Connect.&lt;/li&gt;
&lt;/ol&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%2F6o3swvxx8dvxuvv46ah8.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%2F6o3swvxx8dvxuvv46ah8.png" alt="password" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your tables will instantly pop up in the Navigator window, ready to be loaded into your report&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%2Fssews8ij5wwbiyi73gty.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%2Fssews8ij5wwbiyi73gty.png" alt="pop-out" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: The Cloud Roadblock (Bypassing Aiven's Security Alert)
&lt;/h2&gt;

&lt;p&gt;Now, try taking your long Aiven cloud host string, pasting it into that exact same Power BI connection box, and hitting connect. It will fail instantly.&lt;br&gt;
Instead of seeing your tables, Power BI will flash a frustrating SSL/Security Error on your screen.&lt;br&gt;
Why does this happen? Cloud databases like Aiven sit on the internet outside your home network. To protect your information, they force everything to be strictly encrypted. Power BI relies entirely on your Windows operating system to check if a cloud connection is safe. Because Windows doesn't recognize Aiven out-of-the-box, it panics and blocks the pipeline.&lt;br&gt;
To break through this wall, we just need to download a digital security passport (a CA Certificate) from Aiven and slide it into your Windows security vault. Once Windows trusts it, Power BI will too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.1: Downloading the Security Passport
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open your web browser and log into your Aiven.io console.&lt;/li&gt;
&lt;li&gt;Click on your running PostgreSQL service page.&lt;/li&gt;
&lt;li&gt;Scroll down to the Connection Information panel on your dashboard.&lt;/li&gt;
&lt;li&gt;Locate the CA Certificate block and click the Download button. This saves a small file called ca.pem to your machine.&lt;/li&gt;
&lt;li&gt;Pro-Tip: Don't leave this file in your temporary Downloads folder. Move it to a permanent folder on your computer, because Windows needs to look at this file in the background every single time your Power BI charts refresh.&lt;/li&gt;
&lt;/ol&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%2F58109dxbp89y61152ose.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%2F58109dxbp89y61152ose.png" alt="Download ca cert" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3.2: Slipping the Certificate into the Windows Vault
&lt;/h3&gt;

&lt;p&gt;Now, we explicitly tell Windows that this cloud database is completely safe.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click your Windows taskbar search tool, type "Manage computer certificates" (or press Win + R, type certlm.msc, and press Enter) to open the Certificate Manager.&lt;/li&gt;
&lt;li&gt;On the left side panel, locate and expand the folder named &lt;strong&gt;Trusted Root Certification Authorities&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Right-click the underlying &lt;strong&gt;Certificates sub-folder -&amp;gt; Go to All Tasks -&amp;gt; Click Import....&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Step into the wizard and click Next. When the file browser opens, change the extension dropdown in the bottom-right corner from "X.509" to "All Files (.)" otherwise, your ca.pem file will stay invisible&lt;/li&gt;
&lt;li&gt;Select your ca.pem file, click Next through the default locations, and click Finish. Click Yes on the dramatic Windows security warning pop-up.
&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%2F9zd395typk8rvnzlp797.png" alt="cert" width="800" height="450"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Part 4: The Flawless Cloud Connection
&lt;/h2&gt;

&lt;p&gt;With your Windows operating system now validating the security handshake in the background, the connection barrier completely disappears.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch Power BI Desktop and open a blank report canvas.&lt;/li&gt;
&lt;li&gt;On the top Home ribbon, click the &lt;strong&gt;Get Data button -&amp;gt; select More -&amp;gt; choose PostgreSQL database.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Input your Aiven cloud host and port details into the server field.
&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%2Fhr6a2zmppmpns0v5z90x.png" alt="aiven host and port" width="800" height="450"&gt;
&lt;/li&gt;
&lt;li&gt;The Cloud Switch: Because your machine now holds the matching validation key, check the "Use encrypted connection" box.&lt;/li&gt;
&lt;li&gt;Click OK, pivot to the Database credentials tab on the left sidebar, enter your administrative username (avnadmin), and paste your cloud service password.&lt;/li&gt;
&lt;li&gt;Hit Connect.
The data gates open instantly. The Navigator interface will open up beautifully, displaying your custom cloud schema and your loaded tables completely free of security alerts and ready to be turned into stunning dashboards.
&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%2F6g0v2i1yybfhfh17tp48.png" alt="navigation pane" width="800" height="450"&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Stepping up from basic flat files to structured database servers can feel like a steep learning curve, but it is one of the most valuable skills you can learn. Local connections are fantastic for practicing and testing things quickly on your own machine. But learning how to work with certificates and cloud encryption layers is what prepares you for real-world data environments. You don't need to be a coding genius to build secure pipelines, you just need to know how to configure your security lanes&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>dbeaver</category>
      <category>cloudstorage</category>
    </item>
    <item>
      <title>Relationships Are Hard (Except in Power BI Data Modeling)</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Tue, 30 Jun 2026 15:04:34 +0000</pubDate>
      <link>https://dev.to/ms_njenga/relationships-are-hard-except-in-power-bi-data-modeling-27cp</link>
      <guid>https://dev.to/ms_njenga/relationships-are-hard-except-in-power-bi-data-modeling-27cp</guid>
      <description>&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%2Fw4p6v9qnmifak9g7s6iu.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%2Fw4p6v9qnmifak9g7s6iu.png" alt="Cover Image" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;u&gt;INTRODUCTION&lt;/u&gt;
&lt;/h2&gt;

&lt;p&gt;Power BI is mainly associated with the creation of beautiful, interactive dashboards and reports, but did you know that it has more to it than that? Have a seat as I explain its immense power and what great works it can achieve. &lt;/p&gt;

&lt;p&gt;As always, I am here to break it down to a very digestible form.&lt;/p&gt;

&lt;p&gt;For the high performance of the reports we see, solid data models are vital. Designing a model correctly ensures fast calculation speed, accurate DAX data analysis, and easy report maintenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;Data Schemas &lt;/u&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;schema&lt;/strong&gt; is the structural blueprint of your data model. It dictates how your data tables are organized and connected.&lt;br&gt;
Before deciding on a schema, you typically break your data into two types of tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fact Tables&lt;/strong&gt;: The Actions (The Numbers)&lt;br&gt;
Think of a Fact Table as a continuous digital receipt log. It only cares about tracking business events and recording the raw numbers.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt; (Streaming App): Every time someone presses play on Netflix, it logs the event. It records Numeric Data like Duration_Minutes (45) and Pause_Count (2), alongside IDs to link to details.&lt;br&gt;
&lt;strong&gt;The Goal&lt;/strong&gt;: It answers "How much?" or "How many?"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dimension Tables&lt;/strong&gt;: The Context (The Text)A Dimension Table is your lookup list. It holds all the &lt;u&gt;descriptive details&lt;/u&gt; that give meaning to your raw numbers.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt; (Streaming App): Instead of repeating the movie title, director, release year, and genre on every single play receipt, you store it once in a &lt;code&gt;Movies&lt;/code&gt; lookup table next to a unique Movie_ID.&lt;br&gt;
&lt;strong&gt;The Goal&lt;/strong&gt;: It answers "Who?", "What?", or "Where?"&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choosing your Blueprint(Schema)
&lt;/h3&gt;

&lt;p&gt;Now that you have known your Facts from your Dimensions, how do you arrange them in Power BI?&lt;br&gt;
There are 3 schema types. Namely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Star&lt;/li&gt;
&lt;li&gt;Snowflake&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Star Schema
&lt;/h4&gt;

&lt;p&gt;The Star schema gets its name because of its shape. You place the number-heavy Fact Table in the center and surround it with your descriptive Dimension Tables like the points of a star.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why use it&lt;/strong&gt;: It provides the best query performance, reduces data redundancy, and uses one-to-many relationships that are easy for Power BI to process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Snowflake Schema
&lt;/h4&gt;

&lt;p&gt;The Snowflake Schema happens when you split your dimension tables into even smaller lookup tables(e.g., a "Product" table branching out into separate "Brand" and "Category" tables).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why use it&lt;/strong&gt;: It is useful when you need to optimize storage or map highly complex hierarchical relationships, though it can slow down report rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Flat Schema
&lt;/h4&gt;

&lt;p&gt;This represents a single, massive, consolidated table where all information lives together. This happens automatically when you load a single CSV or flat Excel file into Power BI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why use it&lt;/strong&gt;: It requires no modeling effort and is perfectly fine for small, simple datasets. However, it leads to heavy data redundancy and poor performance on large datasets.
Here is a visual representation of the three.&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%2Ffymq3if9ay2qx3ryxh2l.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%2Ffymq3if9ay2qx3ryxh2l.png" alt="Visual rep of the 3 schemas" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;Relationships&lt;/u&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The 3 Core Settings of a Relationship&lt;/strong&gt;&lt;br&gt;
Every relationship you build in Power BI is defined by three main settings:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Cardinality (The Type of Connection)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;One-to-Many (1:*): The most common and efficient type. One row in your lookup table (e.g., Customers) connects to many rows in your transaction table (e.g., Sales).&lt;/li&gt;
&lt;li&gt;Many-to-One (*:1): The same as one-to-many, just viewed from the opposite direction.&lt;/li&gt;
&lt;li&gt;One-to-One (1:1): Links single unique rows between two tables. It is rarely used and usually means the tables should just be merged.&lt;/li&gt;
&lt;li&gt;Many-to-Many (&lt;em&gt;:&lt;/em&gt;): Links non-unique values on both sides. It should be avoided when possible because it can cause unpredictable calculations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Cross Filter Direction (How the Data Flows)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Single Direction: The filter flows only one way—usually from the Dimension table down to the Fact table. This is the safest and fastest setting.&lt;/li&gt;
&lt;li&gt;Both Directions (Bi-directional): Filters flow both ways. While powerful, it can severely slow down your reports and create ambiguous data paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Relationship Status
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Active: The primary path Power BI uses to calculate data between two tables. Only one active relationship can exist between any two tables at a time.&lt;/li&gt;
&lt;li&gt;Inactive: Secondary paths. They sit in the background and are only triggered when you specifically call them using DAX formulas (like USERELATIONSHIP).
&lt;em&gt;Active vs. Inactive Example&lt;/em&gt;
If your Sales table has a Order_Date and a Ship_Date, you can only link one of them actively to your Calendar table. You would make Order_Date active for your daily sales reports, and keep Ship_Date inactive, activating it only when calculating shipping backlogs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;u&gt;Power Query Joins: Merging Tables&lt;/u&gt;
&lt;/h3&gt;

&lt;p&gt;In Power BI, &lt;strong&gt;joins&lt;/strong&gt; are used to merge two tables permanently into a single, wider table during the data preparation phase. &lt;/p&gt;

&lt;p&gt;While &lt;em&gt;relationships&lt;/em&gt; connect separate tables in your visual model, &lt;em&gt;joins&lt;/em&gt; physically combine your data rows in the Power Query Editor using the &lt;strong&gt;Merge Queries&lt;/strong&gt; feature.&lt;/p&gt;

&lt;h4&gt;
  
  
  The 6 Types of Joins in Power BI
&lt;/h4&gt;

&lt;p&gt;Power BI offers six different ways to combine your datasets based on a matching column:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Left Outer:&lt;/strong&gt; Keeps &lt;strong&gt;all rows from the first table&lt;/strong&gt;, and only matching rows from the second table. This is the most common join.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right Outer:&lt;/strong&gt; Keeps &lt;strong&gt;all rows from the second table&lt;/strong&gt;, and only matching rows from the first table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Outer(The collector):&lt;/strong&gt; Keeps &lt;strong&gt;all rows from both tables&lt;/strong&gt;. Unmatched rows display null values for the missing data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inner(The matchmaker):&lt;/strong&gt; Keeps &lt;strong&gt;only rows that match&lt;/strong&gt; in both tables. Any unmatched data is completely discarded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Left Anti:&lt;/strong&gt; Keeps rows &lt;strong&gt;only present in the first table&lt;/strong&gt; that do not have a match in the second. It is perfect for finding missing records.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right Anti:&lt;/strong&gt; Keeps rows &lt;strong&gt;only present in the second table&lt;/strong&gt; that do not have a match in the first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Join Type&lt;/th&gt;
&lt;th&gt;Records Kept from Table 1 (Left)&lt;/th&gt;
&lt;th&gt;Records Kept from Table 2 (Right)&lt;/th&gt;
&lt;th&gt;Best Used For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Left Outer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Only Matches&lt;/td&gt;
&lt;td&gt;Adding descriptive columns to transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Right Outer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only Matches&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Inverting data priorities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Full Outer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;Combining disparate budgets or targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only Matches&lt;/td&gt;
&lt;td&gt;Only Matches&lt;/td&gt;
&lt;td&gt;Filtering for strict overlap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Left Anti&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only Unmatched&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Finding customers who &lt;em&gt;haven't&lt;/em&gt; bought yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Right Anti&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Only Unmatched&lt;/td&gt;
&lt;td&gt;Finding orphaned data in secondary tables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Join vs. Relationship: What's the difference?
&lt;/h3&gt;

&lt;p&gt;It is common to confuse Power Query joins with Data Model relationships. Here is how to differentiate them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Joins (Power Query):&lt;/strong&gt; Happen during data ingestion. They alter the physical structure of your data by flattening tables together, which consumes more memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relationships (Data Model):&lt;/strong&gt; Happen after the data is loaded. They keep tables separate and link them virtually, maximizing Power BI’s performance engine.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>analytics</category>
      <category>database</category>
      <category>dataengineering</category>
      <category>powerbi</category>
    </item>
    <item>
      <title>Into the Bashlands: Surviving the Big Black Screen Without an Undo Button</title>
      <dc:creator>Faith Njenga</dc:creator>
      <pubDate>Mon, 29 Jun 2026 21:44:39 +0000</pubDate>
      <link>https://dev.to/ms_njenga/head-on-with-linux-49c4</link>
      <guid>https://dev.to/ms_njenga/head-on-with-linux-49c4</guid>
      <description>&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%2Fdbicnclzw3bcsgrru5ca.jpeg" 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%2Fdbicnclzw3bcsgrru5ca.jpeg" alt="LinuxCover" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;It's not talked about enough: the fear of the terminal window, or in other words, the big black screen.&lt;br&gt;
 Where there are no folder icons to double click on, their creation is not just a right-click, no buttons to undo mistakes and a single mistake can trigger an error message that looks like a loud, red alarm. Well, I want you to take a deep breath. The creeping of a sudden wave of imposter syndrome might not be news.&lt;br&gt;
 &lt;strong&gt;You are not alone in this&lt;/strong&gt;&lt;br&gt;
I decided to dive into the unknown and become a Data Engineer. If you do not know Linux in this line of Duty is a very crucial tool. Just like you I always thought that the command line was reserved for a certain category that I like call 'Big Boys' also known as cybersecurity experts or people who have memorized every shortcut since 1995. But over the last few weeks I have decided to get my hands dirty with real-world server deployment and yes, I can attest that: &lt;strong&gt;The terminal is not a monster. It is just a conversation.&lt;/strong&gt;&lt;br&gt;
Every time the terminal throws a scary Fatal error to you, it is not judging you but simply pointing out a missing configuration step in plain text. In this article, I am going to take my time and explain the nitty-gritty  of what I learned in setting up a remote cloud database from scratch. We will look at my exact real-world configuration, demystify database permissions and show how visual tools like Dbeaver can come to the rescue all these in simple and friendly terms.&lt;/p&gt;
&lt;h3&gt;
  
  
  Remote Access and Navigating
&lt;/h3&gt;

&lt;p&gt;Because a remote cloud server does not have a monitor or a keyboard attached to it in a physical room, we have to access its brain over the internet. The tool we use is &lt;strong&gt;SSH(Secure Shell)&lt;/strong&gt;. This of SSH as a secure, encrypted digital tunnel that stretches from your home laptop keyboard straight into the host operating system.&lt;br&gt;
For my recent project, our remote Ubuntu cloud server was hosted at the public IP address &lt;code&gt;159.65.222.96&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The login Banner&lt;/strong&gt;&lt;br&gt;
When you first connect via SSH using the administrative root user account, the server greets you with a system status banner:&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%2F9p2crmj50bif2n3dpews.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%2F9p2crmj50bif2n3dpews.png" alt="connect via SSH to Ubuntu cloud server" width="800" height="249"&gt;&lt;/a&gt;&lt;br&gt;
Don't let this numbers overwhelm you. Look at them as a health dashboard for your remote computer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;usage of /: 4.2%&lt;/code&gt; means your server's storage drive is mostly empty. Your data pipelines have plenty of room to breathe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Memory usage:28%&lt;/code&gt; tells you that more that two-thirds of your server's RAM is sitting open and ready to process heavy computational tasks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Creating an Isolated User &amp;amp; The privilege shift
&lt;/h3&gt;

&lt;p&gt;To practice good security habits and protect our environment, we need to move away from the &lt;code&gt;root&lt;/code&gt; account. I created my own isolated user account (&lt;code&gt;faithn&lt;/code&gt;) using standard Linux configuration tools.&lt;br&gt;
Here is exactly how I built that user account and gave it administrative &lt;code&gt;sudo&lt;/code&gt; privileges while logged in as root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#1. Create the new user account (The system will prompt you to set a password)&lt;/span&gt;
root@assignmentServer:~# adduser faithn

&lt;span class="c"&gt;#2. Grant the new user access to administrative powers ('sudo' group)&lt;/span&gt;
root@assignmentServer:~# usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;faithn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Securely switch over to the new personal account by nesting a new connection&lt;/li&gt;
&lt;/ol&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%2F4vnhuhgmc3h7sye03xrd.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%2F4vnhuhgmc3h7sye03xrd.png" alt="Logging in my isolated account" width="800" height="403"&gt;&lt;/a&gt;&lt;br&gt;
Look at that prompt change:&lt;br&gt;
'faithn@assignmentServer:~$'.The dollar sign($) means I am now standing safely inside my personal use sandbox. I have my own room, my own security, and I can't accidentally break the rest of the server.&lt;/p&gt;
&lt;h3&gt;
  
  
  Finding Your Way Around Without a Mouse
&lt;/h3&gt;

&lt;p&gt;Now that we are inside our isolated faithn user account, how do we look around? Because you cannot see any folders or icons, you have to use your keyboard as your eyes. Here are some commands that helped me navigate my new digital apartment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pwd&lt;/code&gt; (Print Working Directory): Tells you exactly where you are standing. The exact location where you are at.&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%2Fce43pfgiy0jry9ksbfkt.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%2Fce43pfgiy0jry9ksbfkt.png" alt="PWD" width="712" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ls -la&lt;/code&gt; (List All): This prints out every file and folder in your current directory, including hidden system files that control your settings.&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%2F9st84kio8wk8lphzr1qo.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%2F9st84kio8wk8lphzr1qo.png" alt="list of files with details" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;mkdir&lt;/code&gt; (Make Directory)- This is equivalent of right-clicking on a desktop screen and selecting &lt;em&gt;New&amp;gt; Folder&lt;/em&gt;.&lt;br&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%2F09hx6i5gqwfse067npba.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%2F09hx6i5gqwfse067npba.png" alt="Creating a folder" width="798" height="97"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cd&lt;/code&gt; (Change Directory)- This is the physical act of walking into a folder.&lt;/p&gt;&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%2F3chtxt883d3xjtfpj8nl.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%2F3chtxt883d3xjtfpj8nl.png" alt="Navigating inside our new folder" width="800" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;touch&lt;/code&gt;- This instantly lays  blank sheets of paper on your desk. It is a fresh text file ready for configuration details or pipeline scripts. 
&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%2Fj3evv2puk22s3je3l4bk.png" alt="Creating a new file" width="797" height="77"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Unmasking the PostgreSQL Prompt
&lt;/h2&gt;

&lt;p&gt;The next major conceptual hurdle for any beginner data engineer is understanding Context Switching.&lt;/p&gt;

&lt;p&gt;Once, I confirmed that our database software is installed by running &lt;br&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%2F1fpwrak4u4y4qfu1cpxd.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%2F1fpwrak4u4y4qfu1cpxd.png" alt="Get PostgreSQL version" width="800" height="79"&gt;&lt;/a&gt;&lt;br&gt;
It was time to log in.&lt;/p&gt;
&lt;h2&gt;
  
  
  The right way to Login In
&lt;/h2&gt;

&lt;p&gt;Once your user roles and permissions are properly set up the server, logging into your personal database should be clean and direct. You run this command into your regular Linux terminal prompt:&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%2Fdyuicqn4eijqaafmjggh.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%2Fdyuicqn4eijqaafmjggh.png" alt="Login to Postgresql" width="800" height="86"&gt;&lt;/a&gt;&lt;br&gt;
If everything is configured correctly, the server will bypass all administrative barriers and drop you straight into your destination. You will see a clean welcome message and responsive prompt ready for your data queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;psql (16.14 (Ubuntu 16.14-0ubuntu0.24.04.1))
Type "help" for help.

&lt;/span&gt;&lt;span class="gp"&gt;faithn=#&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The First Trap: Getting Stuck in the Prompts
&lt;/h3&gt;

&lt;p&gt;However, when you are first figuring things out, it is incredibly easy to take a wrong turn and find yourself stuck like I was.&lt;br&gt;
 I tried to log straight into my database but I instead I fell directly into a classic terminal context trap that puzzles almost every single beginner:&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%2Fgqta71ugnmh8g8zhnblr.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%2Fgqta71ugnmh8g8zhnblr.png" alt="Basic trap" width="800" height="147"&gt;&lt;/a&gt;&lt;br&gt;
Let's dissect this line by line, because understanding this sequence will instantly remove your fear of database environments.&lt;/p&gt;
&lt;h3&gt;
  
  
  Decoding the Prompt Symbols ($, #, and -)
&lt;/h3&gt;

&lt;p&gt;The trailing symbols on your prompt lines are visual road signs showing which program is listening to your keyboard :&lt;br&gt;
&lt;code&gt;faithn@assignmentServer:~$&lt;/code&gt; The $ means you are in the Linux Shell. You run standard system commands here like ls or mkdir.&lt;br&gt;
&lt;code&gt;postgres=#&lt;/code&gt;The # means you crossed a portal into the PostgreSQL Engine. Linux commands will not work here; the server only accepts SQL code.&lt;br&gt;
&lt;code&gt;postgres-#&lt;/code&gt; The dash (-) means PostgreSQL is waiting for you to finish a SQL statement. Typing &lt;code&gt;psql&lt;/code&gt; twice triggers this because it isn't a valid SQL command, and the engine thinks you are still typing your sentence.&lt;br&gt;
&lt;strong&gt;The Fix&lt;/strong&gt;: If you ever get trapped on a line with a dash (-#) and your keyboard feels locked, press Ctrl + C. This breaks the waiting loop and returns you to a clean &lt;code&gt;postgres=#&lt;/code&gt; prompt.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Second Trap: The "Role Does Not Exist" Block
&lt;/h3&gt;

&lt;p&gt;Once you exit the prompt loops and confidently try to log into your database using your custom name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash/usr/lib/postgresql/16/bin/psql &lt;span class="nt"&gt;-d&lt;/span&gt; faithn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL flatly rejects you with a fatal message:&lt;br&gt;
&lt;code&gt;FATAL: role "faithn" does not exist&lt;/code&gt; &lt;br&gt;
This error makes your stomach drop. You think, "But wait, My Linux username is faithn, and I am typing it correctly! Why does it say I don't exist?"&lt;/p&gt;
&lt;h4&gt;
  
  
  Breaking Down the Trap
&lt;/h4&gt;

&lt;p&gt;This trap happens because Linux users and Database users are completely isolated from each other. Just because the server has a user profile named faithn doesn't mean the PostgreSQL application automatically knows who you are. PostgreSQL tracks its own separate guest list of users, which it calls Roles.&lt;br&gt;
Think of your Linux account as your security badge to get into the main university building. The Database Role is a completely separate guest list held by a second guard standing right outside the locked database room. When you first try to connect, that second guard checks his specific list, doesn't see your name, and locks you out,.&lt;br&gt;
 &lt;strong&gt;The Clean Solution&lt;/strong&gt;&lt;br&gt;
To fix this trap, we use the Master Database Admin identity (&lt;code&gt;postgres=#&lt;/code&gt;) to officially add your name to the database guest list and create your file storage container:&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;sql&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;faithn&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;LOGIN&lt;/span&gt; &lt;span class="n"&gt;SUPERUSER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;faithn&lt;/span&gt; &lt;span class="k"&gt;OWNER&lt;/span&gt; &lt;span class="n"&gt;faithn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By explicitly running these, the two separate systems are perfectly linked. Now when you type &lt;code&gt;sudo -u postgres psql faithn&lt;/code&gt;, the database guard sees your name on the list and lets you right in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Schema and Importing Data with DBeaver
&lt;/h2&gt;

&lt;p&gt;Now that our database boundaries are unlocked, we can construct our ingestion environment. In data engineering, we use a Schema as a logical container to organize our tables. For this project, we are building a landing bucket called staging.&lt;br&gt;
We log into our database and run a simple, clean SQL script to lay down our structure:&lt;br&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%2Fzrb31c9vgnizzx4dfpwk.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%2Fzrb31c9vgnizzx4dfpwk.png" alt="Creating a schema" width="799" height="190"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Bypassing Terminal Fatigue
&lt;/h3&gt;

&lt;p&gt;At this point, a classic beginner trap is trying to write massive INSERT INTO statements directly in the command line for dozens of records. It is a massive waste of time, and a single missing comma will break the entire thing. Real-world data engineers don't do everything the hard way, we balance terminal power with graphical comfort. This is the exact moment where DBeaver becomes your ultimate rescue tool. Because we configured our remote cloud server (159.65.222.96) to allow external network connections on port 5432, we can hook DBeaver directly into our backend using our personal faithn credentials. Instead of typing endless lines of code to force our data into the black box of the terminal, DBeaver peels back the scary layer of the command line and gives us a friendly, visual import wizard. I simply selected the mock student CSV spreadsheet generated on my local desktop, clicked through the visual mapping guide, and let the software automatically parse and load all records into our remote cloud database in seconds.&lt;br&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%2Fvsx1mj1c0lipzmg4sguj.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%2Fvsx1mj1c0lipzmg4sguj.png" alt="Connectingdbeaver" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It gives you the absolute best of both worlds: your data is safely running on an enterprise Linux server in the cloud, but you get to interact with it using a beautiful, visual, human-friendly application.&lt;br&gt;
To verify that DBeaver successfully uploaded your records from your local PC to your remote cloud database, you can switch back to your Linux server terminal (&lt;code&gt;faithn@assignmentServer&lt;/code&gt;) and run this standard SQL 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="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;staging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Will See on Your Screen
&lt;/h3&gt;

&lt;p&gt;Running that command will print your data cleanly inside your terminal layout, showing you the exact rows you just uploaded through DBeaver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;age |      course      | admission_number | grade | student_id |   gender   |    major    
-----+------------------+------------------+-------+------------+------------+-------------
  22 | Computer Science |      4083        | F     |        1   | Male       | Physics     
  22 | Business         |      7444        | F     |        2   | Female     | Physics     
  18 | Biology          |      4103        | C     |        3   | Female     | Physics     
  26 | Biology          |      7590        | B     |        4   | Female     | Chemistry   
  30 | Psychology       |      1978        | F     |        5   | Female     | Chemistry   
  30 | Biology          |      9396        | C     |        6   | Female     | English     
  24 | Biology          |      5989        | C     |        7   | Non-binary | Mathematics 
  25 | Biology          |      8362        | B     |        8   | Non-binary | Physics     
  25 | Business         |      3275        | D     |        9   | Non-binary | Chemistry   
  21 | Business         |      6182        | B     |       10   | Female     | Physics




&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>machinelearning</category>
      <category>linux</category>
      <category>database</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
