<?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: Gideon Kiprono</title>
    <description>The latest articles on DEV Community by Gideon Kiprono (@gideon_kiprono_bfdedde010).</description>
    <link>https://dev.to/gideon_kiprono_bfdedde010</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%2F3952212%2Faa7c3a6d-c93c-42bf-b03c-78a69896b986.jpg</url>
      <title>DEV Community: Gideon Kiprono</title>
      <link>https://dev.to/gideon_kiprono_bfdedde010</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gideon_kiprono_bfdedde010"/>
    <language>en</language>
    <item>
      <title>Understanding Subqueries and CTE's</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Sat, 26 Sep 2026 10:02:34 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/understanding-subqueries-and-ctes-120m</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/understanding-subqueries-and-ctes-120m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine you are a data analyst working for a retail company. Your manager asks you to identify customers who spend more than the average customer, employees earning above their department's average salary, or products generating the highest revenue.&lt;/p&gt;

&lt;p&gt;You already know how to retrieve data using SELECT, filter records using WHERE, and combine tables using JOIN. However, some business questions require you to perform multiple calculations before arriving at the final answer.&lt;/p&gt;

&lt;p&gt;For example, how would you identify employees earning above the company's average salary without knowing the average salary beforehand?&lt;/p&gt;

&lt;p&gt;You would first need to calculate the average salary and then use that result to identify employees whose salaries exceed it.&lt;/p&gt;

&lt;p&gt;This is where SQL Subqueries and Common Table Expressions (CTEs) come in.&lt;/p&gt;

&lt;p&gt;Both techniques allow you to break down complex analytical problems into smaller, more manageable queries. They help you perform intermediate calculations, filter records based on the results of other queries, and organize your SQL code more effectively.&lt;/p&gt;

&lt;p&gt;In this article, we will explore what subqueries and CTEs are, how they work, their key differences, and how to apply them to real-world data analysis problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Subqueries
&lt;/h2&gt;

&lt;p&gt;A subquery, also known as a nested query or inner query, is a SQL query written inside another SQL query.&lt;/p&gt;

&lt;p&gt;The inner query produces a result that the outer query uses to perform another operation.&lt;/p&gt;

&lt;p&gt;In simple terms, a subquery allows you to answer one question and use that answer to solve another question within the same SQL statement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax of a Subquery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SELECT column_name&lt;br&gt;
FROM table_name&lt;br&gt;
WHERE column_name operator (&lt;br&gt;
    SELECT column_name&lt;br&gt;
    FROM table_name&lt;br&gt;
);&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The query inside the parentheses is called the inner query or subquery, while the query surrounding it is called the outer query.&lt;/p&gt;

&lt;p&gt;For a simple, independent subquery, SQL can evaluate the inner query and use its result in the outer query. However, the actual execution strategy depends on the database engine and the type of subquery.&lt;/p&gt;

&lt;p&gt;Let's understand this concept using a practical example.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example 1: Finding Employees Earning Above the Average Salary
&lt;/h3&gt;

&lt;p&gt;Suppose we have an employees table containing the following information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;employee_id&lt;/th&gt;
    &lt;th&gt;employee_name&lt;/th&gt;
    &lt;th&gt;department&lt;/th&gt;
    &lt;th&gt;salary&lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;1&lt;/td&gt;
    &lt;td&gt;John&lt;/td&gt;
    &lt;td&gt;IT&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;Mary&lt;/td&gt;
    &lt;td&gt;HR&lt;/td&gt;
    &lt;td&gt;55000&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;3&lt;/td&gt;
    &lt;td&gt;Peter&lt;/td&gt;
    &lt;td&gt;IT&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;Jane&lt;/td&gt;
    &lt;td&gt;Finance&lt;/td&gt;
    &lt;td&gt;45000&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;td&gt;5&lt;/td&gt;
    &lt;td&gt;David&lt;/td&gt;
    &lt;td&gt;Finance&lt;/td&gt;
    &lt;td&gt;50000&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The HR manager wants to identify employees earning above the company's average salary.&lt;/p&gt;

&lt;p&gt;To solve this problem, we need to calculate the average salary and then compare each employee's salary against that average.&lt;/p&gt;
&lt;h5&gt;
  
  
  Step 1: Calculate the average salary.
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;SELECT AVG(salary) AS average_salary&lt;br&gt;
FROM employees;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AVG() function calculates the average salary across all employees.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;average_salary: 60000&lt;/strong&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Step 2: Use the average salary to identify employees earning above it.
&lt;/h5&gt;

&lt;p&gt;Instead of manually entering 60000 into another query, we can combine both operations using a subquery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SELECT&lt;br&gt;
    employee_name,&lt;br&gt;
    department,&lt;br&gt;
    salary&lt;br&gt;
FROM employees&lt;br&gt;
WHERE salary &amp;gt; (&lt;br&gt;
    SELECT AVG(salary)&lt;br&gt;
    FROM employees&lt;br&gt;
);&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How does this query work?&lt;/p&gt;

&lt;p&gt;The inner query:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SELECT AVG(salary)&lt;br&gt;
FROM employees;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;calculates the company's average salary.&lt;/p&gt;

&lt;p&gt;The outer query:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SELECT&lt;br&gt;
    employee_name,&lt;br&gt;
    department,&lt;br&gt;
    salary&lt;br&gt;
FROM employees&lt;br&gt;
WHERE salary &amp;gt; (...);&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;retrieves employees whose salaries exceed the value returned by the inner query.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;employee_name&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;/tr&gt;


&lt;tr&gt;
    &lt;td&gt;John&lt;/td&gt;
    &lt;td&gt;IT&lt;/td&gt;
    &lt;td&gt;80000&lt;/td&gt;
  &lt;/tr&gt;

&lt;tr&gt;
    &lt;td&gt;Peter&lt;/td&gt;
    &lt;td&gt;IT&lt;/td&gt;
    &lt;td&gt;70000&lt;/td&gt;
  &lt;/tr&gt;

&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that we did not need to enter the average salary manually. The query calculates it directly from the data.&lt;/p&gt;

&lt;p&gt;If the employee salaries change, running the query again will use the updated average salary.&lt;/p&gt;

&lt;p&gt;This makes subqueries particularly useful when working with dynamic data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Types of SQL Subqueries
&lt;/h3&gt;

&lt;p&gt;Subqueries can be classified according to the number of values they return and how they interact with the outer query.&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Scalar Subqueries
&lt;/h4&gt;

&lt;p&gt;A scalar subquery returns a single value, such as an average salary, maximum price, or total revenue.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;SELECT&lt;br&gt;
    employee_name,&lt;br&gt;
    salary&lt;br&gt;
FROM employees&lt;br&gt;
WHERE salary &amp;gt; (&lt;br&gt;
    SELECT AVG(salary)&lt;br&gt;
    FROM employees&lt;br&gt;
);&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, the subquery returns one value: the average salary.&lt;/p&gt;

&lt;p&gt;The outer query uses that value to filter employees.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Multiple-Row Subqueries
&lt;/h4&gt;

&lt;p&gt;A multiple-row subquery returns more than one row.&lt;/p&gt;

&lt;p&gt;These subqueries are commonly used with operators such as IN, ANY, and ALL.&lt;/p&gt;

&lt;p&gt;For example, suppose we have a departments table containing department names and locations.&lt;/p&gt;

&lt;p&gt;We want to identify employees working in departments located in Nairobi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SELECT&lt;br&gt;
    employee_name,&lt;br&gt;
    department&lt;br&gt;
FROM employees&lt;br&gt;
WHERE department IN (&lt;br&gt;
    SELECT department_name&lt;br&gt;
    FROM departments&lt;br&gt;
    WHERE location = 'Nairobi'&lt;br&gt;
);&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The inner query retrieves the names of departments located in Nairobi.&lt;/p&gt;

&lt;p&gt;The outer query then selects employees whose department appears in that list.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Correlated Subqueries
&lt;/h4&gt;

&lt;p&gt;A correlated subquery is a subquery that references a column from the outer query.&lt;/p&gt;

&lt;p&gt;Unlike an independent subquery, it depends on values supplied by the outer query.&lt;/p&gt;

&lt;p&gt;For example, suppose we want to identify employees earning above the average salary in their respective departments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SELECT&lt;br&gt;
    e.employee_name,&lt;br&gt;
    e.department,&lt;br&gt;
    e.salary&lt;br&gt;
FROM employees AS e&lt;br&gt;
WHERE e.salary &amp;gt; (&lt;br&gt;
    SELECT AVG(e2.salary)&lt;br&gt;
    FROM employees AS e2&lt;br&gt;
    WHERE e2.department = e.department&lt;br&gt;
);&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example, the subquery calculates the average salary for the department associated with each employee in the outer query.&lt;/p&gt;

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

&lt;p&gt;WHERE e2.department = e.department****&lt;/p&gt;

&lt;p&gt;connects the inner query to the outer query.&lt;/p&gt;

&lt;p&gt;This allows each employee's salary to be compared against the average salary of their own department rather than the company-wide average.&lt;/p&gt;

&lt;p&gt;Correlated subqueries are useful when performing comparisons within groups, such as comparing individual customer spending against the average spending of customers in the same region.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Common Table Expressions (CTEs)
&lt;/h2&gt;

&lt;p&gt;A Common Table Expression (CTE) is a temporary named result set defined within a SQL statement using the WITH keyword.&lt;/p&gt;

&lt;p&gt;A CTE allows you to write a query, give its result a meaningful name, and reference that result in the main SQL statement.&lt;/p&gt;

&lt;p&gt;Think of a CTE as creating a temporary working table that helps you organize a complex query into smaller, logical steps.&lt;/p&gt;

&lt;p&gt;Unlike a permanent database table, a CTE does not remain available for use by later, separate SQL statements.&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic Syntax of a CTE
&lt;/h3&gt;

&lt;p&gt;The basic structure of a Common Table Expression looks like 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;cte_name&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;column_name&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;condition&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;cte_name&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 break this down.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;WITH&lt;/code&gt; keyword tells SQL that we are defining a Common Table Expression.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cte_name&lt;/code&gt; is the name we give to the result produced by the query inside the parentheses.&lt;/p&gt;

&lt;p&gt;The main query can then reference the CTE using that name.&lt;/p&gt;

&lt;p&gt;In simple terms:&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 temporary result
    ↓
Give it a name
    ↓
Use that name in the main query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's apply this to our employee data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Finding Employees Earning Above the Average Salary Using a CTE
&lt;/h3&gt;

&lt;p&gt;Earlier, we solved this problem using a subquery:&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can solve the same problem using a CTE.&lt;/p&gt;

&lt;p&gt;First, we create a CTE that calculates the company's average 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;WITH&lt;/span&gt; &lt;span class="n"&gt;average_salary&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;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="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;average_salary&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_salary&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 understand what is happening.&lt;/p&gt;

&lt;p&gt;The CTE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;average_salary&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;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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;calculates the average salary and gives the result the name &lt;code&gt;average_salary&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually, it produces:&lt;/p&gt;

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

&lt;tr&gt;
&lt;td&gt;60000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The main query then uses that 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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;average_salary&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_salary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;employee_name&lt;/th&gt;
&lt;th&gt;department&lt;/th&gt;
&lt;th&gt;salary&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;John&lt;/td&gt;
&lt;td&gt;IT&lt;/td&gt;
&lt;td&gt;80000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Peter&lt;/td&gt;
&lt;td&gt;IT&lt;/td&gt;
&lt;td&gt;70000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We have therefore answered the same question using two different approaches.&lt;/p&gt;

&lt;p&gt;The subquery places the average calculation directly inside the &lt;code&gt;WHERE&lt;/code&gt; condition, while the CTE calculates the average first, gives the result a meaningful name, and then references that result in the main query.&lt;/p&gt;

&lt;p&gt;For a simple problem like this, the subquery may be shorter. However, as queries become more complex, CTEs can make SQL code easier to read and maintain.&lt;/p&gt;




&lt;h3&gt;
  
  
  Using a CTE to Summarize Data
&lt;/h3&gt;

&lt;p&gt;CTEs become particularly useful when we need to perform an intermediate calculation before carrying out further analysis.&lt;/p&gt;

&lt;p&gt;Suppose we have a &lt;code&gt;sales&lt;/code&gt; table containing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;15000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;25000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;30000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;20000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Management wants to identify customers who have spent more than KSh 30,000 in total.&lt;/p&gt;

&lt;p&gt;Before filtering the customers, we first need to calculate the total amount spent by each customer.&lt;/p&gt;

&lt;p&gt;We can use a CTE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;customer_spending&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer_spending&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CTE produces an intermediate result similar to:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;total_spent&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;45000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;45000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The main query then filters this 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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer_spending&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;giving us:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;total_spent&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;45000&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;45000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This demonstrates an important advantage of CTEs: we can perform one analytical step, give the result a meaningful name, and then continue our analysis using that result.&lt;/p&gt;




&lt;h3&gt;
  
  
  Using Multiple CTEs
&lt;/h3&gt;

&lt;p&gt;We are not limited to creating only one CTE.&lt;/p&gt;

&lt;p&gt;SQL allows us to define multiple CTEs within the same statement by separating them with commas.&lt;/p&gt;

&lt;p&gt;The general structure is:&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;cte_one&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="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;cte_two&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="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="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;cte_one&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;cte_two&lt;/span&gt;
    &lt;span class="k"&gt;ON&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 be particularly useful when solving a problem that contains several analytical steps.&lt;/p&gt;

&lt;p&gt;Suppose we want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Calculate the total amount spent by each customer.&lt;/li&gt;
&lt;li&gt;Calculate the average customer spending.&lt;/li&gt;
&lt;li&gt;Identify customers who spent more than the average customer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can break the problem into logical steps.&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;customer_spending&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;average_spending&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt;
        &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_spent&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_spent&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer_spending&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;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customer_spending&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;
&lt;span class="k"&gt;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;average_spending&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_spent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_spent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the second CTE can reference the result produced by the first CTE.&lt;/p&gt;

&lt;p&gt;Conceptually, our query follows this process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Raw Sales Data
      ↓
Calculate spending per customer
      ↓
Calculate average customer spending
      ↓
Compare each customer with the average
      ↓
Return above-average customers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of trying to solve the entire problem in one deeply nested query, CTEs allow us to express the analysis as a sequence of understandable steps.&lt;/p&gt;

&lt;p&gt;This is one of the main reasons CTEs are popular when writing analytical SQL.&lt;/p&gt;




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

&lt;p&gt;At this point, you may be wondering:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If both subqueries and CTEs can produce intermediate results, which one should I use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer depends on the problem you are solving.&lt;/p&gt;

&lt;p&gt;Consider our original employee example.&lt;/p&gt;

&lt;p&gt;Using a subquery:&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_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;salary&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a CTE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;average_salary&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;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="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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;CROSS&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;average_salary&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_salary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both can produce the same result.&lt;/p&gt;

&lt;p&gt;The main difference here is how the query is organized.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Subquery&lt;/th&gt;
&lt;th&gt;CTE&lt;/th&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Location&lt;/td&gt;
&lt;td&gt;Nested inside another query&lt;/td&gt;
&lt;td&gt;Defined before the main query using WITH&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Readability&lt;/td&gt;
&lt;td&gt;Good for simple queries&lt;/td&gt;
&lt;td&gt;Often clearer for complex queries&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Nesting&lt;/td&gt;
&lt;td&gt;Can become difficult to read when heavily nested&lt;/td&gt;
&lt;td&gt;Can break complex logic into named steps&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Reuse within the same statement&lt;/td&gt;
&lt;td&gt;Often requires repeating the subquery&lt;/td&gt;
&lt;td&gt;A CTE can often be referenced multiple times&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Recursive queries&lt;/td&gt;
&lt;td&gt;Not normally used for recursion&lt;/td&gt;
&lt;td&gt;Recursive CTEs can handle hierarchical problems&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Lifetime&lt;/td&gt;
&lt;td&gt;Exists only as part of its containing statement&lt;/td&gt;
&lt;td&gt;Exists only for the statement in which it is defined&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It is important to note that a CTE is &lt;strong&gt;not automatically faster&lt;/strong&gt; than a subquery.&lt;/p&gt;

&lt;p&gt;Modern database engines have query optimizers that determine how SQL statements should actually be executed. Depending on the database system and the query, a CTE may be inlined, materialized, or optimized in another way.&lt;/p&gt;

&lt;p&gt;Therefore, CTEs should not automatically be chosen because they are assumed to improve performance.&lt;/p&gt;

&lt;p&gt;Their biggest advantage is often &lt;strong&gt;clarity and organization&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Should You Use a Subquery?
&lt;/h2&gt;

&lt;p&gt;Subqueries work particularly well when the intermediate calculation is relatively simple and is needed in only one place.&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;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;price&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;price&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;products&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic is straightforward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Find the average price and return products priced above it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing a separate CTE may add unnecessary complexity to such a simple query.&lt;/p&gt;

&lt;p&gt;Subqueries are commonly useful when working with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WHERE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HAVING&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FROM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXISTS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NOT EXISTS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Comparison operators such as &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, and &lt;code&gt;=&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When Should You Use a CTE?
&lt;/h2&gt;

&lt;p&gt;CTEs become especially useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A query contains several logical steps.&lt;/li&gt;
&lt;li&gt;The same intermediate result needs to be referenced more than once.&lt;/li&gt;
&lt;li&gt;Nested subqueries are becoming difficult to understand.&lt;/li&gt;
&lt;li&gt;You want to give intermediate calculations meaningful names.&lt;/li&gt;
&lt;li&gt;You are working with hierarchical or recursive data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, names such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_spending
monthly_revenue
department_average
top_customers
regional_sales
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;immediately communicate what each part of the query represents.&lt;/p&gt;

&lt;p&gt;Compare that with several layers of unnamed nested queries.&lt;/p&gt;

&lt;p&gt;A well-structured CTE can make complex analytical SQL read almost like a sequence of instructions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes When Using Subqueries and CTEs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Returning Multiple Values Where One Is Expected
&lt;/h3&gt;

&lt;p&gt;Consider:&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;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="o"&gt;=&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;salary&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'IT'&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 IT department contains several employees with different salaries, the subquery may return multiple rows.&lt;/p&gt;

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

&lt;p&gt;Depending on the intended question, an operator such as &lt;code&gt;IN&lt;/code&gt; may be more appropriate:&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;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="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;salary&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'IT'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always think about whether your subquery is expected to return &lt;strong&gt;one value, one row, or multiple rows&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Making Subqueries Unnecessarily Deep
&lt;/h3&gt;

&lt;p&gt;A query containing many levels of nested subqueries can quickly become difficult to understand.&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;Query
 └── Subquery
      └── Subquery
           └── Subquery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the logic becomes difficult to follow, consider restructuring the query using CTEs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Forgetting That a CTE Is Temporary
&lt;/h3&gt;

&lt;p&gt;A CTE is available only to the SQL statement immediately associated with 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 sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;customer_spending&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;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;customer_spending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this statement finishes, you cannot run a separate query such as:&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;customer_spending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and expect the CTE still to exist.&lt;/p&gt;

&lt;p&gt;If you need to permanently store a result, you may need a table, view, or another database object depending on your requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Assuming CTEs Are Always Faster
&lt;/h3&gt;

&lt;p&gt;CTEs are excellent for organizing SQL, but they do not automatically improve query performance.&lt;/p&gt;

&lt;p&gt;Performance depends on factors such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The database management system&lt;/li&gt;
&lt;li&gt;Available indexes&lt;/li&gt;
&lt;li&gt;Table sizes&lt;/li&gt;
&lt;li&gt;Join conditions&lt;/li&gt;
&lt;li&gt;Filtering&lt;/li&gt;
&lt;li&gt;Aggregations&lt;/li&gt;
&lt;li&gt;Query execution plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use CTEs primarily when they make your SQL logic clearer, and investigate the execution plan when performance matters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Putting Everything Together
&lt;/h2&gt;

&lt;p&gt;Subqueries and CTEs ultimately help us solve the same fundamental problem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can we use the result of one query as part of a larger analytical question?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A subquery places one query inside another:&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
     ↓
(Subquery)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A CTE takes a more step-by-step approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH intermediate_result AS (...)
              ↓
         Main Query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither approach is universally better.&lt;/p&gt;

&lt;p&gt;For a short calculation that is used once, a subquery may be the simplest solution.&lt;/p&gt;

&lt;p&gt;For an analysis containing several intermediate calculations, a CTE can make the logic significantly easier to follow.&lt;/p&gt;




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

&lt;p&gt;As SQL queries become more advanced, business questions rarely require just one simple &lt;code&gt;SELECT&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;You may need to calculate an average before filtering records, summarize customer transactions before comparing spending patterns, or perform several intermediate calculations before producing the final result.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;subqueries and Common Table Expressions (CTEs)&lt;/strong&gt; become valuable.&lt;/p&gt;

&lt;p&gt;Subqueries allow us to place one query inside another and use the result as part of a larger SQL operation. They are particularly useful for concise calculations and filtering based on dynamically generated values.&lt;/p&gt;

&lt;p&gt;CTEs, on the other hand, allow us to break more complex SQL logic into &lt;strong&gt;named, logical steps&lt;/strong&gt; using the &lt;code&gt;WITH&lt;/code&gt; keyword. This can make analytical queries easier to read, understand, debug, and maintain.&lt;/p&gt;

&lt;p&gt;The easiest way to remember the distinction is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A subquery nests the logic, while a CTE names and organizes the logic.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a data analyst, you do not need to choose one approach for every situation. The important skill is understanding the problem, recognizing the shape of the intermediate result you need, and selecting the approach that expresses your logic most clearly.&lt;/p&gt;

&lt;p&gt;Once you are comfortable with subqueries and CTEs, you are ready to tackle more advanced SQL techniques such as &lt;strong&gt;window functions, recursive CTEs, ranking, and multi-step analytical queries&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>database</category>
      <category>data</category>
      <category>beginners</category>
    </item>
    <item>
      <title>WHAT IS SQL JOINS</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Sun, 20 Sep 2026 16:03:04 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/what-is-sql-joins-3i65</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/what-is-sql-joins-3i65</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine walking into a car dealership and asking the manager:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Which customers bought which cars, how much did they spend, and which salesperson handled each sale?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The information needed to answer this question might already exist in the company's database — but there is a catch.&lt;/p&gt;

&lt;p&gt;It is not necessarily stored in one place.&lt;/p&gt;

&lt;p&gt;Customer information might be stored in a &lt;code&gt;customers&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Information about sales might be stored separately in a &lt;code&gt;sales&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;car_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;C10&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;C15&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;C12&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And information about the cars might exist in yet another table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;car_id&lt;/th&gt;
&lt;th&gt;make&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C10&lt;/td&gt;
&lt;td&gt;Toyota&lt;/td&gt;
&lt;td&gt;Hilux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C12&lt;/td&gt;
&lt;td&gt;Ford&lt;/td&gt;
&lt;td&gt;Ranger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C15&lt;/td&gt;
&lt;td&gt;Mazda&lt;/td&gt;
&lt;td&gt;CX-5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each table tells us something useful, but none of them can answer our original question on its own.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;customers&lt;/code&gt; table knows &lt;strong&gt;who the customers are&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sales&lt;/code&gt; table knows &lt;strong&gt;what transactions occurred&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;cars&lt;/code&gt; table knows &lt;strong&gt;which vehicles were sold&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What connects these pieces of information?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL JOINs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A JOIN allows us to combine related information stored across different tables by identifying columns that connect those tables.&lt;/p&gt;

&lt;p&gt;In our example, &lt;code&gt;customer_id&lt;/code&gt; connects customers to their sales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customers                         sales
─────────                         ─────
customer_id ───────────────────► customer_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, &lt;code&gt;car_id&lt;/code&gt; connects a sale to information about the vehicle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sales                             cars
─────                             ────
car_id ─────────────────────────► car_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once these relationships are understood, SQL can bring the information together and allow us to answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which customer purchased each vehicle?&lt;/li&gt;
&lt;li&gt;Which cars generated the most revenue?&lt;/li&gt;
&lt;li&gt;Which customers have never made a purchase?&lt;/li&gt;
&lt;li&gt;Which vehicles have never been sold?&lt;/li&gt;
&lt;li&gt;How much has each customer spent?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the reasons JOINs are such an important SQL skill. Real-world databases rarely store everything in one giant table. Instead, data is often separated into related tables, and analysts need to know how to bring the right information together.&lt;/p&gt;

&lt;p&gt;In this article, we will explore the major types of SQL JOINs — &lt;strong&gt;INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, CROSS JOIN, and SELF JOIN&lt;/strong&gt; — using practical examples to understand not only &lt;strong&gt;how they work, but when and why we use them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let's connect the tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Join: Understanding Keys and Table Relationships
&lt;/h2&gt;

&lt;p&gt;Before writing our first SQL JOIN, we need to understand one important question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does SQL know which rows from one table should be connected to rows in another table?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer lies in &lt;strong&gt;keys&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Primary Keys
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;primary key&lt;/strong&gt; is a column, or combination of columns, that uniquely identifies each record in a table.&lt;/p&gt;

&lt;p&gt;Consider our &lt;code&gt;customers&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;customer_id&lt;/code&gt; is the primary key.&lt;/p&gt;

&lt;p&gt;Each customer has a unique ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;James Mwangi  → 101
Faith Chebet  → 102
Mary Atieno   → 103
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if two customers happen to have the same name, their &lt;code&gt;customer_id&lt;/code&gt; values should still be different.&lt;/p&gt;

&lt;p&gt;A table can be created with a primary key like 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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="nb"&gt;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;customer_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;county&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PRIMARY KEY&lt;/code&gt; constraint tells the database that &lt;code&gt;customer_id&lt;/code&gt; should uniquely identify each customer.&lt;/p&gt;




&lt;h3&gt;
  
  
  Foreign Keys
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;foreign key&lt;/strong&gt; is a column in one table that references a primary key, or another unique key, in another table.&lt;/p&gt;

&lt;p&gt;Consider our &lt;code&gt;sales&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;car_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;C10&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;C15&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;C12&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;customer_id&lt;/code&gt; column appears here again.&lt;/p&gt;

&lt;p&gt;But this time, it is being used to identify &lt;strong&gt;which customer made each purchase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We can visualize the connection 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;CUSTOMERS                         SALES

customer_id                      customer_id
───────────                      ───────────
101 James Mwangi  ────────────► 101
102 Faith Chebet  ────────────► 102
103 Mary Atieno   ────────────► 103
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;customers&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id = Primary Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;sales&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customer_id = Foreign Key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This relationship allows us to connect customer information with sales information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Relationship
&lt;/h2&gt;

&lt;p&gt;One customer can make several purchases.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Customers&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;customer_id&lt;/th&gt;
&lt;th&gt;customer_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;James Mwangi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sales&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;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;2,200,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice that &lt;code&gt;customer_id 101&lt;/code&gt; appears twice in the &lt;code&gt;sales&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;That is perfectly acceptable because James has made two purchases.&lt;/p&gt;

&lt;p&gt;The relationship can therefore be described as:&lt;br&gt;
&lt;/p&gt;

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

    ONE                        MANY

     101  ────────────────┬── S001
                          │
                          └── S002
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is known as a &lt;strong&gt;one-to-many relationship&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One customer can be associated with many sales, while each sale in this example belongs to one customer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The JOIN Condition
&lt;/h2&gt;

&lt;p&gt;Now that we know how the tables are related, we can tell SQL how to connect them.&lt;/p&gt;

&lt;p&gt;This is done using the &lt;code&gt;ON&lt;/code&gt; condition.&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="n"&gt;customers&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;sales&lt;/span&gt;
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&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;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important part for now is:&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;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&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;customer_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are essentially telling SQL:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Match a customer with a sale whenever their &lt;code&gt;customer_id&lt;/code&gt; values are equal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SQL compares the values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customers.customer_id       sales.customer_id

        101        =               101       ✓
        102        =               102       ✓
        103        =               103       ✓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once SQL finds the matching values, it can combine information from both tables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Not Join Using Customer Names?
&lt;/h2&gt;

&lt;p&gt;You might wonder why we cannot simply 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;ON&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_name&lt;/span&gt; &lt;span class="o"&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;customer_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Names are not always reliable identifiers.&lt;/p&gt;

&lt;p&gt;Two customers could have the same name, names may be misspelled, or a customer's name could change.&lt;/p&gt;

&lt;p&gt;IDs are generally more reliable for establishing relationships between tables.&lt;/p&gt;

&lt;p&gt;That is why relational databases commonly use &lt;strong&gt;primary and foreign keys&lt;/strong&gt; to connect related records.&lt;/p&gt;




&lt;h2&gt;
  
  
  Our Database Relationship
&lt;/h2&gt;

&lt;p&gt;For the examples in this article, we will work with three tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CUSTOMERS
──────────────
customer_id (PK)
customer_name
county
       │
       │ customer_id
       ▼
SALES
──────────────
sale_id (PK)
customer_id (FK)
car_id (FK)
amount
       │
       │ car_id
       ▼
CARS
──────────────
car_id (PK)
make
model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PK&lt;/code&gt; represents a &lt;strong&gt;Primary Key&lt;/strong&gt;, while &lt;code&gt;FK&lt;/code&gt; represents a &lt;strong&gt;Foreign Key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now that we understand how the tables are connected, we are ready to start joining them.&lt;/p&gt;

&lt;p&gt;The first and perhaps most fundamental type of JOIN we will explore is the &lt;strong&gt;INNER JOIN&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. INNER JOIN — Finding Matching Records
&lt;/h2&gt;

&lt;p&gt;Now that we understand how tables are connected using keys, let's look at our first type of JOIN: the &lt;strong&gt;INNER JOIN&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; returns only the records that have &lt;strong&gt;matching values in both tables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If a record does not have a match in both tables, it will not appear in the result.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's look at an example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our Customers Table
&lt;/h3&gt;

&lt;p&gt;Suppose we have the following customers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Our Sales Table
&lt;/h3&gt;

&lt;p&gt;Now consider the sales records:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&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;Customers &lt;code&gt;101&lt;/code&gt;, &lt;code&gt;102&lt;/code&gt;, and &lt;code&gt;103&lt;/code&gt; have corresponding records in the &lt;code&gt;sales&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;However, customer &lt;code&gt;104&lt;/code&gt;, Brian Otieno, does not have a sales record.&lt;/p&gt;

&lt;p&gt;If we use an &lt;code&gt;INNER JOIN&lt;/code&gt;, SQL will return only customers who have matching sales records.&lt;/p&gt;

&lt;h3&gt;
  
  
  INNER JOIN Syntax
&lt;/h3&gt;

&lt;p&gt;We can join the two tables using:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="e78n4k"&lt;br&gt;
SELECT&lt;br&gt;
    customers.customer_name,&lt;br&gt;
    customers.county,&lt;br&gt;
    sales.sale_id,&lt;br&gt;
    sales.amount&lt;br&gt;
FROM customers&lt;br&gt;
INNER JOIN sales&lt;br&gt;
    ON customers.customer_id = sales.customer_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The most important part is:



```sql id="6a3c2f"
ON customers.customer_id = sales.customer_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tells SQL to compare the &lt;code&gt;customer_id&lt;/code&gt; column in the &lt;code&gt;customers&lt;/code&gt; table with the &lt;code&gt;customer_id&lt;/code&gt; column in the &lt;code&gt;sales&lt;/code&gt; table.&lt;/p&gt;

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

&lt;p&gt;```text id="z4ub62"&lt;br&gt;
CUSTOMERS                         SALES&lt;/p&gt;

&lt;p&gt;101 James Mwangi  ────────────── 101 S001 ✓&lt;br&gt;
102 Faith Chebet  ────────────── 102 S003 ✓&lt;br&gt;
103 Mary Atieno   ────────────── 103 S002 ✓&lt;br&gt;
104 Brian Otieno                 No match ✗&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Because Brian does not have a matching record in `sales`, he will not appear in the result.

### Result

The query produces:

| customer_name | county  | sale_id |    amount |
| ------------- | ------- | ------- | --------: |
| James Mwangi  | Nairobi | S001    | 3,500,000 |
| Faith Chebet  | Nakuru  | S003    | 4,100,000 |
| Mary Atieno   | Kisumu  | S002    | 2,800,000 |

Only records with matches in **both tables** have been returned.

---

### Using Table Aliases

Writing the complete table names repeatedly can make SQL queries unnecessarily long.

Instead of:



```sql id="8m4qu7"
SELECT
    customers.customer_name,
    customers.county,
    sales.sale_id,
    sales.amount
FROM customers
INNER JOIN sales
    ON customers.customer_id = sales.customer_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;we can assign shorter names called &lt;strong&gt;aliases&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="qk7m9p"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_name,&lt;br&gt;
    c.county,&lt;br&gt;
    s.sale_id,&lt;br&gt;
    s.amount&lt;br&gt;
FROM customers AS c&lt;br&gt;
INNER JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Here:



```text id="5o8by4"
c → customers
s → sales
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The query performs exactly the same operation, but it is shorter and easier to read.&lt;/p&gt;

&lt;p&gt;Aliases become particularly useful when joining several tables.&lt;/p&gt;


&lt;h2&gt;
  
  
  Joining More Than Two Tables
&lt;/h2&gt;

&lt;p&gt;What if we also want to know &lt;strong&gt;which car each customer purchased?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;sales&lt;/code&gt; table contains a &lt;code&gt;car_id&lt;/code&gt;, which connects it to the &lt;code&gt;cars&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;We can therefore join all three tables:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="6v8q3f"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_name,&lt;br&gt;
    c.county,&lt;br&gt;
    ca.make,&lt;br&gt;
    ca.model,&lt;br&gt;
    s.amount&lt;br&gt;
FROM sales AS s&lt;br&gt;
INNER JOIN customers AS c&lt;br&gt;
    ON s.customer_id = c.customer_id&lt;br&gt;
INNER JOIN cars AS ca&lt;br&gt;
    ON s.car_id = ca.car_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Now SQL follows the relationships:



```text id="79om1b"
CUSTOMERS
    │
    │ customer_id
    ▼
  SALES
    │
    │ car_id
    ▼
   CARS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;th&gt;make&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;td&gt;Toyota&lt;/td&gt;
&lt;td&gt;Hilux&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;td&gt;Ford&lt;/td&gt;
&lt;td&gt;Ranger&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;td&gt;Mazda&lt;/td&gt;
&lt;td&gt;CX-5&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can now answer a much more meaningful business question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which customer purchased which car, and how much did they spend?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This demonstrates the real power of SQL JOINs. Information that was separated across multiple tables can now be analysed together.&lt;/p&gt;


&lt;h2&gt;
  
  
  When Would You Use an INNER JOIN?
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; is useful when you are interested only in records that have corresponding information in both tables.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Customers who have made purchases&lt;/li&gt;
&lt;li&gt;Employees assigned to departments&lt;/li&gt;
&lt;li&gt;Products that have been ordered&lt;/li&gt;
&lt;li&gt;Students enrolled in courses&lt;/li&gt;
&lt;li&gt;Drivers who have completed trips&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple way to remember it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;INNER JOIN = Give me the matches.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what happens if we want to see &lt;strong&gt;all customers&lt;/strong&gt;, including those who have never purchased anything?&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; cannot give us that complete picture.&lt;/p&gt;

&lt;p&gt;For that, we need our next JOIN: the &lt;strong&gt;LEFT JOIN&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. LEFT JOIN — Keeping Everything from the Left Table
&lt;/h2&gt;

&lt;p&gt;In the previous section, we saw that an &lt;code&gt;INNER JOIN&lt;/code&gt; returns only records that have matching values in both tables.&lt;/p&gt;

&lt;p&gt;But sometimes we want a different answer.&lt;/p&gt;

&lt;p&gt;Suppose management asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Show me all our customers, including those who have never made a purchase.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An &lt;code&gt;INNER JOIN&lt;/code&gt; would not work because customers without a matching sales record would be excluded.&lt;/p&gt;

&lt;p&gt;This is where a &lt;strong&gt;LEFT JOIN&lt;/strong&gt; becomes useful.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;LEFT JOIN&lt;/code&gt; returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;All records from the left table&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Matching records from the right table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NULL&lt;/code&gt; where a record from the left table has no corresponding match in the right table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple way to remember this is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;LEFT JOIN = Keep everything on the left and bring matching information from the right.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Our Two Tables
&lt;/h3&gt;

&lt;p&gt;Let's continue using the &lt;code&gt;customers&lt;/code&gt; and &lt;code&gt;sales&lt;/code&gt; tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customers&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;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sales&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;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice again that Brian Otieno (&lt;code&gt;customer_id = 104&lt;/code&gt;) does not have a corresponding record in the &lt;code&gt;sales&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;With an &lt;code&gt;INNER JOIN&lt;/code&gt;, Brian was excluded.&lt;/p&gt;

&lt;p&gt;Let's see what happens with a &lt;code&gt;LEFT JOIN&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  LEFT JOIN Syntax
&lt;/h3&gt;



&lt;p&gt;```sql id="w5w92z"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_id,&lt;br&gt;
    c.customer_name,&lt;br&gt;
    c.county,&lt;br&gt;
    s.sale_id,&lt;br&gt;
    s.amount&lt;br&gt;
FROM customers AS c&lt;br&gt;
LEFT JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Here, `customers` is the **left table** because it appears before `LEFT JOIN`.



```text id="mmx2r8"
customers                     sales
   LEFT                       RIGHT
     │                          │
     └──────── LEFT JOIN ───────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;SQL keeps every record from &lt;code&gt;customers&lt;/code&gt; and then looks for matching sales information.&lt;/p&gt;

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

&lt;p&gt;```text id="77dtb4"&lt;br&gt;
CUSTOMERS                         SALES&lt;/p&gt;

&lt;p&gt;101 James Mwangi  ────────────── 101 S001 ✓&lt;br&gt;
102 Faith Chebet  ────────────── 102 S003 ✓&lt;br&gt;
103 Mary Atieno   ────────────── 103 S002 ✓&lt;br&gt;
104 Brian Otieno                 No match&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The important difference is that Brian is **not removed** from the result.

### Result

| customer_id | customer_name | county  | sale_id |    amount |
| ----------: | ------------- | ------- | ------- | --------: |
|         101 | James Mwangi  | Nairobi | S001    | 3,500,000 |
|         102 | Faith Chebet  | Nakuru  | S003    | 4,100,000 |
|         103 | Mary Atieno   | Kisumu  | S002    | 2,800,000 |
|         104 | Brian Otieno  | Mombasa | NULL    |      NULL |

Because Brian has no matching sale, SQL cannot provide a `sale_id` or `amount`.

Instead, those values appear as:



```text id="0n3m8g"
NULL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In SQL, &lt;code&gt;NULL&lt;/code&gt; represents a &lt;strong&gt;missing or unknown value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not the same as:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="cf1e11"&lt;br&gt;
0&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


and it is not the same as an empty string.

In this example, the `NULL` values tell us something useful: **Brian has no matching sales record.**

---

## Finding Customers Who Have Never Purchased

LEFT JOIN becomes especially useful when we combine it with `IS NULL`.

Suppose management asks:

&amp;gt; **“Which customers have never made a purchase?”**

We can write:



```sql id="xxo5ox"
SELECT
    c.customer_id,
    c.customer_name,
    c.county
FROM customers AS c
LEFT JOIN sales AS s
    ON c.customer_id = s.customer_id
WHERE s.customer_id IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This technique is extremely useful in real-world analysis.&lt;/p&gt;

&lt;p&gt;For example, we could use it to find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customers who have never purchased&lt;/li&gt;
&lt;li&gt;Products that have never been sold&lt;/li&gt;
&lt;li&gt;Employees who have not been assigned to a department&lt;/li&gt;
&lt;li&gt;Vehicles that have never completed a trip&lt;/li&gt;
&lt;li&gt;Students who have not registered for a course&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What Happens When One Customer Has Multiple Sales?
&lt;/h2&gt;

&lt;p&gt;Suppose James makes another purchase.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;sales&lt;/code&gt; table becomes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S004&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;2,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When we run our &lt;code&gt;LEFT JOIN&lt;/code&gt;, James appears twice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;S004&lt;/td&gt;
&lt;td&gt;2,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;NULL&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;This is not SQL accidentally creating a duplicate.&lt;/p&gt;

&lt;p&gt;James has &lt;strong&gt;two matching records&lt;/strong&gt; in the &lt;code&gt;sales&lt;/code&gt; table, so SQL returns one result row for each match.&lt;/p&gt;

&lt;p&gt;This is an important concept when working with one-to-many relationships.&lt;/p&gt;


&lt;h2&gt;
  
  
  INNER JOIN vs LEFT JOIN
&lt;/h2&gt;

&lt;p&gt;We can now clearly see the difference between the first two JOINs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;INNER JOIN&lt;/th&gt;
&lt;th&gt;LEFT JOIN&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Returns matching records only&lt;/td&gt;
&lt;td&gt;Returns all left-table records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unmatched left records disappear&lt;/td&gt;
&lt;td&gt;Unmatched left records remain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful when only matches matter&lt;/td&gt;
&lt;td&gt;Useful when missing matches also matter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Consider Brian:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="97cx92"&lt;br&gt;
                    INNER JOIN      LEFT JOIN&lt;/p&gt;

&lt;p&gt;Brian Otieno            ✗               ✓&lt;br&gt;
Sales record            ✗               NULL&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


So if the question is:

**“Which customers have purchased something?”**

an `INNER JOIN` may be appropriate.

But if the question is:

**“Show me every customer and their purchases, if they have any,”**

a `LEFT JOIN` is more appropriate.

---

## A Common LEFT JOIN Mistake

Suppose we want all customers but only sales above KSh 3,000,000.

Consider:



```sql id="47e0f3"
SELECT
    c.customer_name,
    s.amount
FROM customers AS c
LEFT JOIN sales AS s
    ON c.customer_id = s.customer_id
WHERE s.amount &amp;gt; 3000000;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;WHERE&lt;/code&gt; condition removes rows where &lt;code&gt;s.amount&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;. As a result, customers without matching sales disappear, which can make the result behave more like an &lt;code&gt;INNER JOIN&lt;/code&gt; for this condition.&lt;/p&gt;

&lt;p&gt;If our intention is to retain all customers while matching only sales above KSh 3,000,000, we can place the condition inside the &lt;code&gt;ON&lt;/code&gt; clause:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="8oy89a"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_name,&lt;br&gt;
    s.amount&lt;br&gt;
FROM customers AS c&lt;br&gt;
LEFT JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id&lt;br&gt;
    AND s.amount &amp;gt; 3000000;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This preserves all customers from the left table while restricting which sales records qualify as matches.

Understanding where filtering happens is important when working with outer joins.

---

## When Should You Use a LEFT JOIN?

Use a `LEFT JOIN` when you want to keep **all records from your main table**, regardless of whether corresponding information exists in another table.

For a data analyst, LEFT JOIN is particularly useful when investigating **missing activity**.

For example:



```text id="6dq1k4"
All Customers
      ↓
LEFT JOIN
      ↓
Sales
      ↓
Customers with purchases
+
Customers without purchases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A simple rule to remember is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;INNER JOIN asks: “Which records match?”&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;LEFT JOIN asks: “Give me everything on the left, whether it matches or not.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have now seen what happens when we preserve the table on the &lt;strong&gt;left&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But what if we want to preserve every record from the table on the &lt;strong&gt;right&lt;/strong&gt; instead?&lt;/p&gt;

&lt;p&gt;That brings us to the &lt;strong&gt;RIGHT JOIN&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. RIGHT JOIN — Keeping Everything from the Right Table
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; works similarly to a &lt;code&gt;LEFT JOIN&lt;/code&gt;, but instead of keeping every record from the table on the left, it keeps &lt;strong&gt;every record from the table on the right&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;RIGHT JOIN&lt;/code&gt; returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All records from the &lt;strong&gt;right table&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Matching records from the &lt;strong&gt;left table&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NULL&lt;/code&gt; values where a right-table record does not have a corresponding match in the left table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple way to remember it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RIGHT JOIN = Keep everything on the right and bring matching information from the left.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's continue with our &lt;code&gt;customers&lt;/code&gt; and &lt;code&gt;sales&lt;/code&gt; example.&lt;/p&gt;
&lt;h3&gt;
  
  
  Customers Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Sales Table
&lt;/h3&gt;

&lt;p&gt;This time, imagine our &lt;code&gt;sales&lt;/code&gt; table contains:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S004&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;2,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice something unusual.&lt;/p&gt;

&lt;p&gt;The sale &lt;code&gt;S004&lt;/code&gt; references &lt;code&gt;customer_id = 105&lt;/code&gt;, but customer &lt;code&gt;105&lt;/code&gt; does not appear in our &lt;code&gt;customers&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;This could represent a data-quality problem, imported historical data, or—in a database without an enforced foreign-key constraint—an unmatched record.&lt;/p&gt;

&lt;p&gt;Let's see what happens when we use a &lt;code&gt;RIGHT JOIN&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  RIGHT JOIN Syntax
&lt;/h3&gt;



&lt;p&gt;```sql id="7d4n23"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_id,&lt;br&gt;
    c.customer_name,&lt;br&gt;
    c.county,&lt;br&gt;
    s.sale_id,&lt;br&gt;
    s.amount&lt;br&gt;
FROM customers AS c&lt;br&gt;
RIGHT JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Here:



```text id="32j8fk"
customers                     sales
   LEFT                       RIGHT
     │                          │
     └──────── RIGHT JOIN ──────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because &lt;code&gt;sales&lt;/code&gt; is the table on the &lt;strong&gt;right&lt;/strong&gt;, every sales record will be retained.&lt;/p&gt;

&lt;p&gt;SQL looks for matching customers:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="yk39du"&lt;br&gt;
CUSTOMERS                         SALES&lt;/p&gt;

&lt;p&gt;101 James Mwangi  ────────────── 101 S001 ✓&lt;br&gt;
102 Faith Chebet  ────────────── 102 S003 ✓&lt;br&gt;
103 Mary Atieno   ────────────── 103 S002 ✓&lt;br&gt;
                                  105 S004 ✗&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The sale for customer `105` does not have a matching customer record.

However, because we are using a `RIGHT JOIN`, the sale is still included.

### Result

| customer_id | customer_name | county  | sale_id |    amount |
| ----------: | ------------- | ------- | ------- | --------: |
|         101 | James Mwangi  | Nairobi | S001    | 3,500,000 |
|         103 | Mary Atieno   | Kisumu  | S002    | 2,800,000 |
|         102 | Faith Chebet  | Nakuru  | S003    | 4,100,000 |
|        NULL | NULL          | NULL    | S004    | 2,500,000 |

Notice the final row.

The sales information exists:



```text id="79hwha"
S004 → KSh 2,500,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;but SQL cannot find corresponding customer information.&lt;/p&gt;

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

&lt;p&gt;```text id="i1a68m"&lt;br&gt;
customer_name → NULL&lt;br&gt;
county        → NULL&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


---

## Finding Sales Without Matching Customers

This can be useful when checking data quality.

For example:



```sql id="dhkqz5"
SELECT
    s.sale_id,
    s.customer_id,
    s.amount
FROM customers AS c
RIGHT JOIN sales AS s
    ON c.customer_id = s.customer_id
WHERE c.customer_id IS NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S004&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;2,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This tells us that sale &lt;code&gt;S004&lt;/code&gt; references a customer that cannot be found in the &lt;code&gt;customers&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;As a data analyst, this could be something worth investigating before performing further analysis.&lt;/p&gt;


&lt;h2&gt;
  
  
  RIGHT JOIN vs LEFT JOIN
&lt;/h2&gt;

&lt;p&gt;The main difference is simply &lt;strong&gt;which table must be preserved&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="3ew49g"&lt;br&gt;
LEFT JOIN&lt;/p&gt;

&lt;p&gt;CUSTOMERS  ← Keep everything&lt;br&gt;
    │&lt;br&gt;
    └──────── SALES&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Compared with:



```text id="rrk6mm"
RIGHT JOIN

CUSTOMERS ────────┐
                  │
              SALES ← Keep everything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can summarize them as:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LEFT JOIN&lt;/th&gt;
&lt;th&gt;RIGHT JOIN&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Keeps all rows from the left table&lt;/td&gt;
&lt;td&gt;Keeps all rows from the right table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matches information from the right&lt;/td&gt;
&lt;td&gt;Matches information from the left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missing right-side values become &lt;code&gt;NULL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Missing left-side values become &lt;code&gt;NULL&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Can a RIGHT JOIN Be Written as a LEFT JOIN?
&lt;/h2&gt;

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

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

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

&lt;p&gt;```sql id="tg0sgr"&lt;br&gt;
SELECT *&lt;br&gt;
FROM customers AS c&lt;br&gt;
RIGHT JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


We could simply reverse the table order:



```sql id="32frst"
SELECT *
FROM sales AS s
LEFT JOIN customers AS c
    ON s.customer_id = c.customer_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Both queries express the same basic requirement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep every sales record and bring in customer information where a matching customer exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because of this, many SQL developers and analysts prefer &lt;code&gt;LEFT JOIN&lt;/code&gt; for readability and consistency and simply change which table appears first.&lt;/p&gt;

&lt;p&gt;Nevertheless, understanding &lt;code&gt;RIGHT JOIN&lt;/code&gt; is important because you will encounter it in existing SQL queries and technical assessments.&lt;/p&gt;


&lt;h2&gt;
  
  
  When Should You Use a RIGHT JOIN?
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;RIGHT JOIN&lt;/code&gt; can be useful when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every record from the right table must be retained.&lt;/li&gt;
&lt;li&gt;You want to identify records on the right that have no corresponding match on the left.&lt;/li&gt;
&lt;li&gt;The structure of an existing query makes keeping the right table convenient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple way to remember the first three joins is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;INNER JOIN:&lt;/strong&gt; Keep the matches.&lt;br&gt;
&lt;strong&gt;LEFT JOIN:&lt;/strong&gt; Keep everything on the left.&lt;br&gt;
&lt;strong&gt;RIGHT JOIN:&lt;/strong&gt; Keep everything on the right.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But there is still one question left:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we want to keep everything from both tables — whether the records match or not?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For that, we use a &lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. FULL OUTER JOIN — Keeping Everything from Both Tables
&lt;/h2&gt;

&lt;p&gt;So far, we have seen that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INNER JOIN&lt;/code&gt; keeps only matching records.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LEFT JOIN&lt;/code&gt; keeps all records from the left table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RIGHT JOIN&lt;/code&gt; keeps all records from the right table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But what happens when we want to keep &lt;strong&gt;all records from both tables&lt;/strong&gt;, regardless of whether they have a match?&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;FULL OUTER JOIN&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Records that match in both tables&lt;/li&gt;
&lt;li&gt;Unmatched records from the left table&lt;/li&gt;
&lt;li&gt;Unmatched records from the right table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where no corresponding record exists, SQL returns &lt;code&gt;NULL&lt;/code&gt; for the missing values.&lt;/p&gt;

&lt;p&gt;A simple way to remember it is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FULL OUTER JOIN = Give me everything from both tables and match what you can.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Our Customers Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;James Mwangi&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;Faith Chebet&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;Mary Atieno&lt;/td&gt;
&lt;td&gt;Kisumu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;104&lt;/td&gt;
&lt;td&gt;Brian Otieno&lt;/td&gt;
&lt;td&gt;Mombasa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Our Sales Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;sale_id&lt;/th&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;amount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S001&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;3,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S002&lt;/td&gt;
&lt;td&gt;103&lt;/td&gt;
&lt;td&gt;2,800,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S003&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;4,100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S004&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;td&gt;2,500,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are two important unmatched records:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="m7n3f8"&lt;br&gt;
Customer 104 → No sale&lt;br&gt;
Sale S004    → Customer 105 is not in customers&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


A `FULL OUTER JOIN` allows us to see both.

### FULL OUTER JOIN Syntax



```sql id="w4j2nv"
SELECT
    c.customer_id AS customer_record_id,
    c.customer_name,
    c.county,
    s.sale_id,
    s.customer_id AS sales_customer_id,
    s.amount
FROM customers AS c
FULL OUTER JOIN sales AS s
    ON c.customer_id = s.customer_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;SQL first matches records where the &lt;code&gt;customer_id&lt;/code&gt; exists in both tables:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="zt28oc"&lt;br&gt;
CUSTOMERS                         SALES&lt;/p&gt;

&lt;p&gt;101 James Mwangi  ────────────── 101 S001 ✓&lt;br&gt;
102 Faith Chebet  ────────────── 102 S003 ✓&lt;br&gt;
103 Mary Atieno   ────────────── 103 S002 ✓&lt;/p&gt;

&lt;p&gt;104 Brian Otieno                 No match&lt;br&gt;
                                  105 S004&lt;br&gt;
                                  No match&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Unlike an `INNER JOIN`, neither unmatched record is discarded.

### Result

| customer_record_id | customer_name | county  | sale_id | sales_customer_id |    amount |
| -----------------: | ------------- | ------- | ------- | ----------------: | --------: |
|                101 | James Mwangi  | Nairobi | S001    |               101 | 3,500,000 |
|                102 | Faith Chebet  | Nakuru  | S003    |               102 | 4,100,000 |
|                103 | Mary Atieno   | Kisumu  | S002    |               103 | 2,800,000 |
|                104 | Brian Otieno  | Mombasa | NULL    |              NULL |      NULL |
|               NULL | NULL          | NULL    | S004    |               105 | 2,500,000 |

The result gives us three different situations.

### 1. Matching Records

For customers `101`, `102`, and `103`, SQL found matching records in both tables.

Their customer and sales information therefore appears together.

### 2. Customer Without a Sale

Brian Otieno (`customer_id = 104`) exists in the `customers` table but has no corresponding sale.

His sales information therefore appears as:



```text id="9k1qxr"
sale_id → NULL
amount  → NULL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Sale Without a Customer
&lt;/h3&gt;

&lt;p&gt;Sale &lt;code&gt;S004&lt;/code&gt; references &lt;code&gt;customer_id = 105&lt;/code&gt;, but that customer cannot be found in the &lt;code&gt;customers&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;Therefore, the customer information appears as &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This makes &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; particularly useful when comparing datasets and looking for discrepancies.&lt;/p&gt;


&lt;h2&gt;
  
  
  Finding Only the Unmatched Records
&lt;/h2&gt;

&lt;p&gt;Sometimes we are not interested in the matches at all.&lt;/p&gt;

&lt;p&gt;Instead, we want to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which records exist in one table but do not have a corresponding record in the other?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can combine &lt;code&gt;FULL OUTER JOIN&lt;/code&gt; with a &lt;code&gt;WHERE&lt;/code&gt; condition:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="h6v4np"&lt;br&gt;
SELECT&lt;br&gt;
    c.customer_id,&lt;br&gt;
    c.customer_name,&lt;br&gt;
    s.sale_id,&lt;br&gt;
    s.customer_id AS sales_customer_id&lt;br&gt;
FROM customers AS c&lt;br&gt;
FULL OUTER JOIN sales AS s&lt;br&gt;
    ON c.customer_id = s.customer_id&lt;br&gt;
WHERE c.customer_id IS NULL&lt;br&gt;
   OR s.customer_id IS NULL;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This would identify:



```text id="7d2u9b"
Brian Otieno → Customer without a sale
S004         → Sale without a matching customer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This type of query can be very useful during &lt;strong&gt;data validation, reconciliation, and data-quality checks&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Comparing the Four Main JOINs
&lt;/h2&gt;

&lt;p&gt;We can now bring together everything we have learned.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JOIN&lt;/th&gt;
&lt;th&gt;Matching Rows&lt;/th&gt;
&lt;th&gt;Unmatched Left Rows&lt;/th&gt;
&lt;th&gt;Unmatched Right Rows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RIGHT JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Another simple way to remember them is:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="21a5hx"&lt;br&gt;
INNER JOIN&lt;br&gt;
→ Keep only matches&lt;/p&gt;

&lt;p&gt;LEFT JOIN&lt;br&gt;
→ Keep matches + everything from the left&lt;/p&gt;

&lt;p&gt;RIGHT JOIN&lt;br&gt;
→ Keep matches + everything from the right&lt;/p&gt;

&lt;p&gt;FULL OUTER JOIN&lt;br&gt;
→ Keep everything from both sides&lt;/p&gt;

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


The JOIN you choose should therefore depend on the **question you are trying to answer**, not simply on which syntax you remember.

For example:

**“Which customers have made purchases?”**

→ `INNER JOIN`

**“Show me all customers, including those who have never purchased.”**

→ `LEFT JOIN`

**“Keep every sales transaction, even if customer information is missing.”**

→ `RIGHT JOIN` — or equivalently restructure the query using a `LEFT JOIN`.

**“Show me all records from both datasets and highlight where information does not match.”**

→ `FULL OUTER JOIN`

Understanding these four JOINs gives you the foundation for combining related tables in SQL.

## Conclusion

SQL JOINs are an essential skill when working with relational databases because real-world data is often distributed across multiple related tables. Instead of storing customers, sales, products, employees, and other information in one large table, relational databases organize this information into separate tables that can be connected when needed.

In this article, we explored four important types of SQL JOINs:

* **INNER JOIN** — returns records that have matching values in both tables.
* **LEFT JOIN** — returns all records from the left table and matching records from the right table.
* **RIGHT JOIN** — returns all records from the right table and matching records from the left table.
* **FULL OUTER JOIN** — returns all records from both tables, whether they have a match or not.

The key to choosing the correct JOIN is to first understand the **question you are trying to answer**.

If you only need customers who have made purchases, an `INNER JOIN` may be appropriate. If you need all customers, including those who have never purchased anything, a `LEFT JOIN` would be more suitable. If you need to compare two datasets and identify both matching and unmatched records, a `FULL OUTER JOIN` can provide a more complete picture.

A simple way to remember the four JOINs is:



```text
INNER JOIN      → Keep the matches

LEFT JOIN       → Keep everything on the left

RIGHT JOIN      → Keep everything on the right

FULL OUTER JOIN → Keep everything from both sides
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, understanding JOIN syntax is only part of the skill. It is equally important to understand &lt;strong&gt;primary keys, foreign keys, table relationships, NULL values, and the level of detail in each table&lt;/strong&gt;. These concepts help ensure that tables are joined correctly and that the results of an analysis are reliable.&lt;/p&gt;

&lt;p&gt;For a data analyst, JOINs are particularly valuable because they allow data from different parts of a business to be brought together. A sales transaction can be connected to a customer, a customer to a location, a sale to a product, or an employee to a department. Once these connections are made, seemingly separate pieces of information can become meaningful business insights.&lt;/p&gt;

&lt;p&gt;The best way to become comfortable with SQL JOINs is through practice. Create a few related tables, experiment with each JOIN, examine the results, and ask yourself why certain records appeared while others did not.&lt;/p&gt;

&lt;p&gt;Ultimately, SQL JOINs are about one simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Connecting related pieces of data to reveal a more complete story.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sql</category>
      <category>beginners</category>
      <category>database</category>
      <category>data</category>
    </item>
    <item>
      <title>INTRODUCTION TO SQL (DML &amp; DDL</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Sun, 20 Sep 2026 15:23:53 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/introduction-to-sql-dml-ddl-4p6b</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/introduction-to-sql-dml-ddl-4p6b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Every day, businesses and applications generate large amounts of data. Think about an online shop that needs to keep track of its customers, products, orders, and payments. This information needs to be stored in an organized way so that it can easily be accessed, updated, and analyzed. One common way of doing this is by using a &lt;strong&gt;relational database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A relational database organizes data into &lt;strong&gt;tables&lt;/strong&gt;, which consist of rows and columns. For example, a company may have a &lt;code&gt;customers&lt;/code&gt; table containing customer information and an &lt;code&gt;orders&lt;/code&gt; table containing details about purchases made by those customers.&lt;/p&gt;

&lt;p&gt;To communicate with these databases, we use &lt;strong&gt;SQL (Structured Query Language)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SQL is a language used to interact with relational databases. It allows us to perform tasks such as creating tables, adding new records, retrieving information, updating existing data, and deleting data that is no longer required.&lt;/p&gt;

&lt;p&gt;For example, imagine we have a table called &lt;code&gt;customers&lt;/code&gt;. If we wanted to see all the customers stored in that table, 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="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple SQL statement asks the database to return all the records from the &lt;code&gt;customers&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;However, not all SQL commands perform the same type of task. Some commands are used to &lt;strong&gt;define and change the structure of a database&lt;/strong&gt;, while others are used to &lt;strong&gt;work with the data stored inside those structures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;SQL commands are therefore commonly grouped into categories according to their purpose. Two important categories that every SQL beginner should understand are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DDL — Data Definition Language&lt;/strong&gt;, which is used to define and manage database structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DML — Data Manipulation Language&lt;/strong&gt;, which is used to add, modify, and remove data stored within those structures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we will explore DDL and DML using practical examples and learn how they work together when building and managing a relational database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Definition Language (DDL)
&lt;/h2&gt;

&lt;p&gt;Before we can store customer details, sales transactions, products, or any other information in a relational database, we first need to create a structure that will hold that data.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;Data Definition Language (DDL)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DDL (Data Definition Language)&lt;/strong&gt; is a category of SQL commands used to &lt;strong&gt;create, define, modify, and remove database objects and structures&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Database objects can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Tables&lt;/li&gt;
&lt;li&gt;Schemas&lt;/li&gt;
&lt;li&gt;Views&lt;/li&gt;
&lt;li&gt;Indexes&lt;/li&gt;
&lt;li&gt;Sequences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a beginner, the easiest place to understand DDL is by working with &lt;strong&gt;tables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Imagine we are developing a database for a car dealership. Before we can add information about the cars available for sale, we need to create a table that defines what information should be stored.&lt;/p&gt;

&lt;p&gt;For example, we might want to store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Car ID&lt;/li&gt;
&lt;li&gt;Make&lt;/li&gt;
&lt;li&gt;Model&lt;/li&gt;
&lt;li&gt;Year of manufacture&lt;/li&gt;
&lt;li&gt;Price&lt;/li&gt;
&lt;li&gt;Status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DDL allows us to create this structure before inserting the actual car records.&lt;/p&gt;

&lt;p&gt;The main DDL commands we will explore are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CREATE&lt;/code&gt; — creates a new database object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALTER&lt;/code&gt; — changes the structure of an existing database object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TRUNCATE&lt;/code&gt; — removes all records from a table while retaining its structure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DROP&lt;/code&gt; — removes a database object completely.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  1. CREATE — Creating Database Objects
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;CREATE&lt;/code&gt; command is used to create new database objects.&lt;/p&gt;

&lt;p&gt;One of its most common uses is creating a table.&lt;/p&gt;

&lt;p&gt;The general syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="8o7rta"&lt;br&gt;
CREATE TABLE table_name (&lt;br&gt;
    column_name data_type constraints&lt;br&gt;
);&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Let's create a table called `cars`:



```sql id="o5ryy6"
CREATE TABLE cars (
    car_id SERIAL PRIMARY KEY,
    make VARCHAR(50) NOT NULL,
    model VARCHAR(50) NOT NULL,
    year_of_manufacture INT,
    price DECIMAL(12,2),
    status VARCHAR(30)
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At this point, we have created the &lt;strong&gt;structure&lt;/strong&gt; of the table, but we have not added any car records.&lt;/p&gt;

&lt;p&gt;We can think of it like creating an empty spreadsheet:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;car_id&lt;/th&gt;
&lt;th&gt;make&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;year_of_manufacture&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The columns exist, but there is no data yet.&lt;/p&gt;
&lt;h4&gt;
  
  
  Understanding the CREATE statement
&lt;/h4&gt;

&lt;p&gt;Let's break down some important parts.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="jj9e0h"&lt;br&gt;
car_id SERIAL PRIMARY KEY&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


`car_id` is the name of the column.

`SERIAL` is a PostgreSQL feature commonly used to generate sequential integer values automatically.

`PRIMARY KEY` means that the column uniquely identifies each record in the table.

Next:



```sql id="o7i5fc"
make VARCHAR(50) NOT NULL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;VARCHAR(50)&lt;/code&gt; allows text values with a maximum length of 50 characters.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NOT NULL&lt;/code&gt; means that this column must contain a value.&lt;/p&gt;

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

&lt;p&gt;```sql id="5c9yte"&lt;br&gt;
price DECIMAL(12,2)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


allows us to store decimal numbers, making it suitable for values such as prices.

Choosing appropriate **data types and constraints** is an important part of database design because they determine what kind of information can be stored in each column.

---

### 2. ALTER — Modifying an Existing Table

Database requirements can change over time.

Suppose we created our `cars` table but later realized that we also need to record the colour of each vehicle.

We do not necessarily need to delete and recreate the entire table.

Instead, we can use `ALTER TABLE`.



```sql id="8nmzoh"
ALTER TABLE cars
ADD COLUMN colour VARCHAR(30);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Our table structure now becomes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;car_id&lt;/th&gt;
&lt;th&gt;make&lt;/th&gt;
&lt;th&gt;model&lt;/th&gt;
&lt;th&gt;year_of_manufacture&lt;/th&gt;
&lt;th&gt;price&lt;/th&gt;
&lt;th&gt;status&lt;/th&gt;
&lt;th&gt;colour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;ALTER TABLE&lt;/code&gt; can be used for several structural changes.&lt;/p&gt;

&lt;p&gt;For example, we can rename a column:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="r62ec9"&lt;br&gt;
ALTER TABLE cars&lt;br&gt;
RENAME COLUMN colour TO car_colour;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


We can also remove a column:



```sql id="81vzi4"
ALTER TABLE cars
DROP COLUMN car_colour;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Therefore, &lt;code&gt;ALTER&lt;/code&gt; is useful when the structure of an existing database object needs to change.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. TRUNCATE — Removing All Records
&lt;/h3&gt;

&lt;p&gt;Suppose our &lt;code&gt;cars&lt;/code&gt; table contains hundreds of records and we want to remove &lt;strong&gt;all the rows&lt;/strong&gt; while keeping the table itself.&lt;/p&gt;

&lt;p&gt;We can use:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="m0xcr7"&lt;br&gt;
TRUNCATE TABLE cars;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


After executing the command, the table still exists:

| car_id | make | model | year_of_manufacture | price | status |
| ------ | ---- | ----- | ------------------- | ----- | ------ |
|        |      |       |                     |       |        |

The **structure remains**, but the records have been removed.

This distinction is important because `TRUNCATE` does not mean the same thing as `DROP`.

---

### 4. DROP — Removing a Database Object

The `DROP` command removes a database object completely.

For example:



```sql id="adwsm2"
DROP TABLE cars;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This removes the &lt;code&gt;cars&lt;/code&gt; table itself.&lt;/p&gt;

&lt;p&gt;After executing the statement, we can no longer query the table because its definition has been removed from the database.&lt;/p&gt;

&lt;p&gt;A simple way to remember the difference is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TRUNCATE&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```text id="tv5mdv"&lt;br&gt;
Table&lt;br&gt;
├── Structure  ✓&lt;br&gt;
└── Records    ✗&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


**DROP**



```text id="ezcn2w"
Table
├── Structure  ✗
└── Records    ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Because commands such as &lt;code&gt;DROP&lt;/code&gt; and &lt;code&gt;TRUNCATE&lt;/code&gt; can remove large amounts of data or entire database objects, they should be used carefully, particularly in production environments.&lt;/p&gt;


&lt;h2&gt;
  
  
  Putting DDL Together
&lt;/h2&gt;

&lt;p&gt;Let's look at the lifecycle of our table.&lt;/p&gt;

&lt;p&gt;First, we &lt;strong&gt;create&lt;/strong&gt; it:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="dl1qv5"&lt;br&gt;
CREATE TABLE cars (&lt;br&gt;
    car_id SERIAL PRIMARY KEY,&lt;br&gt;
    make VARCHAR(50),&lt;br&gt;
    model VARCHAR(50),&lt;br&gt;
    price DECIMAL(12,2)&lt;br&gt;
);&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Later, our requirements change, so we **alter** it:



```sql id="w1m2lg"
ALTER TABLE cars
ADD COLUMN status VARCHAR(30);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If we want to remove all its records but keep the structure, we can use:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="a3ic11"&lt;br&gt;
TRUNCATE TABLE cars;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


And if the table itself is no longer required:



```sql id="n4nbbw"
DROP TABLE cars;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The four commands can therefore be summarized as:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Table Structure&lt;/th&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a new object&lt;/td&gt;
&lt;td&gt;Created&lt;/td&gt;
&lt;td&gt;Empty initially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Changes an existing object's structure&lt;/td&gt;
&lt;td&gt;Modified&lt;/td&gt;
&lt;td&gt;Usually retained&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TRUNCATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes all table records&lt;/td&gt;
&lt;td&gt;Retained&lt;/td&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DROP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes the database object&lt;/td&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;td&gt;Removed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DDL therefore provides the &lt;strong&gt;structure or foundation of our database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once that structure exists, we can begin adding and working with actual records. This brings us to the next category of SQL commands: &lt;strong&gt;Data Manipulation Language (DML)&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data Manipulation Language (DML)
&lt;/h2&gt;

&lt;p&gt;Once a database and its tables have been created, the next step is to work with the data stored inside those tables. This is where &lt;strong&gt;Data Manipulation Language (DML)&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DML (Data Manipulation Language)&lt;/strong&gt; refers to SQL commands used to &lt;strong&gt;add, modify, and remove data stored in database tables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of a database table like a spreadsheet. The table structure defines the columns available, while DML allows us to work with the individual records stored in the rows.&lt;/p&gt;

&lt;p&gt;For example, suppose we have the following &lt;code&gt;customers&lt;/code&gt; table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;th&gt;email&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;John Kamau&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:john@example.com"&gt;john@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mary Wanjiku&lt;/td&gt;
&lt;td&gt;Kiambu&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:mary@example.com"&gt;mary@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Brian Kiptoo&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:brian@example.com"&gt;brian@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The main DML commands we will explore are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INSERT&lt;/code&gt; — adds new records to a table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UPDATE&lt;/code&gt; — modifies existing records.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE&lt;/code&gt; — removes records from a table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will also look at &lt;code&gt;SELECT&lt;/code&gt;, which retrieves data from a database. &lt;code&gt;SELECT&lt;/code&gt; is sometimes taught alongside DML, although some SQL classifications place it in a separate category called &lt;strong&gt;DQL (Data Query Language)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. INSERT — Adding Data
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;INSERT&lt;/code&gt; statement is used when we want to add new records to a table.&lt;/p&gt;

&lt;p&gt;The basic syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="5um3sm"&lt;br&gt;
INSERT INTO table_name (column1, column2, column3)&lt;br&gt;
VALUES (value1, value2, value3);&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Suppose a new customer named Alice joins our business. We could add her to the `customers` table using:



```sql id="98hspj"
INSERT INTO customers (customer_name, county, email)
VALUES ('Alice Njeri', 'Nairobi', 'alice@example.com');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Breaking this statement down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INSERT INTO&lt;/code&gt; tells SQL that we want to add a new record.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customers&lt;/code&gt; identifies the table receiving the data.&lt;/li&gt;
&lt;li&gt;The columns in parentheses specify where the values should be stored.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VALUES&lt;/code&gt; contains the actual information being inserted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After executing the statement, our table might contain:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;customer_id&lt;/th&gt;
&lt;th&gt;customer_name&lt;/th&gt;
&lt;th&gt;county&lt;/th&gt;
&lt;th&gt;email&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;John Kamau&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:john@example.com"&gt;john@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Mary Wanjiku&lt;/td&gt;
&lt;td&gt;Kiambu&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:mary@example.com"&gt;mary@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Brian Kiptoo&lt;/td&gt;
&lt;td&gt;Nakuru&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:brian@example.com"&gt;brian@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Alice Njeri&lt;/td&gt;
&lt;td&gt;Nairobi&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:alice@example.com"&gt;alice@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can also insert several records in a single statement:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="i4n3rx"&lt;br&gt;
INSERT INTO customers (customer_name, county, email)&lt;br&gt;
VALUES&lt;br&gt;
    ('Peter Otieno', 'Kisumu', '&lt;a href="mailto:peter@example.com"&gt;peter@example.com&lt;/a&gt;'),&lt;br&gt;
    ('Faith Chebet', 'Kericho', '&lt;a href="mailto:faith@example.com"&gt;faith@example.com&lt;/a&gt;'),&lt;br&gt;
    ('David Mwangi', 'Nyeri', '&lt;a href="mailto:david@example.com"&gt;david@example.com&lt;/a&gt;');&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This is known as inserting **multiple rows**.

---

### 2. UPDATE — Modifying Existing Data

Data stored in a database does not always remain the same. A customer might change their email address, move to another county, or update other personal details.

The `UPDATE` statement allows us to modify existing records.

Its basic syntax is:



```sql id="17l8gf"
UPDATE table_name
SET column_name = new_value
WHERE condition;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Suppose Alice moves from Nairobi to Nakuru. We could update her record using:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="e21cbr"&lt;br&gt;
UPDATE customers&lt;br&gt;
SET county = 'Nakuru'&lt;br&gt;
WHERE customer_id = 4;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The statement can be understood as:



```text id="8pn4ow"
UPDATE customers
        ↓
Which table?

SET county = 'Nakuru'
        ↓
What should change?

WHERE customer_id = 4
        ↓
Which record should change?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;WHERE&lt;/code&gt; clause is extremely important because it determines which rows are affected.&lt;/p&gt;

&lt;p&gt;Consider the following statement:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="r1v61y"&lt;br&gt;
UPDATE customers&lt;br&gt;
SET county = 'Nakuru';&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Because there is no `WHERE` condition, SQL will attempt to change the county to `Nakuru` for **every row in the table**.

Therefore, before executing an `UPDATE`, always check whether your `WHERE` condition identifies the intended records.

You can even check the affected records first:



```sql id="4ppfl4"
SELECT *
FROM customers
WHERE customer_id = 4;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once you are satisfied that the correct record has been identified, you can execute the &lt;code&gt;UPDATE&lt;/code&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. DELETE — Removing Data
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;DELETE&lt;/code&gt; statement removes existing records from a table.&lt;/p&gt;

&lt;p&gt;Its basic syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="rfdjhu"&lt;br&gt;
DELETE FROM table_name&lt;br&gt;
WHERE condition;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


For example, suppose we want to remove the customer whose ID is `4`:



```sql id="97gexx"
DELETE FROM customers
WHERE customer_id = 4;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;WHERE&lt;/code&gt; condition tells the database exactly which record should be removed.&lt;/p&gt;

&lt;p&gt;As with &lt;code&gt;UPDATE&lt;/code&gt;, you must be careful when using &lt;code&gt;DELETE&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;```sql id="b1f44m"&lt;br&gt;
DELETE FROM customers;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


Without a `WHERE` clause, this statement targets **all rows in the table**.

The table itself still exists, but its records will be removed.

This is different from:



```sql id="c68daw"
DROP TABLE customers;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;DROP TABLE&lt;/code&gt; removes the table itself, including its structure, whereas &lt;code&gt;DELETE&lt;/code&gt; removes records from an existing table.&lt;/p&gt;


&lt;h3&gt;
  
  
  4. SELECT — Retrieving Data
&lt;/h3&gt;

&lt;p&gt;Although &lt;code&gt;SELECT&lt;/code&gt; does not change the data stored in a table, it is one of the SQL commands you will use most frequently.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt; is used to retrieve information from a database.&lt;/p&gt;

&lt;p&gt;To retrieve every column from the &lt;code&gt;customers&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="syf0pz"&lt;br&gt;
SELECT *&lt;br&gt;
FROM customers;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


The `*` means **all columns**.

If we only need customer names and counties, we can specify those columns:



```sql id="xy7fyq"
SELECT customer_name, county
FROM customers;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can also use &lt;code&gt;WHERE&lt;/code&gt; to retrieve only records matching a particular condition:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="rd3jgg"&lt;br&gt;
SELECT customer_name, county&lt;br&gt;
FROM customers&lt;br&gt;
WHERE county = 'Nairobi';&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


This asks the database to return customers whose county is Nairobi.

`SELECT` becomes much more powerful when combined with SQL features such as:

* `WHERE` for filtering
* `ORDER BY` for sorting
* `GROUP BY` for grouping
* Aggregate functions such as `SUM()`, `COUNT()`, `AVG()`, `MIN()`, and `MAX()`
* `JOIN` for retrieving related information from multiple tables

These concepts can be explored separately as you progress beyond basic DML.

---

## Bringing the DML Commands Together

We can think about the commands in terms of the lifecycle of a record.

A record is first **created**:



```sql id="j18qnd"
INSERT INTO customers (customer_name, county, email)
VALUES ('Alice Njeri', 'Nairobi', 'alice@example.com');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It can later be &lt;strong&gt;read&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="os2xvw"&lt;br&gt;
SELECT *&lt;br&gt;
FROM customers&lt;br&gt;
WHERE customer_name = 'Alice Njeri';&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


If something changes, it can be **updated**:



```sql id="c39tn2"
UPDATE customers
SET county = 'Nakuru'
WHERE customer_name = 'Alice Njeri';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally, if the record is no longer required, it can be &lt;strong&gt;deleted&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```sql id="vz7ij9"&lt;br&gt;
DELETE FROM customers&lt;br&gt;
WHERE customer_name = 'Alice Njeri';&lt;/p&gt;

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


These four operations are often described using the acronym **CRUD**:

| CRUD Operation | SQL Command | Purpose                |
| -------------- | ----------- | ---------------------- |
| Create         | `INSERT`    | Add new data           |
| Read           | `SELECT`    | Retrieve existing data |
| Update         | `UPDATE`    | Modify existing data   |
| Delete         | `DELETE`    | Remove existing data   |

CRUD operations form the foundation of how many applications interact with relational databases.

For example, when a customer creates an account on an online platform, the application may use an `INSERT` operation behind the scenes. When the customer views their profile, the application retrieves their information. Updating their address modifies the existing record, while deleting an account may involve removing or deactivating the associated data.

Understanding DML therefore goes beyond learning SQL syntax. It helps us understand how applications create, retrieve, modify, and manage the data that organizations use every day.

##Conclusion

Understanding DDL (Data Definition Language) and DML (Data Manipulation Language) is an important foundation for anyone learning SQL and relational databases.

DDL focuses on the structure of the database. Commands such as CREATE, ALTER, TRUNCATE, and DROP allow us to create and manage database objects such as tables.

DML, on the other hand, focuses on the data stored within those structures. Commands such as INSERT, UPDATE, and DELETE allow us to add, modify, and remove records, while SELECT is commonly used to retrieve and explore the stored data.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>database</category>
      <category>beginners</category>
      <category>learning</category>
      <category>writing</category>
    </item>
    <item>
      <title>DIVING DEEPER INTO POWER BI</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Wed, 16 Sep 2026 23:58:27 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/diving-deeper-into-power-bi-ih3</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/diving-deeper-into-power-bi-ih3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you’ve ever packed a backpack for a long trip, you know that just throwing everything into one giant pocket makes a total mess. When you urgently need your passport, you have to dig past tangled headphones, loose change, and extra clothes. Power BI is no different. If you dump all your data into one big, messy spreadsheet, your reports will break and run painfully slow. Mastering Power BI is simply the art of packing your data into the right compartments so that finding answers is instant and effortless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Power BI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Power BI is a &lt;em&gt;Microsoft's business analytics and data visualization platform&lt;/em&gt;. It lets you &lt;em&gt;connect to various data sources&lt;/em&gt;, &lt;em&gt;clean and transform raw information, and build interactive dashboards&lt;/em&gt; that reveal trends and track Key Performance Indicators (KPIs) to drive better business decisions.&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%2Fkw6qjux6jfvzk3ei9kh0.JPG" 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%2Fkw6qjux6jfvzk3ei9kh0.JPG" alt="bi intro" width="799" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the definition, we can say that the Power BI has four main functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Connect&lt;/em&gt; -Power BI enables one to intergrate data from various sources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Transform&lt;/em&gt; - This utilises tools like Power Query and is useful in cleaning, merging and working on raw data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Model&lt;/em&gt;- Power BI enables creating relationship within a dataset to enable easy analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Visualize&lt;/em&gt; - Finaly, you can use the models created to visualize data in a dashboard and draw conclusions that can be used to make business decisions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Power BI provides a complete platform for collecting, transforming, modelling, analyzing, and visualizing data. To fully utilize its capabilities, it is important to understand the key concepts that form the foundation of Power BI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Areas to be covered&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The foundation : Data Ingestion and Transformation (Power Query)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fact vs. Dimension Tables: Organizing Your Pantry&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Blueprint: Data Modelling and Schema Design&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Glue : Joins vs Relationships&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Engine : The DAX Calculation layer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Presentation : Visualizations and Reporting&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this section, we will dive deeper into what Power BI is and explore the fundamental concepts that will help us build efficient data models and create impactful analytical reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  The foundation : Data Ingestion and Transformation (Power Query)
&lt;/h2&gt;

&lt;p&gt;Before you can cook a magnificent, five-star meal, you have to spend some time in the kitchen prepping your ingredients. You would never throw an unpeeled onion, unwashed vegetables, and raw chicken straight into a pot together. If you did, the final dish would be a complete disaster. You have to wash, chop, peel, and sort your ingredients first.&lt;/p&gt;

&lt;p&gt;In Power BI, this digital kitchen is called Power Query, and it is where you perform ETL (Extract, Transform, Load).&lt;/p&gt;

&lt;p&gt;When you import raw data into Power BI from Excel spreadsheets or databases, it arrives messy. It often contains typos, missing information, blank rows, and poorly formatted dates. Power Query acts as your data preparation room. Without writing any complicated code, you can click simple buttons to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chop away excess data&lt;/strong&gt;: Remove columns and rows you don’t need.&lt;br&gt;
&lt;strong&gt;Wash the data&lt;/strong&gt;: Fix typos, replace errors, and filter out blank spaces.&lt;br&gt;
&lt;strong&gt;Organize the pantry&lt;/strong&gt;: Ensure numbers are treated as numbers and dates look like actual calendar dates.&lt;/p&gt;

&lt;p&gt;By taking the time to cleanly prepare your data "ingredients" in Power Query first, you ensure that the rest of your Power BI building experience is fast, smooth, and error-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fact vs. Dimension Tables: Organizing Your Pantry
&lt;/h2&gt;

&lt;p&gt;Now that your data ingredients are clean and prepped, it’s time to organize them. In Power BI, you don't just dump all your data into one massive, endless spreadsheet. Instead, you separate your data into two distinct types of tables: &lt;strong&gt;Fact tables&lt;/strong&gt; and &lt;strong&gt;Dimension tables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To a complete beginner, these names sound intimidating, but they are just like dividing your kitchen into a Sales Ledger and a Pantry.&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%2Fqc9cwahevylhk636h36o.JPG" 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%2Fqc9cwahevylhk636h36o.JPG" alt="fact table and dimension table" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Fact TAbles (The "Sales Ledger)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Think of the Fact table as a fast-moving restaurant's sales ledger. Every single time a customer buys a meal, a new line is written down. It answers the questions “&lt;em&gt;How much?&lt;/em&gt;”, “&lt;em&gt;How many?&lt;/em&gt;”, and “&lt;em&gt;When?&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What lives here:&lt;/strong&gt; Numbers, quantities, and prices (metrics). It also holds unique ID numbers to link back to your pantry.&lt;br&gt;
&lt;strong&gt;The Shape:&lt;/strong&gt; It is a narrow but incredibly deep table. It might only have a few columns, but it can easily hold millions of rows recording every single historical transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Dimension Tables (The "Pantry Items")&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Think of Dimension tables as the labeled jars sitting neatly in your pantry. They don't record actions; instead, they hold the descriptive details about the things surrounding the actions. They answer the questions “&lt;em&gt;Who?&lt;/em&gt;”, “&lt;em&gt;What?&lt;/em&gt;”, and “&lt;em&gt;Where?&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What lives here:&lt;/strong&gt; Text labels like product names, food categories, customer addresses, store locations, and calendar dates.&lt;br&gt;
&lt;strong&gt;The Shape:&lt;/strong&gt; It is a _wide but shallow _table. It has lots of descriptive columns (like item color, size, and category) but far fewer rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Separate Them?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you wrote the full name, address, and phone number of a customer on every single line of your sales ledger, your book would become massive, bloated, and impossible to read. By keeping the details in the pantry (Dimension tables) and the math in the ledger (Fact table), Power BI can find answers in milliseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blueprint: Data Modelling and Schema Design
&lt;/h2&gt;

&lt;p&gt;Now that you have separated your clean ingredients into a transactional ledger (Fact table) and structured pantry jars (Dimension tables), you need a plan for how they will sit together. In the tech world, this layout plan is called &lt;strong&gt;Schema Design&lt;/strong&gt;, and the act of connecting them is called &lt;strong&gt;Data Modeling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of this as designing the physical blueprint and traffic flow of your kitchen so the chef can grab ingredients instantly without tripping over anything.&lt;/p&gt;

&lt;p&gt;In Power BI, there is one supreme blueprint that beats all others: &lt;strong&gt;The Star Schema.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a Star Schema?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Star Schema is simply a visual pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Center:&lt;/strong&gt; You place your heavy Fact table (the sales ledger) right in the middle of your digital workspace.&lt;br&gt;
&lt;strong&gt;The Points:&lt;/strong&gt; You place your Dimension tables (the pantry jars for Products, Customers, and Dates) in a ring surrounding that central table.&lt;/p&gt;

&lt;p&gt;When you draw connection lines from each surrounding pantry jar to the central ledger, the layout naturally begins to look like a &lt;strong&gt;star&lt;/strong&gt; (or a solar system with planets orbiting a central sun).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is this blueprint the "Golden Rule"?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Power BI is custom-engineered from the ground up to read data that is laid out like a star. When a user clicks a button on your dashboard to filter by "October" or "Espresso Drinks," Power BI starts at the outer edge of the star (the Dimension table) and flows smoothly down a single highway directly into the center (the Fact table) to calculate the totals.&lt;/p&gt;

&lt;p&gt;If you don't follow this blueprint—and instead create a chaotic spiderweb of tangled tables—your report will become painfully slow, your charts will show incorrect numbers, and your calculations will break.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Glue: Joins vs. Relationships
&lt;/h2&gt;

&lt;p&gt;Now that you have your Star Schema blueprint mapped out on paper, you need to actually connect your tables so they can talk to each other. In Power BI, this is where beginners often get tripped up because they hear two terms used interchangeably: &lt;strong&gt;Joins **and **Relationships&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While both act as the "glue" that connects your data using a matching ID column (like a ProductID), they happen at entirely different times and serve completely different purposes.&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%2Foldwt8t5dzfzywb0921t.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%2Foldwt8t5dzfzywb0921t.png" alt="JOINS" width="710" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Joins (The Upstream Glue)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A Join happens early in the kitchen prep room &lt;strong&gt;(Power Query)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Imagine taking two separate pieces of paper—like a list of subcategories and a list of categories—and physically smashing them together with glue to create one single, wider spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal:&lt;/strong&gt; You use Joins to clean up messy data layouts (like a snowflake schema) and flatten them down so they fit perfectly into your neat outer pantry jars (Dimension tables).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Relationships (The Downstream Glue)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A Relationship happens later on the main stage (&lt;strong&gt;Model View canvas&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Instead of physically smashing tables together, a relationship keeps the tables completely separate but cuts a "peek-a-boo window" or draws a bridge between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Goal:&lt;/strong&gt; You draw virtual connection lines directly from your outer pantry jars to your central ledger. This tells Power BI: "When a user filters a chart by a specific product name over here, look across the bridge and update the total sales numbers over there."&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Difference Matters to You
&lt;/h3&gt;

&lt;p&gt;If you use Joins for everything, your data model becomes one giant, bloated spreadsheet that slows Power BI to a crawl. If you use Relationships correctly to connect your Star Schema, your file size stays incredibly small, and your reports run lightning-fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Engine: The DAX Calculation Layer.
&lt;/h2&gt;

&lt;p&gt;Once your tables are neatly prepped, organized into a star layout, and connected by relationships, your data is officially ready to work for you. But how do you actually calculate your business metrics, like your total profit margins, year-over-year growth, or active customer counts?&lt;/p&gt;

&lt;p&gt;You use *&lt;em&gt;DAX *&lt;/em&gt;(Data Analysis Expressions).&lt;/p&gt;

&lt;p&gt;Think of DAX as the high-powered engine under the hood of Power BI. While it looks a bit like Excel formulas, it behaves completely differently. For a beginner, the absolute most important step to mastering DAX is understanding the difference between a &lt;strong&gt;Calculated Column&lt;/strong&gt; and a &lt;strong&gt;Measure&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calculated Columns (The Hard Drive Glues)
&lt;/h3&gt;

&lt;p&gt;A Calculated Column calculates a new value for every single row in your table, right when the data is loaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; Imagine writing a new number by hand onto every single receipt line in your ledger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Cost:&lt;/strong&gt; Because these numbers are physically written into your data, they take up permanent storage space. If you have millions of rows, your file size will balloon quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Use them sparingly, usually only when you need to create a new text label for a slicer or filter (like grouping ages into "Under 30" and "30+").&lt;/p&gt;

&lt;h3&gt;
  
  
  Measures (The On-Demand Calculator)
&lt;/h3&gt;

&lt;p&gt;A Measure does not calculate anything ahead of time and takes up zero physical storage space. It sits completely invisible in your model until you drop it onto a chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Analogy:&lt;/strong&gt; Think of a measure like a programmable button on a smart calculator. It waits patiently until you click it, instantly looks at whatever filters are currently active on your dashboard screen, and calculates the answer on the fly in milliseconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Benefit:&lt;/strong&gt; It keeps your Power BI files incredibly small and handles dynamic filtering seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt; Use measures for 99% of your math—sums, averages, percentages, and performance metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Golden Concept: Filter Context
&lt;/h3&gt;

&lt;p&gt;As a beginner, you will hear the phrase "Filter Context" a lot. Don't let it scare you. It simply means that &lt;strong&gt;DAX measures adapt to their environment&lt;/strong&gt;. If you drop a "Total Sales" measure into a bar chart showing different countries, that single measure instantly figures out how to calculate Sales for Canada in the Canada bar, and Sales for Kenya in the Kenya bar. It only calculates what the user is currently looking at.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Presentation: Visualizations &amp;amp; Reporting
&lt;/h2&gt;

&lt;p&gt;You have prepped your ingredients in Power Query, organized your kitchen into a Star Schema, connected the tables with relationships, and built your DAX calculation engine. Now comes the fun part: serving the meal to your guests. In Power BI, this is the &lt;strong&gt;Visualization and Reporting layer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is where the magic happens for the person reading your report, but as a creator, you must understand how your back-end structure controls your front-end visuals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visuals are Just Windows into Your Data
&lt;/h3&gt;

&lt;p&gt;Every chart, graph, map, and card you drop onto your canvas is just a visual window running your DAX calculations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rule for Layouts:&lt;/strong&gt; When building a visual, you will almost always pull your text categories and labels (like Product Name, Region, or Month) from your &lt;strong&gt;Dimension tables&lt;/strong&gt; to use as your chart axes, rows, or slicers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rule for Numbers:&lt;/strong&gt; You will always pull your dynamic &lt;strong&gt;Measures&lt;/strong&gt; (like Total Revenue or Profit Margin) into the values field of the chart.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Interaction Works: Filter Propagation
&lt;/h3&gt;

&lt;p&gt;The coolest feature of Power BI for a beginner is interactivity—click on a slice of a pie chart, and the rest of the page instantly updates. This is called &lt;strong&gt;Filter Propagation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because you drew those neat "relationship bridges" earlier in your Star Schema, clicking a visual sends a silent command across the bridge. It instantly filters down your tables behind the scenes, and your DAX measures recalculate the new numbers in the blink of an eye.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep It Simple for Your User
&lt;/h3&gt;

&lt;p&gt;When designing the actual report page, remember that your audience didn't see all the hard work you did behind the scenes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don't clutter the page with dozens of complex charts just because you can.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stick to 3 or 4 key visuals per page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure your most critical number (like total profit) stands out in a large, easy-to-read "Card" visual right at the top.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: Your Data Masterpiece Awaits
&lt;/h2&gt;

&lt;p&gt;Building a Power BI report doesn't have to feel like a mystery. By understanding these core concepts, you have shifted from someone who just clicks random buttons to a true data architect.&lt;/p&gt;

&lt;p&gt;Remember the journey your data takes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Power Query cleans and preps your raw ingredients.&lt;/li&gt;
&lt;li&gt;The Star Schema organizes them into a central Fact table surrounded by detailed Dimension tables.&lt;/li&gt;
&lt;li&gt;Relationships build the bridges that let your tables talk to each other.&lt;/li&gt;
&lt;li&gt;DAX Measures act as the smart, on-demand calculators.&lt;/li&gt;
&lt;li&gt;Visualizations bring it all to life for your audience.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Now, It’s Your Turn!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The absolute best way to master Power BI is to get your hands dirty. Open up Power BI Desktop, pull in a simple spreadsheet, and start sorting your ingredients. Don't worry about making it perfect on your first try—every expert dashboard designer started exactly where you are sitting right now. Trust your blueprint, lean into the process, and go build your very first data masterpiece!&lt;/p&gt;

</description>
      <category>data</category>
      <category>learning</category>
      <category>software</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Connecting Power BI to SQL databases : Local PostgreSQL and Aiven cloud</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Tue, 15 Sep 2026 16:54:13 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/connecting-power-bi-to-sql-databases-local-postgresql-and-aiven-cloud-25ll</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/connecting-power-bi-to-sql-databases-local-postgresql-and-aiven-cloud-25ll</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Power BI supports connectivity to a wide range of data sources. This includes &lt;em&gt;files, relational databases, cloud platforms, web services and online applications&lt;/em&gt;. In this article, we will focus specifically on connecting Power BI to PostgreSQL database. This can be done either locally or using the cloud.&lt;br&gt;
A &lt;strong&gt;Database&lt;/strong&gt; is is a highly organized, centralized digital system designed to store, manage, secure, and instantly retrieve massive amounts of structured data.&lt;br&gt;
A database is managed by a software engine as known as the &lt;strong&gt;database management system (DBMS)&lt;/strong&gt;, like SQL Server or PostgreSQL. When Power BI asks for data, this engine does the heavy lifting: filtering, aggregating, and sorting millions of rows in milliseconds before sending just the necessary results back to Power BI.&lt;br&gt;
We will explore two scenarios: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting Power BI to a postgreSQL database hosted locally.&lt;/li&gt;
&lt;li&gt;Connecting Power BI to a cloud-hosted postgreSQL database using Aiven with SSL configuraion &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before proceeding, it will be important to compare the two scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;Attribute&lt;/th&gt;
    &lt;th&gt;Local PostgreSQL&lt;/th&gt;
    &lt;th&gt;Cloud-Hosted PostgreSQL&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Primary Location&lt;/td&gt;
    &lt;td&gt;Host machine (localhost or local IP)&lt;/td&gt;
    &lt;td&gt;Public cloud server instance&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt; 
  &lt;td&gt;Security Setup&lt;/td&gt;
  &lt;td&gt;Minimal setup; generally unencrypted over local networks&lt;/td&gt;
  &lt;td&gt;Strict Aiven Console CA certificate download and SSL configuration (sslmode=require)&lt;/td&gt;
  &lt;/tr&gt;
&lt;tr&gt; 
  &lt;td&gt;Accessibility&lt;/td&gt;
  &lt;td&gt;Limited to the local device or a restricted local network&lt;/td&gt;
  &lt;td&gt;Accessible anywhere over the internet&lt;/td&gt;
  &lt;/tr&gt;
&lt;tr&gt; 
  &lt;td&gt;Maintenance&lt;/td&gt;
  &lt;td&gt;User-managed (manual updates, scaling, backups)&lt;/td&gt;
  &lt;td&gt;Fully managed by Aiven (automated patches, easy hardware scaling)&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Connecting Power BI to a local SQL database
&lt;/h2&gt;

&lt;p&gt;A local database is a database server installed and run entirely on your own local computer rather than a remote cloud or external server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Process 1: Connecting  a new localhost database in DBeaver.
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;br&gt;
To get started with the process, we need to download all the tools that we need for the process. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The postgreSQL from our browser. Clink this link to download from the official site, &lt;a href="https://www.postgresql.org/download/" rel="noopener noreferrer"&gt;PostgreSQL download link&lt;/a&gt;.
We then install it to our computer.&lt;/li&gt;
&lt;li&gt;DBeaver which is a free, open-source database management tool recommended for personal projects. Used to manage and explore SQL databases like MySQL, MariaDB, PostgreSQL, SQLite, Apache Family, and more. We will download and install it in our local machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;&lt;br&gt;
We then open the DBeaver and create a new local database connection with PostgreSQL Database.&lt;br&gt;
When you open the debeaver, a the Home screen will open as shown  below will open:&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%2Flixvupg5huh2vvf8jmtn.JPG" 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%2Flixvupg5huh2vvf8jmtn.JPG" alt="DBeaver home layout" width="799" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create a new database connection, we will press the CTRL + SHIFT + N  open or alternatively we can click the new connection wizard button &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%2Fyl12e4rzx3r7vzmqtfus.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%2Fyl12e4rzx3r7vzmqtfus.png" alt="new connection button" width="20" height="20"&gt;&lt;/a&gt; in the toolbar. This will open a new window as shown below.&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%2F70t90s9kucdzu2w241iv.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%2F70t90s9kucdzu2w241iv.png" alt="create a databse window" width="800" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;br&gt;
We will then click on the postgreSQL icon and it will take us to a new window to configure the connection. Since we are using localhost, we will not be required to change the configurations. We only need to input our password which we had set while installing our postgreSQL database.&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%2F17wb4rksdbe7o8p0dkkh.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%2F17wb4rksdbe7o8p0dkkh.png" alt="connection configuration" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;&lt;br&gt;
After inputing our password, we will be required to test our connection by pressing the test connection button in the bottom left conner of the window pane. If the connection is sucessful, we will the press the finish button in the bottom right conner to complete the process.&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%2Fnh416vpkirrq6nuf8ad8.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%2Fnh416vpkirrq6nuf8ad8.png" alt="Test connection and finish" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;&lt;br&gt;
In the home screen of DBeaver, a section will appear that will show our new connection as shown below.&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%2F8817f326cmg4mwyat43w.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%2F8817f326cmg4mwyat43w.png" alt="Connection established" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Process 2: Importing data to our database
&lt;/h3&gt;

&lt;p&gt;After confirming that we already have our database connection working in DBeaver, we will need to import data to our database.&lt;br&gt;
This is an important step that we will need in the later stages.&lt;/p&gt;

&lt;p&gt;To import data to DBeaver, on the left side expand on databases &amp;gt; defaultdb &amp;gt; schemas &amp;gt; either create a new schema or use the public schema.&lt;br&gt;
We will first create a databse schema where now we will be able to put our data.&lt;br&gt;
To create a schema we will use the sql command: &lt;br&gt;
&lt;strong&gt;create schema schemaName;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy6umtlqz0rvx1sei7jsv.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%2Fy6umtlqz0rvx1sei7jsv.png" alt="Creating Schema" width="800" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside our schema, we will import our data from the browser.&lt;br&gt;
&lt;em&gt;Right click on your preffered schema and click import data&lt;/em&gt;.A window will po up as shown below.&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%2Fxgt2jmqfj45bwh7bvone.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%2Fxgt2jmqfj45bwh7bvone.png" alt="dbeaver data import" width="799" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to -&amp;gt; Input file(s), Browse and select your data&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%2Fiw4x3pr9p2ucn7vlvlxf.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%2Fiw4x3pr9p2ucn7vlvlxf.png" alt="data importation" width="800" height="824"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After selecting the file to be imported, you will click next until the end where you you will click proceed. This will complete the process of importation. Congratulations, you will now have completed the process of data importation. Your data will appear as shown below;&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%2F74z80f87wv84mipbb90v.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%2F74z80f87wv84mipbb90v.png" alt="data importation complete" width="799" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Process 3: Getting data in power BI
&lt;/h3&gt;

&lt;p&gt;We will now open our Power BI desktop app, we will click on the blank remote to work with it.&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%2F1812hqy1waldejlxg14o.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%2F1812hqy1waldejlxg14o.png" alt="bi home" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expand the view in the Get data section as shown below.&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%2F9slsfgjgjhqbogbk6avg.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%2F9slsfgjgjhqbogbk6avg.png" alt="get data source" width="751" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;more...&lt;/strong&gt; to expand our options to open a dialog box.&lt;br&gt;
In the dialog box, search for &lt;strong&gt;PostgreSQL&lt;/strong&gt; database under the Database category and click &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3gpmytofi0zv7sd54bag.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%2F3gpmytofi0zv7sd54bag.png" alt="choosing get data  source" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking connect, a dialog box will appear that will require our server and database name. We get this information from the time we connected our PostgreSQL database in DBeaver.&lt;br&gt;
Server: &lt;strong&gt;localhost:5432  / 127.0.0.1 : 5432&lt;/strong&gt; . &lt;br&gt;
(&lt;em&gt;Note: 5432 is our port number.&lt;/em&gt;)&lt;br&gt;
Database: &lt;strong&gt;postgres&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After imputing the values, we proceed by clicking the &lt;strong&gt;ok buttton&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fepw9f1j02mbl1uncw9s2.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%2Fepw9f1j02mbl1uncw9s2.png" alt="db config" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new dialog box will appear that requires our User name and password.&lt;br&gt;
User name: &lt;strong&gt;postgres&lt;/strong&gt;&lt;br&gt;
Password: **** (requires the password you set while installing PostgreSql.)&lt;br&gt;
Proceed by clicking the button &lt;strong&gt;connect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9jm0uid3t0rc4wxopkc.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%2Fo9jm0uid3t0rc4wxopkc.png" alt="configure bi" width="796" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This wil take us to a new window where we can access all the tables we had already imported in DBeaver. Choose the tables you want to work on. if you need to clean you can using the &lt;strong&gt;transform&lt;/strong&gt; button which will direct you into power query where you will be able to clean your data. Otherwise, you can &lt;strong&gt;load&lt;/strong&gt; the tables directly so you can start working on them.&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%2F0kjui4y54iqs6h7gfvg0.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%2F0kjui4y54iqs6h7gfvg0.png" alt="power bi localhost connection done" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations you have successfully connected Power BI to your localhost postgreSQL database!&lt;/p&gt;

&lt;p&gt;Let's now look into how you can connect to a cloud based &lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Power BI to a cloud-hosted postgreSQL database using Aiven with SSL configuraion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;STEP 1: Setting up our aiven account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Aiven.&lt;/strong&gt;&lt;br&gt;
Aiven is a fully managed cloud data platform that provides open-source technologies, such as PostgreSQL and Apache Kafka, as a service. It automates complex infrastructure tasks like setup, security patching, backups, and scaling so developers can focus strictly on building applications. The platform allows you to deploy these managed databases seamlessly across major cloud environments, including AWS, Google Cloud, and Microsoft Azure. By offering an accessible free tier, it makes it easy for developers to launch and manage a PostgreSQL database globally with minimal effort.&lt;/p&gt;

&lt;p&gt;To setup our aiven account,we will need to go to our browser an type &lt;a href="https://aiven.io/" rel="noopener noreferrer"&gt;aiven.io&lt;/a&gt;&lt;br&gt;
 When aiven.io opens, click on &lt;strong&gt;get building&lt;/strong&gt; to proceed to set up our aiven account.&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%2Fbmpmg0i32oamudwrv5nb.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%2Fbmpmg0i32oamudwrv5nb.png" alt="aiven home" width="799" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will need to sign up into your account and then you login  to your account. You will be taken to a window that will require you to create a service.&lt;br&gt;
Choose &lt;strong&gt;PostgreSQL as your service&lt;/strong&gt; and click the &lt;strong&gt;create service&lt;/strong&gt; buttom in the bottom right of the window.&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%2Fnowh938qttpvty9kwzh7.JPG" 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%2Fnowh938qttpvty9kwzh7.JPG" alt="aiven console" width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will be taken into the projects tabs where we can see our service details. This details are important to help us later when we open Power BI.&lt;br&gt;
&lt;strong&gt;Note&lt;/strong&gt;: Make sure the services builds until you see it's status changes to &lt;strong&gt;running&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1vkxo5gpf866n9xqdi5.JPG" 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%2Fq1vkxo5gpf866n9xqdi5.JPG" alt="aiven service build" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;STEP 2: Connecting our database in DBeaver.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To establish a cloud database connection in dbeaver, we will start by opening dbeaver console.&lt;/p&gt;

&lt;p&gt;Using the &lt;strong&gt;new connection button&lt;/strong&gt; or the shortcut &lt;strong&gt;SHIFT + CTRL + N&lt;/strong&gt; to establish a open a dialog window.&lt;br&gt;
In the dialog box, choose &lt;strong&gt;postgreSQL&lt;/strong&gt; as our database and click &lt;strong&gt;next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Figrwfewys61epeemmstj.JPG" 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%2Figrwfewys61epeemmstj.JPG" alt="dabase choice" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we configure the database based on the details we generated from the service we created in aiven.io&lt;/p&gt;

&lt;p&gt;Host: &lt;em&gt;according to the service you created in aiven.&lt;/em&gt;&lt;br&gt;
Port: &lt;em&gt;according to the service you created in aiven.&lt;/em&gt;&lt;br&gt;
Database: &lt;em&gt;defaultdb&lt;/em&gt;&lt;br&gt;
Username: &lt;em&gt;avnadmin&lt;/em&gt;&lt;br&gt;
Password: &lt;em&gt;according to the service you created in aiven.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdu2pxno2r01ddt0seygp.JPG" 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%2Fdu2pxno2r01ddt0seygp.JPG" alt="configurations" width="799" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test the connection by clicking on the button &lt;strong&gt;Test connection&lt;/strong&gt; in the bottom left corner then click &lt;strong&gt;finish&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fztsozkm0x8xrxr1f8c47.JPG" 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%2Fztsozkm0x8xrxr1f8c47.JPG" alt="test aiven conn" width="717" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After finishing the action, you will be able to see a new connection in the General tab under connections.&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%2F76nc5ti73p7ot244b5bw.JPG" 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%2F76nc5ti73p7ot244b5bw.JPG" alt="general connection" width="622" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 43: CA cerificate download and Importation into our Trusted root crtificates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The proceed without doing this step we will generate this error.&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%2Fkx6wzbchn5pqpxgjl2lm.JPG" 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%2Fkx6wzbchn5pqpxgjl2lm.JPG" alt="ca error" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need the Aiven PostgreSQL Service Certificate because Aiven enforces strict, mandatory SSL/TLS encryption for all database connections by default, and your computer needs to trust Aiven's identity to establish a secure link.&lt;br&gt;
Therefore we will need to download and import the CA certificate into our trusted root certifications. &lt;/p&gt;

&lt;p&gt;To proceed we will download the CA certificate in aiven.io.&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%2Ffhcuow5dxw2y9dz9pq47.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%2Ffhcuow5dxw2y9dz9pq47.png" alt="va cert download" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After dowloading the CA certificate, we will need to import the certifictae to our managed user certificates.&lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;managed user certificate control panel&lt;/strong&gt; in your computer, and under the &lt;strong&gt;Trusted root certification&lt;/strong&gt;, &lt;strong&gt;right click on certificates , under all tasks click on import&lt;/strong&gt;, click &lt;strong&gt;next&lt;/strong&gt; then &lt;strong&gt;browse&lt;/strong&gt; the VA certificte you downloaded and proceed until you click the &lt;strong&gt;finish button&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmex0ikvkec7gvbzrfhlx.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%2Fmex0ikvkec7gvbzrfhlx.png" alt="browse va cert" width="562" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To confirm if the process is successful, a success message box will appear.&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%2Fhjusmzua64oyz8vlesje.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%2Fhjusmzua64oyz8vlesje.png" alt="va import success" width="417" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;STEP 4: Connecting PowerBI to our aiven PostgreSQL database.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will now open our Power BI Desktop App, we will click on the &lt;strong&gt;blank report&lt;/strong&gt; to work with it.&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%2F1812hqy1waldejlxg14o.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%2F1812hqy1waldejlxg14o.png" alt="bi home" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expand the view in the **Get data **section as shown below.&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%2F9slsfgjgjhqbogbk6avg.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%2F9slsfgjgjhqbogbk6avg.png" alt="get data source" width="751" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;more...&lt;/strong&gt; to expand our options to open a dialog box.&lt;br&gt;
In the dialog box, search for &lt;strong&gt;PostgreSQL&lt;/strong&gt; database under the Database category and click &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3gpmytofi0zv7sd54bag.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%2F3gpmytofi0zv7sd54bag.png" alt="choosing get data  source" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A dialog box will appear that will require us to input the server and Database box with values that we obtain while creating the service in aiven.io.&lt;br&gt;
Server: &lt;strong&gt;Host name : port number&lt;/strong&gt; (Depending on your service)&lt;br&gt;
Database: &lt;strong&gt;defaultdb&lt;/strong&gt;&lt;br&gt;
Press &lt;strong&gt;ok&lt;/strong&gt; to proceed.&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%2F1nebbeb3375jtjxmtvw6.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%2F1nebbeb3375jtjxmtvw6.png" alt="server and database" width="799" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new dialog box will appear that will require our username and password. Refer to our service conection details in aiven.ai.&lt;br&gt;
Username: &lt;strong&gt;avnadmin&lt;/strong&gt;&lt;br&gt;
Password: (&lt;em&gt;as provided in the service you created&lt;/em&gt;)&lt;br&gt;
Click &lt;strong&gt;connect&lt;/strong&gt; to to proceed&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%2F9f1t83npkfxbaqzrdlb4.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%2F9f1t83npkfxbaqzrdlb4.png" alt="bi aiven" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will take us to a new window where we can access all the tables we had already imported in DBeaver. Choose the tables you want to work on. if you need to clean you can using the &lt;strong&gt;transform&lt;/strong&gt; button which will direct you into power query where you will be able to clean your data. Otherwise, you can &lt;strong&gt;load&lt;/strong&gt; the tables directly so you can start working on them.&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%2F0kjui4y54iqs6h7gfvg0.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%2F0kjui4y54iqs6h7gfvg0.png" alt="power bi connection done" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations you have successfully connected Power BI to your aiven postgreSQL database!&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this blog. Until next time.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>database</category>
      <category>learning</category>
      <category>data</category>
    </item>
    <item>
      <title>HOW EXCEL IS USED IN REAL WORLD DATA ANALYSIS</title>
      <dc:creator>Gideon Kiprono</dc:creator>
      <pubDate>Sat, 06 Jun 2026 21:14:21 +0000</pubDate>
      <link>https://dev.to/gideon_kiprono_bfdedde010/how-excel-is-used-in-real-world-data-analysis-ef2</link>
      <guid>https://dev.to/gideon_kiprono_bfdedde010/how-excel-is-used-in-real-world-data-analysis-ef2</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;h2&gt;
  
  
  What is Excel
&lt;/h2&gt;

&lt;p&gt;Excel is an important tool used in Data analysis. Excel enables a data analyst to ineteract with raw data. Raw data are always messy and require cleaning. This is now the work of excel to help in cleaning, organizing, analysing and visualizing data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Ways Excel is used in real world data analysis
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Financial Modelling&lt;/strong&gt; . Excel is used in building financial models that can be used to predict the performance of a business. Excel formulas can be used to calculate profits, revenue and costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auditing&lt;/strong&gt;.Auditors use excel in analysing financial records of an organization.They use it to check errors, detect any fraud and verify financial records.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tasks Automation&lt;/strong&gt;. Excel can be used to automate repetitive tasks using formulas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Visualization&lt;/strong&gt;. One can use excel to turn raw data into charts, graphs and dashboards for easier interpretation of the data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic featuers of Excel
&lt;/h2&gt;

&lt;p&gt;Excel as a tool has many features that enable it bto function properly, this includes;&lt;/p&gt;

&lt;h4&gt;
  
  
  Excel Ribbon
&lt;/h4&gt;

&lt;p&gt;The Excel Ribbon is the toolbar located at the top of the excel interface that contains all commands and tools organized into tabs such as Home, Insert, Data, Formulas and View. Each tab groups related functions for easy access.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Entry and Formatting
&lt;/h4&gt;

&lt;p&gt;Excel allows users to enter different kinds of data such as texts, numbers and dates. Formatting tools are also used for organizing data to improve readability.&lt;/p&gt;

&lt;h4&gt;
  
  
  Filtering and Sorting
&lt;/h4&gt;

&lt;p&gt;Filtering helps in displaying specific data, while sorting arranges data in ascending or descending order. They help to in organizing large datasets.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conditional Formating
&lt;/h4&gt;

&lt;p&gt;This is a special feature that helps in highlighting data based on specific rules, making it easier to identify trends and patterns.&lt;/p&gt;

&lt;h4&gt;
  
  
  Functions in Excel
&lt;/h4&gt;

&lt;p&gt;Excel has differnt kinds of functions that aid in data analysis;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Mathematical / Aggregate Functions&lt;/em&gt;: SUM(), AVERAGE(), MEDIAN(),MODE().&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Statistical Functions&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Text Functions&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Lookup Functions&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Date and time functions&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I am not going to lie, Before doing a deep dive into how excel works, my understanding was that excel was not that powerful in data analysis. To my suprise, I have come to know and appreciate excel as a powerful tool in the hands of any aspiring analyst. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>datascience</category>
      <category>data</category>
      <category>analytics</category>
    </item>
  </channel>
</rss>
