<?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>Forem: Sharon M.</title>
    <description>The latest articles on Forem by Sharon M. (@sharon_m).</description>
    <link>https://forem.com/sharon_m</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3708636%2F0dfe75c2-b0b9-46c2-bd36-b7453a53ceb2.png</url>
      <title>Forem: Sharon M.</title>
      <link>https://forem.com/sharon_m</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sharon_m"/>
    <language>en</language>
    <item>
      <title>Window Functions in PostgreSQL using DBeaver</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Mon, 02 Mar 2026 19:35:05 +0000</pubDate>
      <link>https://forem.com/sharon_m/window-functions-in-postgresql-using-dbeaver-doj</link>
      <guid>https://forem.com/sharon_m/window-functions-in-postgresql-using-dbeaver-doj</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Key Components of Window Functions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OVER()&lt;/li&gt;
&lt;li&gt;PARTITION BY&lt;/li&gt;
&lt;li&gt;ORDER BY&lt;/li&gt;
&lt;li&gt;Frame Clause (Optional)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;The Store Database&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Demonstrating Window Functions Using the Store Tables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dedicated Window Functions&lt;/li&gt;
&lt;li&gt;
Ranking Functions

&lt;ul&gt;
&lt;li&gt;ROW_NUMBER()&lt;/li&gt;
&lt;li&gt;RANK()&lt;/li&gt;
&lt;li&gt;DENSE_RANK()&lt;/li&gt;
&lt;li&gt;NTILE(n)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Offset (Navigation) Functions&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;In the previous article, we saw how &lt;strong&gt;joins&lt;/strong&gt; allow us to combine data from multiple related tables. Using a small retail store database, we connected the &lt;strong&gt;departments&lt;/strong&gt;, &lt;strong&gt;employees&lt;/strong&gt; and &lt;strong&gt;sales&lt;/strong&gt; tables to answer various questions about the business.&lt;/p&gt;

&lt;p&gt;Joins are essential in relational databases in that, when information is stored across several tables, joins bring the data together so that it can be analyzed and actually make sense.&lt;/p&gt;

&lt;p&gt;However, many analytical questions require something slightly different. Sometimes we first combine tables using joins, and other times we work directly with a single table. In both cases, the goal is the same: to analyze how rows relate to other rows in the dataset.&lt;/p&gt;

&lt;p&gt;For example, using our data we might want to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which employee has the &lt;strong&gt;highest sales within each department&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;What is the &lt;strong&gt;running total of sales over time&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;How does &lt;strong&gt;each sale compare to the previous sale&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;How can employees be &lt;strong&gt;ranked based on their performance&lt;/strong&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These types of questions require calculations that look at multiple rows at the same time while still keeping every row visible in the result. This is where window functions become useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Window functions&lt;/strong&gt; allow SQL to perform calculations across a group of related rows &lt;strong&gt;without collapsing&lt;/strong&gt; the results into a &lt;strong&gt;single summary row&lt;/strong&gt;. Unlike traditional aggregate functions such as &lt;code&gt;SUM()&lt;/code&gt; or &lt;code&gt;AVG()&lt;/code&gt;, which return one result for a group of rows, window functions perform calculations across multiple rows, but they keep every row visible in the result.&lt;/p&gt;

&lt;p&gt;In PostgreSQL, a function becomes a &lt;strong&gt;window function&lt;/strong&gt; when it is used together with the &lt;code&gt;OVER()&lt;/code&gt; clause. The &lt;code&gt;OVER()&lt;/code&gt; clause defines the &lt;strong&gt;window&lt;/strong&gt;, which is the set of rows the function will use during its calculation.&lt;/p&gt;

&lt;p&gt;Window functions are commonly used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rank rows within groups (assign ranking positions to rows based on values such as sales performance or salary).&lt;/li&gt;
&lt;li&gt;Calculate running totals (compute cumulative values across ordered rows, such as tracking total sales over time).&lt;/li&gt;
&lt;li&gt;Compare values between consecutive rows.&lt;/li&gt;
&lt;li&gt;Divide data into performance groups (divide rows into buckets based on metrics like revenue or sales volume).&lt;/li&gt;
&lt;li&gt;Analyze trends over time (examine how values change across an ordered sequence such as dates or transactions).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will see these as we demonstrate each window function using the store dataset.&lt;/p&gt;




&lt;h1&gt;
  
  
  Key Components of Window Functions
&lt;/h1&gt;

&lt;p&gt;Window functions rely on several components that determine &lt;strong&gt;how rows are grouped and ordered&lt;/strong&gt; during a calculation. The most important element is the &lt;code&gt;OVER()&lt;/code&gt; clause which may also include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PARTITION BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ORDER BY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Window frame clauses (Optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OVER()
&lt;/h2&gt;

&lt;p&gt;As we earlier stated, &lt;strong&gt;a regular SQL function becomes a window function when it is used together with &lt;code&gt;OVER()&lt;/code&gt;&lt;/strong&gt;. &lt;code&gt;OVER()&lt;/code&gt; defines the set of rows (the window) that the function should consider during the calculation.&lt;/p&gt;

&lt;p&gt;General syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt;
    &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PARTITION BY
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PARTITION BY&lt;/code&gt; &lt;strong&gt;divides rows into groups&lt;/strong&gt; (&lt;em&gt;partitions&lt;/em&gt;). Each partition is processed independently by the window function.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures calculations are performed separately for each department.&lt;/p&gt;

&lt;h2&gt;
  
  
  ORDER BY
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ORDER BY&lt;/code&gt; inside the &lt;code&gt;OVER()&lt;/code&gt; statement &lt;strong&gt;defines the order of rows&lt;/strong&gt; within each partition.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;sale_date&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With time you will observe that ordering is important when performing calculations such as &lt;em&gt;ranking&lt;/em&gt;, &lt;em&gt;running totals&lt;/em&gt; or &lt;em&gt;comparisons between rows&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frame Clause (Optional)
&lt;/h2&gt;

&lt;p&gt;A window frame &lt;strong&gt;further limits which rows are used&lt;/strong&gt; in a calculation within a partition. &lt;strong&gt;Frame clauses&lt;/strong&gt; define which rows relative to the current row should be included in the calculation.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;This frame starts from the &lt;strong&gt;first row&lt;/strong&gt; of the &lt;strong&gt;partition&lt;/strong&gt; and continues up to the &lt;strong&gt;current row&lt;/strong&gt;. It is commonly used when &lt;em&gt;calculating cumulative totals&lt;/em&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Store Database
&lt;/h1&gt;

&lt;p&gt;To demonstrate window functions, we will continue using the same &lt;strong&gt;retail store dataset&lt;/strong&gt; introduced in the previous article on &lt;strong&gt;joins&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The database contains three tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;departments&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.amazonaws.com%2Fuploads%2Farticles%2Fgh2x24t3bp9e6toa56sy.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.amazonaws.com%2Fuploads%2Farticles%2Fgh2x24t3bp9e6toa56sy.png" alt="departments table" width="495" height="164"&gt;&lt;/a&gt; Each department has multiple employees.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;employees &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.amazonaws.com%2Fuploads%2Farticles%2F7hcug2u8ty1qhczd8zqr.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.amazonaws.com%2Fuploads%2Farticles%2F7hcug2u8ty1qhczd8zqr.png" alt="employees table" width="773" height="213"&gt;&lt;/a&gt; Each employee belongs to one department.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sales &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.amazonaws.com%2Fuploads%2Farticles%2Fnbytrp0log5iimnlpeit.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.amazonaws.com%2Fuploads%2Farticles%2Fnbytrp0log5iimnlpeit.png" alt="sales table" width="603" height="215"&gt;&lt;/a&gt; Employees generate sales that are recorded in this table.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Demonstrating Window Functions Using the Store Tables
&lt;/h1&gt;

&lt;p&gt;In PostgreSQL, there is &lt;strong&gt;no completely separate window function library&lt;/strong&gt;. Instead, several built-in functions can operate as window functions when they are used with the &lt;code&gt;OVER()&lt;/code&gt; clause. These functions generally fall into two categories:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Dedicated Window Functions
&lt;/h2&gt;

&lt;p&gt;Dedicated window functions are specialized analytical functions designed to operate across a set of rows related to the current row without collapsing the result into a single output row.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  - Ranking Functions
&lt;/h2&gt;

&lt;p&gt;Ranking functions &lt;strong&gt;assign a ranking value&lt;/strong&gt; to each row within a partition. They include:&lt;/p&gt;




&lt;h2&gt;
  
  
  a. ROW_NUMBER()
&lt;/h2&gt;

&lt;p&gt;Assigns a &lt;strong&gt;unique, sequential number&lt;/strong&gt; to each row within a partition. The numbering starts at 1 and increases based on the order specified in the &lt;code&gt;ORDER BY&lt;/code&gt; clause. If &lt;code&gt;PARTITION BY&lt;/code&gt; is used, the numbering &lt;strong&gt;restarts for each partition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This function is often used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determine the order of events.&lt;/li&gt;
&lt;li&gt;Retrieve the first or latest record in a group.&lt;/li&gt;
&lt;li&gt;Rank rows uniquely even when values are identical.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;em&gt;Question: In what order did each employee record their sales transactions?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sale_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;ROW_NUMBER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sale_date&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;sale_sequence&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JOIN&lt;/strong&gt; - The employees and sales tables are joined so that each sale can be associated with the employee who recorded it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PARTITION BY&lt;/strong&gt; - This divides the result set into partitions for each employee. Each employee’s sales are processed independently and the row numbering resets for each employee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ORDER BY&lt;/strong&gt; - This sorts the sales chronologically within each employee’s partition.&lt;/p&gt;

&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fzlqyqiqm4k4f96ylviy9.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.amazonaws.com%2Fuploads%2Farticles%2Fzlqyqiqm4k4f96ylviy9.png" alt="ROW_NUMBER()" width="610" height="217"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  b. RANK()
&lt;/h2&gt;

&lt;p&gt;Assigns a &lt;strong&gt;ranking value to each row based on the order defined&lt;/strong&gt; in the &lt;code&gt;ORDER BY&lt;/code&gt; clause. If &lt;strong&gt;two or more rows have the same value&lt;/strong&gt;, they receive the &lt;strong&gt;same rank&lt;/strong&gt;. The &lt;strong&gt;next rank number is then skipped&lt;/strong&gt;, &lt;em&gt;creating a gap in the ranking sequence&lt;/em&gt;. For example, if two rows share rank 1, the next row will receive rank 3 instead of 2.&lt;/p&gt;

&lt;p&gt;This function is often used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rank employees by performance.&lt;/li&gt;
&lt;li&gt;Identify top-selling employees.&lt;/li&gt;
&lt;li&gt;Determine position within a leaderboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, there are no ties in my dataset. To demonstrate how &lt;code&gt;RANK()&lt;/code&gt; behaves when ties occur, I inserted an additional sale for Daenerys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sale_date&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;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-04'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fhyzm6w2q8bsh842crgod.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.amazonaws.com%2Fuploads%2Farticles%2Fhyzm6w2q8bsh842crgod.png" alt="inserted an additional sale for Daenerys" width="590" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Question: Which sales transactions generated the highest revenue?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;s&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="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sales_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2F9q7xijmr66nw7dgro2ea.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.amazonaws.com%2Fuploads%2Farticles%2F9q7xijmr66nw7dgro2ea.png" alt="RANK()" width="523" height="231"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  c. DENSE_RANK()
&lt;/h2&gt;

&lt;p&gt;Also assigns a &lt;strong&gt;rank&lt;/strong&gt; to each row but &lt;strong&gt;without gaps in the sequence&lt;/strong&gt;, even if there are &lt;strong&gt;ties&lt;/strong&gt;. This function is useful when &lt;strong&gt;continuous ranking positions&lt;/strong&gt; are required in reports or analyses.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Question: Which sales transactions generated the highest revenue (while maintaining a continuous ranking sequence)?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;s&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="n"&gt;DENSE_RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sales_dense_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fxkmwytxmzsobryzrtynp.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.amazonaws.com%2Fuploads%2Farticles%2Fxkmwytxmzsobryzrtynp.png" alt="DENSE_RANK()" width="538" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;strong&gt;difference&lt;/strong&gt; between &lt;code&gt;RANK()&lt;/code&gt; and &lt;code&gt;DENSE_RANK()&lt;/code&gt; becomes clear when &lt;strong&gt;two rows share the same value&lt;/strong&gt;. In such cases, &lt;strong&gt;&lt;code&gt;RANK()&lt;/code&gt; introduces gaps&lt;/strong&gt; in the ranking sequence, while &lt;strong&gt;&lt;code&gt;DENSE_RANK()&lt;/code&gt; maintains a continuous ranking order&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  d. NTILE(n)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Divides rows into n roughly equal-sized groups&lt;/strong&gt; (&lt;em&gt;tiles or buckets&lt;/em&gt;). Then it returns the bucket number assigned to each row.&lt;/p&gt;

&lt;p&gt;This function is often used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grouping customers into spending levels.&lt;/li&gt;
&lt;li&gt;Dividing employees into performance groups.&lt;/li&gt;
&lt;li&gt;Creating quartiles (&lt;code&gt;NTILE(4)&lt;/code&gt;), deciles (&lt;code&gt;NTILE(10)&lt;/code&gt;) or percentiles for analysis.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;em&gt;Question: How can employees be divided into performance groups based on their total sales?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;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="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;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_sales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NTILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;performance_group&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2F1x0uwt662cq217dvpwfj.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.amazonaws.com%2Fuploads%2Farticles%2F1x0uwt662cq217dvpwfj.png" alt="NTILE(n)" width="569" height="197"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  - Offset (Navigation) Functions
&lt;/h2&gt;

&lt;p&gt;In the &lt;strong&gt;next part&lt;/strong&gt; we will look into navigation (offset) functions such as &lt;code&gt;LAG()&lt;/code&gt;, &lt;code&gt;LEAD()&lt;/code&gt;, &lt;code&gt;FIRST_VALUE()&lt;/code&gt;, &lt;code&gt;LAST_VALUE()&lt;/code&gt; and &lt;code&gt;NTH_VALUE()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I hope this article helps you to better understand how window  functions are used in SQL.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Joins in PostgreSQL Using DBeaver</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Mon, 02 Mar 2026 19:25:45 +0000</pubDate>
      <link>https://forem.com/sharon_m/joins-in-postgresql-using-dbeaver-4oip</link>
      <guid>https://forem.com/sharon_m/joins-in-postgresql-using-dbeaver-4oip</guid>
      <description>&lt;p&gt;In this article, I will walk through the process step by step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;1. Creating a Database and Schema&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the Database&lt;/li&gt;
&lt;li&gt;Create a Schema&lt;/li&gt;
&lt;li&gt;Set the Search Path&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;2. Creating the Tables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Table 1: departments&lt;/li&gt;
&lt;li&gt;Table 2: employees&lt;/li&gt;
&lt;li&gt;Table 3: sales&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;3. Demonstrating Joins Using the Store Tables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a) INNER JOIN&lt;/li&gt;
&lt;li&gt;b) LEFT JOIN&lt;/li&gt;
&lt;li&gt;c) RIGHT JOIN&lt;/li&gt;
&lt;li&gt;d) FULL JOIN&lt;/li&gt;
&lt;li&gt;e) NATURAL JOINS&lt;/li&gt;
&lt;li&gt;f) CROSS JOINS&lt;/li&gt;
&lt;li&gt;g) SELF JOIN&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Inclusive vs Exclusive Joins&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;Suppose you manage a small retail store. The store has multiple departments i.e Electronics, Clothing, and Groceries. Each department has employees assigned to it. And those employees generate sales.&lt;/p&gt;

&lt;p&gt;Now you want to evaluate performance.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Which employee belongs to which department?&lt;/li&gt;
&lt;li&gt;How much revenue has each employee generated?&lt;/li&gt;
&lt;li&gt;Are there employees who have not recorded any sales?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, the data you need is not stored in a single table. It is distributed across several related tables. That is how relational databases are designed. Data is separated logically to reduce redundancy and maintain consistency.&lt;/p&gt;

&lt;p&gt;To demonstrate how this data can be combined meaningfully, I will use PostgreSQL as the database management system. I am connecting to a locally installed &lt;strong&gt;PostgreSQL&lt;/strong&gt; server using &lt;strong&gt;DBeaver&lt;/strong&gt;, which serves as the SQL client for writing and executing queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Creating a Database and Schema
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create the Database
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;database&lt;/em&gt; is the top-level container that stores schemas, tables, functions, and other database objects. This statement creates a new PostgreSQL database named &lt;em&gt;demo&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating the database, I connect to it in DBeaver so that all subsequent objects are created inside &lt;em&gt;demo&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Create a Schema
&lt;/h3&gt;

&lt;p&gt;A schema organizes database objects within a database. It acts as a namespace, preventing naming conflicts and grouping related tables logically. This statement creates a schema named &lt;em&gt;joins_window&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;In this case, the schema &lt;em&gt;joins_window&lt;/em&gt; will contain all tables used in this demonstration.&lt;/p&gt;




&lt;h3&gt;
  
  
  Set the Search Path
&lt;/h3&gt;

&lt;p&gt;By default, PostgreSQL searches for tables in the public schema. Setting the search path ensures that any tables created or queried will reference the &lt;em&gt;joins_window&lt;/em&gt; schema automatically. Syntax:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This keeps the work organized and avoids having to prefix every table with the schema name.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Creating the Tables
&lt;/h2&gt;

&lt;p&gt;To demonstrate joins effectively, I created three related tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;departments&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;employees&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;sales&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each table represents a distinct entity in the store system, and the relationships between them are enforced using &lt;em&gt;primary&lt;/em&gt; and &lt;em&gt;foreign&lt;/em&gt; keys.&lt;/p&gt;




&lt;h3&gt;
  
  
  Table 1: departments
&lt;/h3&gt;

&lt;p&gt;Syntax:&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;departments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;department_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;departments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Electronics'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Clothing'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Groceries'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;department_id&lt;/em&gt; column is defined as a &lt;strong&gt;primary key&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
This guarantees that each department has a unique identifier.&lt;br&gt;&lt;br&gt;
The &lt;em&gt;SERIAL&lt;/em&gt; keyword automatically generates sequential integer values for each new row.&lt;/p&gt;

&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fgdny08c7ii2pbpq4vb08.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.amazonaws.com%2Fuploads%2Farticles%2Fgdny08c7ii2pbpq4vb08.png" alt="Table 1: departments" width="522" height="161"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Table 2: employees
&lt;/h3&gt;

&lt;p&gt;Syntax:&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;employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;employee_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;salary&lt;/span&gt; &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;employees&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;department_id&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;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Arya'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Baelish'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cersei'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Daenerys'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;45000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sansa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;em&gt;employee_id&lt;/em&gt; is the &lt;strong&gt;primary key&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
The &lt;em&gt;department_id&lt;/em&gt; column is a &lt;strong&gt;foreign key&lt;/strong&gt; referencing departments(department_id). This establishes a relationship between employees and departments.&lt;/p&gt;

&lt;p&gt;The foreign key constraint enforces &lt;em&gt;referential integrity&lt;/em&gt;. In practical terms, it prevents inserting an employee with a department that does not exist in the departments table.&lt;/p&gt;

&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2F7m50jt0ocqo6k313ekjc.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.amazonaws.com%2Fuploads%2Farticles%2F7m50jt0ocqo6k313ekjc.png" alt="Table 2: employees" width="707" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This design models a &lt;em&gt;one-to-many&lt;/em&gt; relationship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One department can have many employees.&lt;/li&gt;
&lt;li&gt;Each employee belongs to exactly one department.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Table 3: sales
&lt;/h3&gt;

&lt;p&gt;Syntax:&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;sales&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sale_id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sale_date&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sale_date&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;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-02'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-03'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2500&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-02'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, &lt;em&gt;sale_id&lt;/em&gt; is the &lt;strong&gt;primary key&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
The &lt;em&gt;employee_id&lt;/em&gt; column is a &lt;strong&gt;foreign key&lt;/strong&gt; referencing employees(employee_id). This ensures that every sale is linked to a valid employee.&lt;/p&gt;

&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2F00cpe8ijqtx8hcmoq52k.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.amazonaws.com%2Fuploads%2Farticles%2F00cpe8ijqtx8hcmoq52k.png" alt="Table 3: sales" width="624" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This creates another &lt;em&gt;one-to-many&lt;/em&gt; relationship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One employee can record multiple sales.&lt;/li&gt;
&lt;li&gt;Each sale belongs to one employee.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  3. Demonstrating Joins Using the Store Tables
&lt;/h2&gt;

&lt;p&gt;With the tables now defined and populated, we can begin examining how joins operate. The data we need is distributed across separate tables by design. Departments are stored independently from employees, and sales are stored independently from employees. This separation prevents redundancy and enforces data integrity. However, it also means that answering even simple analytical questions requires combining tables.&lt;/p&gt;

&lt;p&gt;That combination is achieved using &lt;strong&gt;joins&lt;/strong&gt;. Here is a list of the joins we will be looking into:&lt;/p&gt;

&lt;p&gt;a) &lt;strong&gt;INNER JOIN&lt;/strong&gt; - returns only matching rows (intersection of both tables)&lt;br&gt;&lt;br&gt;
b) &lt;strong&gt;LEFT JOIN&lt;/strong&gt; - preserves all rows from the left table.&lt;br&gt;&lt;br&gt;
c) &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; - preserves all rows from the right table.&lt;br&gt;&lt;br&gt;
d) &lt;strong&gt;FULL JOIN&lt;/strong&gt; - preserves all rows from both tables.&lt;br&gt;&lt;br&gt;
e) &lt;strong&gt;CROSS JOINS&lt;/strong&gt; - generates all possible row combinations (Cartesian product).&lt;br&gt;&lt;br&gt;
f) &lt;strong&gt;NATURAL JOIN&lt;/strong&gt; - automatically matches columns with identical names.&lt;br&gt;&lt;br&gt;
g) &lt;strong&gt;SELF JOIN&lt;/strong&gt; - joins a table to itself to model hierarchical relationships.  &lt;/p&gt;

&lt;p&gt;In the sections that follow, some additional rows may be inserted to clearly demonstrate how different join types behave when matching data is present and when it is &lt;em&gt;null&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  a) INNER JOIN
&lt;/h2&gt;

&lt;p&gt;An &lt;strong&gt;INNER JOIN&lt;/strong&gt; returns only the rows where the join condition evaluates to true in both tables. In other words, it keeps only matching records.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column2&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&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;table2&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&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;strong&gt;ON clause&lt;/strong&gt; defines the relationship between the two tables. Without it, PostgreSQL would not know how the rows should be matched.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;&lt;span class="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="n"&gt;e&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;departments&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fx8742z06fo5gx4wnse5m.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.amazonaws.com%2Fuploads%2Farticles%2Fx8742z06fo5gx4wnse5m.png" alt="INNER JOIN" width="563" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL compares &lt;em&gt;employees.department_id&lt;/em&gt; with &lt;em&gt;departments.department_id&lt;/em&gt;. For every employee row, it searches for a department with the same &lt;em&gt;department_id&lt;/em&gt;. When a match is found, the rows are combined into a single result row. If no match is found, that employee would not appear in the result. It returns only the intersection between the two tables.&lt;/p&gt;


&lt;h2&gt;
  
  
  b) LEFT JOIN
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;LEFT JOIN&lt;/strong&gt; guarantees that all rows from the left table appear in the result set. The key difference from an &lt;strong&gt;INNER JOIN&lt;/strong&gt; is this:&lt;/p&gt;

&lt;p&gt;If a matching row does not exist in the right table, the left table row is still preserved. The columns from the right table are filled with &lt;em&gt;NULL&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column2&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At the moment, every employee in our dataset has at least one sale. That would make the &lt;strong&gt;LEFT JOIN&lt;/strong&gt; behave similarly to an &lt;strong&gt;INNER JOIN&lt;/strong&gt;. To demonstrate the difference clearly, we insert an employee who has no associated sales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;employees&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;department_id&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;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Jon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fosztjivqqrnux88bauy3.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.amazonaws.com%2Fuploads%2Farticles%2Fosztjivqqrnux88bauy3.png" alt="INSERT INTO" width="657" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now Jon exists in the employees table but has no corresponding row in the sales table. Let us see how the output will be.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Question: Which employees exist in the company, and what sales have they recorded, if any?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sale_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Ftwh7s7d19692asog39dy.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.amazonaws.com%2Fuploads%2Farticles%2Ftwh7s7d19692asog39dy.png" alt="LEFT JOIN" width="540" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL starts with the employees table (the left table). For each employee, it attempts to find matching rows in the sales table using &lt;em&gt;employee_id&lt;/em&gt;. If matches are found, the rows are combined. If no match is found, the employee row is still returned.&lt;/p&gt;


&lt;h2&gt;
  
  
  c) RIGHT JOIN
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; is structurally similar to a &lt;strong&gt;LEFT JOIN&lt;/strong&gt;, but while a &lt;strong&gt;LEFT JOIN&lt;/strong&gt; guarantees all rows from the left table, a &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; guarantees all rows from the right table. And if a matching row does not exist in the left table, the right table row is still returned. The columns from the left table are filled with &lt;em&gt;NULL&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column2&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;
&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The table written after &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; is the one that will always be preserved (all rows from the right table).&lt;/p&gt;
&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Question: What sales were recorded, and who is associated with each one?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sale_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;RIGHT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fcd7bmf81nztx1ito1fej.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.amazonaws.com%2Fuploads%2Farticles%2Fcd7bmf81nztx1ito1fej.png" alt="RIGHT JOIN" width="533" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL starts with the sales table (the right table). For each sale, it attempts to find a matching employee using &lt;em&gt;employee_id&lt;/em&gt;. If a match is found, the rows are combined. If no match is found, the sale row is still returned.&lt;/p&gt;

&lt;p&gt;When we performed a &lt;strong&gt;LEFT JOIN&lt;/strong&gt; between employees and sales, Jon appeared with &lt;em&gt;NULL&lt;/em&gt; values in the sales columns because he had no recorded sales. In this &lt;strong&gt;RIGHT JOIN&lt;/strong&gt;, Jon does not appear at all because the right table is sales. Jon has no row in the sales table, therefore, there is nothing for the &lt;strong&gt;RIGHT JOIN&lt;/strong&gt; to preserve on his behalf.&lt;/p&gt;


&lt;h2&gt;
  
  
  d) FULL JOIN
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;FULL JOIN&lt;/strong&gt; combines the qualities of both &lt;strong&gt;LEFT&lt;/strong&gt; and &lt;strong&gt;RIGHT&lt;/strong&gt; joins. In that, no row from either table is excluded. If a row exists in one table but not in the other, it still appears in the result and the columns from the missing side are filled with &lt;em&gt;NULL&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;column2&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;
&lt;span class="k"&gt;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common_field&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Question: Provide a complete view of employee records and sales transactions.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sale_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;FULL&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;sales&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fpem0qjkteao33n7ptw4a.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.amazonaws.com%2Fuploads%2Farticles%2Fpem0qjkteao33n7ptw4a.png" alt="FULL JOIN" width="534" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL attempts to match employees to sales using &lt;em&gt;employee_id&lt;/em&gt;. Where a match exists, the rows are combined.&lt;/p&gt;


&lt;h2&gt;
  
  
  e) NATURAL JOINS
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;NATURAL JOIN&lt;/strong&gt; is a type of join that automatically matches columns with the same name in both tables. It is important to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NATURAL JOIN&lt;/strong&gt; only works when columns have the same name.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Warning&lt;/em&gt;: &lt;strong&gt;NATURAL JOIN can be dangerous&lt;/strong&gt; if tables share multiple same-named columns. NATURAL JOINS will join on all shared column names, not just the one you intended.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our schema, both employees and departments contain a column named &lt;em&gt;department_id&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;column_list&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&lt;/span&gt;
&lt;span class="k"&gt;NATURAL&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;table2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;employee_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;department_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;NATURAL&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;departments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fd4nhpqn3mkh9khch1nla.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.amazonaws.com%2Fuploads%2Farticles%2Fd4nhpqn3mkh9khch1nla.png" alt="NATURAL JOINS" width="466" height="217"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  f) CROSS JOINS
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;CROSS JOIN&lt;/strong&gt; produces a &lt;em&gt;Cartesian product&lt;/em&gt; of two tables, that means every row from the first table is combined with every row from the second table.&lt;/p&gt;

&lt;p&gt;So, if table A contains &lt;em&gt;m&lt;/em&gt; rows and table B contains &lt;em&gt;n&lt;/em&gt; rows, the result of a &lt;strong&gt;CROSS JOIN&lt;/strong&gt; will contain: &lt;em&gt;m × n rows&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;column_list&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;table1&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;table2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Question: What are all possible employee–department assignment combinations?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&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;departments&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fvs0p5rwyzb6vmm9yd7sp.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.amazonaws.com%2Fuploads%2Farticles%2Fvs0p5rwyzb6vmm9yd7sp.png" alt="CROSS JOINS" width="464" height="441"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  g) SELF JOIN
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;SELF JOIN&lt;/em&gt; occurs when a table is joined to itself. At first glance, that may seem unnecessary, I honestly thought the same thing when I came across this join. Why would a table need to join to itself? The answer actually lies in hierarchical relationships. What does that mean? Many real-world structures are recursive in nature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An employee reports to another employee (Maybe a manager).&lt;/li&gt;
&lt;li&gt;A product category contains subcategories.&lt;/li&gt;
&lt;li&gt;A comment replies to another comment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each case, the relationship exists within the same table.&lt;/p&gt;

&lt;p&gt;Hence why we use &lt;em&gt;SELF JOIN&lt;/em&gt;, it allows us to treat the same table as two logical instances and define a relationship between them.&lt;/p&gt;

&lt;p&gt;Now this is where we slightly modify the employees table to make it meaningful. We will introduce a reporting structure in the employees table by adding a &lt;em&gt;manager_id&lt;/em&gt; column:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;manager_id&lt;/em&gt; will reference another &lt;em&gt;employee_id&lt;/em&gt; in the same table.&lt;/p&gt;

&lt;p&gt;Next, we assign managers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- Arya reports to Baelish&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;--Cersei reports to Baelish&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- Sansa reports to Daenerys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Fmofu4dt407iwpcbfj3o1.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.amazonaws.com%2Fuploads%2Farticles%2Fmofu4dt407iwpcbfj3o1.png" alt="UPDATE employees Table" width="782" height="216"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  General Syntax:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;columns&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="n"&gt;t1&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;related_column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Question: For each employee, who is their assigned manager?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&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.amazonaws.com%2Fuploads%2Farticles%2Ffzqf2jpxdu1e5tdq4dmm.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.amazonaws.com%2Fuploads%2Farticles%2Ffzqf2jpxdu1e5tdq4dmm.png" alt="SELF JOIN" width="360" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The table &lt;em&gt;employees&lt;/em&gt; is referenced twice where &lt;em&gt;e&lt;/em&gt; represents the employee and &lt;em&gt;m&lt;/em&gt; represents the manager. PostgreSQL matches &lt;em&gt;e.manager_id&lt;/em&gt; to &lt;em&gt;m.employee_id&lt;/em&gt;, where a match exists, the employee is paired with their manager. If an employee has no assigned manager, the manager column appears as &lt;em&gt;NULL&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Inclusive vs Exclusive Joins
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6e7wiicm5lok3lvb44x.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.amazonaws.com%2Fuploads%2Farticles%2Fc6e7wiicm5lok3lvb44x.jpg" alt="Inclusive vs Exclusive Joins" width="616" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inclusive joins&lt;/strong&gt; return matching rows along with unmatched rows from one or both tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exclusive joins&lt;/strong&gt; return only unmatched rows by filtering NULL values after an outer join.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exclusive joins = Outer Join + NULL filter&lt;/em&gt;. These are not separate SQL keywords, but rather combinations of outer joins with filtering conditions using WHERE ... IS NULL.&lt;/p&gt;

&lt;p&gt;This used in analytics to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find customers without orders.&lt;/li&gt;
&lt;li&gt;Find products never sold.&lt;/li&gt;
&lt;li&gt;Find users who didn’t log in.&lt;/li&gt;
&lt;li&gt;Find orphaned records.
You can learn more about Inclusive vs Exclusive Joins &lt;a href="https://www.geeksforgeeks.org/postgresql/explicit-vs-implicit-joins-in-postgresql/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I hope this article helps you to better understand how joins are used in SQL.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>sql</category>
      <category>database</category>
      <category>dbeaver</category>
    </item>
    <item>
      <title>Introduction to MS Excel for Data Analysis</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Sun, 15 Feb 2026 15:27:03 +0000</pubDate>
      <link>https://forem.com/sharon_m/introduction-to-ms-excel-for-data-analysis-12n9</link>
      <guid>https://forem.com/sharon_m/introduction-to-ms-excel-for-data-analysis-12n9</guid>
      <description>&lt;p&gt;Most people think Excel is just for typing in data and doing quick totals. But when I use Excel for data analysis, I treat it like a workspace where I analyze raw data.&lt;/p&gt;

&lt;p&gt;Microsoft Excel is still one of the most used tools in organizations. Even with newer platforms around, a lot of real analysis still starts in Excel. And honestly, I get why. It’s flexible, familiar, easily accessible and it lets you explore and interpret data step by step.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through the flow I follow from the moment I receive a dataset to the point where I can confidently present insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxaznqxvz222hyuuwq9o7.png" alt="Step by Step guide" width="800" height="1200"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Establishing the Analytical Objective&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When I receive data, my first instinct is not to calculate but to understand what I’m solving.&lt;/p&gt;

&lt;p&gt;Sometimes the question is obvious. Sometimes it isn’t. Either way, I ask myself what decision the analysis should support. Am I explaining why something changed over time? Am I comparing regions? Am I checking if we’re making profit or loss? Am I investigating a performance decline?&lt;/p&gt;

&lt;p&gt;If the objective is not clearly defined, it is easy to calculate figures that appear meaningful but do not answer the core question. Defining the objective early ensures that each step taken in Excel aligns with a specific goal.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;2. Evaluating the Raw Data Structure&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After defining the objectives, I scroll through the data slowly, examining its structure rather than immediately performing calculations so as to determine what kind of data I’m dealing with.&lt;/p&gt;

&lt;p&gt;I try to understand what each row represents. One row could be a transaction, a customer, or an event, a product record. Then I look at the columns to ensure headers are clear, if there are blank rows in the middle, and whether someone added totals inside the raw dataset.&lt;/p&gt;

&lt;p&gt;For data to be suitable for analysis, it should follow a consistent structure. Each row should represent a single record and each column should represent one variable.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;3. Data Preparation and Cleaning&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is the step many beginners might find challenging but don’t skip it, the quality of your data matters and this stage determines whether the final results will be reliable.&lt;/p&gt;

&lt;p&gt;I check for duplicates because duplicates can make totals look larger than they should, I always check to see if a duplicate is legitimate or erroneous. I also identify missing values because blanks can break calculations or hide patterns.&lt;/p&gt;

&lt;p&gt;I also confirm and change data types because Excel needs to understand what a date is and what a number is. If Excel stores a number as text, the formula might not behave the way I expect.&lt;/p&gt;

&lt;p&gt;I also scan for values that don’t make sense. Things like negative quantities, strange dates, or percentages that look unrealistic. This stage isn’t exciting, and there is a lot more that goes into data cleaning in excel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/excel/top-5-excel-data-cleaning-techniques-to-know-in-2023/" rel="noopener noreferrer"&gt;Learn more about Data Cleaning with Excel&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. Designing a Logical Workbook Structure&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the raw data looks clean and usable, I separate my work into sheets. This is one habit I always keep.&lt;/p&gt;

&lt;p&gt;I keep raw data in one sheet which I usually rename it as Original. Then I copy and paste the original clean data onto another new sheet each labeled depending on what type of metrics are being performed on the sheet in question (e.g. calculations, summaries, pivot tables, dashboard).&lt;/p&gt;

&lt;p&gt;When everything lives in one giant sheet, errors hide. It also becomes hard to explain what you did. When sheets are separated, you are able to easily navigate through your thought process because it is easy to keep track of what you have done.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;5. Creating new Columns&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After ensuring the data is clean and structured, I determine which additional columns (metrics) are required based on my objectives for this project.&lt;/p&gt;

&lt;p&gt;Raw datasets often contain transactions but lack interpretive measures So, I create derived metrics based on what I’m trying to find out; I may calculate revenue, profit, percentage margins, growth rates, or time-based groupings such as month or quarter or even generate categories.&lt;/p&gt;

&lt;p&gt;It is important to do the necessary calculations, focusing only on metrics that directly affect or support the analysis. This keeps the workbook manageable and also aligned with the objective.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/excel/basic-excel-formulas/" rel="noopener noreferrer"&gt;Learn more about formulars and functions in Excel&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;6. Summarizing Data and Identifying Patterns&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once derived metrics (new columns) are in place, I move from row-level calculations to pattern recognition.&lt;/p&gt;

&lt;p&gt;PivotTables are usually my first choice for this because they allow grouping, aggregation and comparison across categories without rewriting formulas repeatedly. So, instead of focusing on individual records, I proceed to analyzing trends and relationships.&lt;/p&gt;

&lt;p&gt;For example; You can compare regions, categories, months or IDs without writing long formulas.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;7. Visual Interpretation of Analytical Results&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Visual representation supports interpretation. That is, depending on the objective, I use line charts to show trends, column charts to compare categories or tables when precise values are required. I select visualizations based on clarity because charts should simplify understanding. Each chart you choose should clearly represent what you want to interpret. And different charts are fit for different representations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/excel/types-of-charts-in-excel/" rel="noopener noreferrer"&gt;Learn more about charts in Excel&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;8. Scenario Evaluation and Sensitivity Analysis (Testing “What If” Scenarios)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In more &lt;strong&gt;advanced analyses&lt;/strong&gt;, an analyst can evaluate how changes in assumptions affect outcomes. Excel allows input values to be adjusted while linked formulas recalculate results automatically. This makes it possible to test how changes in cost, pricing or volume influence performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.geeksforgeeks.org/excel/what-if-analysis-with-data-tables-in-excel/" rel="noopener noreferrer"&gt;Learn more about What-If Analysis with Data Tables in Excel&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;9. The Last Step Is Always Presentation and Communication of Findings&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The final stage involves presenting insights clearly. That is why it is important to create a clean summary that someone else can understand.&lt;/p&gt;

&lt;p&gt;In order to do so, I create one final sheet in the workbook where I bring together the most important results in one place (The Dashboard). This sheet does not contain raw data or calculations. Instead, it displays key metrics, selected charts, summary figures that directly answer the original analytical question and slicers for interaction.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Excel is powerful, but it has some limitations.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Excel alone may not be enough; when data becomes very large, or when many people need access at the same time or when reporting must refresh automatically. At that point, an analyst such as you and I, may require complementary tools such as Power BI.&lt;/p&gt;

</description>
      <category>excel</category>
      <category>microsoft</category>
      <category>dataanalysis</category>
    </item>
    <item>
      <title>How Analysts Translate Messy Data, DAX, and Dashboards into Action Using Power BI</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Mon, 09 Feb 2026 19:09:55 +0000</pubDate>
      <link>https://forem.com/sharon_m/how-analysts-translate-messy-data-dax-and-dashboards-into-action-using-power-bi-347j</link>
      <guid>https://forem.com/sharon_m/how-analysts-translate-messy-data-dax-and-dashboards-into-action-using-power-bi-347j</guid>
      <description>&lt;p&gt;Very many organizations are not suffering from having too little of data, most organizations are actually drowning in too much of data. They have spreadsheets everywhere. Reports that don’t match. Systems that don’t talk to each other. Numbers that look fine until you try to explain them.&lt;/p&gt;

&lt;p&gt;That’s where analysts come in and, in our case, the analyst is using &lt;strong&gt;Power BI&lt;/strong&gt; to answer the question that most companies have:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“We have the data, but we have no idea what it’s telling us.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Power BI give analysts the tools to take messy, uncooperative data and turn it into something people can actually use. But that only works when &lt;a href="https://worxwide.com/insights/data-design-and-insights-using-power-bi/" rel="noopener noreferrer"&gt;every step&lt;/a&gt; in the process is done correctly.&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.amazonaws.com%2Fuploads%2Farticles%2Fb544l0gm6mrucn6d5sa5.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fb544l0gm6mrucn6d5sa5.webp" alt="Steps" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;From messy data to something you can interpret and report on: Data Shaping&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Real-world data is rarely clean. Anyone who has opened an Excel file pulled from an operational system knows this immediately.&lt;/p&gt;

&lt;p&gt;You might be presented with data that contains blank rows or duplicate records or even data saved in the wrong format of data type for example Dates saved as text. Different words all referring to the same thing for example &lt;strong&gt;“N/A”&lt;/strong&gt;, &lt;strong&gt;“None”&lt;/strong&gt;, and &lt;strong&gt;“Error”&lt;/strong&gt;. County names written five different ways: &lt;strong&gt;Nairobi&lt;/strong&gt;, &lt;strong&gt;nairobi&lt;/strong&gt;, &lt;strong&gt;NAIROBI&lt;/strong&gt;, all treated as separate values.&lt;/p&gt;

&lt;p&gt;If you build visuals on top of data like that, the charts might look impressive. The insights won’t be, and bad insights lead to bad decisions.&lt;/p&gt;

&lt;p&gt;That’s why analysts start with &lt;strong&gt;Power Query&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Power Query Editor&lt;/strong&gt; is where the unglamorous work happens. Removing duplicates, fixing data types, cleaning text, replacing errors, standardizing values. It’s not exciting work, but it’s essential. This step ensures that whatever comes next is built on data that is actually reliable.&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.amazonaws.com%2Fuploads%2Farticles%2Foji426jhuvezneplhm9u.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.amazonaws.com%2Fuploads%2Farticles%2Foji426jhuvezneplhm9u.png" alt="Features of Power Query" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can learn more about data cleaning in Power BI &lt;a href="https://www.credosystemz.com/tutorial/power-bi/data-cleaning-in-power-bi/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Data Modelling: Why structure matters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once the data is clean, the next question we should ask ourselves isn’t, &lt;em&gt;“Which chart should I use?”&lt;/em&gt; It’s:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“How should this data be structured?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where many beginners struggle, especially if they’re used to flat Excel tables. Power BI doesn’t really work with spreadsheets. It works with &lt;strong&gt;models&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of cramming everything into one giant table, analysts separate data into two main types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fact tables&lt;/strong&gt; — the numbers: sales, revenue, quantities
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension tables&lt;/strong&gt; — the context: dates, products, locations, customers
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup reflects how a business actually operates. Sales happen on a date, at a store, for a product, by a customer. Modelling data this way lets Power BI filter and calculate correctly without guessing.&lt;/p&gt;

&lt;p&gt;Relationships within the data then connect these tables. When they’re set up properly, selecting a county, a date, or a product automatically filters the right records in the background.&lt;/p&gt;

&lt;p&gt;When relationships are missing or wrong, everything starts to feel off. Totals stop making sense. Slicers don’t behave. Numbers refuse to change when they should.&lt;/p&gt;

&lt;p&gt;And most of the time, that’s not a DAX problem. It’s a &lt;strong&gt;modelling&lt;/strong&gt; one.&lt;/p&gt;

&lt;p&gt;You can learn more about &lt;a href="https://www.microsoft.com/en-us/power-platform/products/power-bi/topics/data-modeling/what-is-data-modeling" rel="noopener noreferrer"&gt;data modelling&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/power-bi/guidance/star-schema" rel="noopener noreferrer"&gt;schemas&lt;/a&gt; in Power BI on Microsoft.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;DAX: where business questions turn into logic&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Raw data doesn’t usually answer business questions by itself. You won’t find managers asking questions like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“What’s in column F?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They ask things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much revenue did we make?
&lt;/li&gt;
&lt;li&gt;Are we actually profitable?
&lt;/li&gt;
&lt;li&gt;Is performance improving or getting worse?
&lt;/li&gt;
&lt;li&gt;Which areas are falling behind?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where &lt;strong&gt;DAX&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;DAX lets analysts define business logic once and reuse it everywhere. Instead of hardcoding numbers into visuals, analysts create measures like &lt;strong&gt;total revenue&lt;/strong&gt;, &lt;strong&gt;profit margin&lt;/strong&gt;, &lt;strong&gt;averages&lt;/strong&gt;, and &lt;strong&gt;trends&lt;/strong&gt;. These measures automatically respond to filters and slicers.&lt;/p&gt;

&lt;p&gt;The real strength of DAX isn’t memorizing functions. It’s understanding that once a rule is defined, it behaves consistently across charts, tables, and dashboards hence providing a reliable output.&lt;/p&gt;

&lt;p&gt;You can learn more about DAX functions &lt;a href="https://www.geeksforgeeks.org/power-bi/power-bi-functions/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Choosing Visuals&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Charts often get treated like decoration. Pick something colorful, add labels, move on.&lt;/p&gt;

&lt;p&gt;That’s not how analysts think about visuals.&lt;/p&gt;

&lt;p&gt;Every visual should answer a question. Are we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comparing categories? &lt;strong&gt;Bar or column charts&lt;/strong&gt; do that well.
&lt;/li&gt;
&lt;li&gt;Looking at trends over time? &lt;strong&gt;Line charts&lt;/strong&gt; make patterns obvious.
&lt;/li&gt;
&lt;li&gt;Showing proportions? &lt;strong&gt;Pie charts or treemaps&lt;/strong&gt; &lt;em&gt;(note: but only when categories are few).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Tracking performance? &lt;strong&gt;KPI cards&lt;/strong&gt; work.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the wrong chart can quietly push people toward the wrong conclusion. A cluttered dashboard doesn’t feel confusing because one isn’t smart. It feels confusing because the analyst didn’t make clear choices.&lt;/p&gt;

&lt;p&gt;You don’t use every visual available. You choose the ones that make the message easier to understand.&lt;/p&gt;

&lt;p&gt;You can learn more about choosing the right visuals &lt;a href="https://learn.microsoft.com/en-us/power-bi/visuals/power-bi-visualizations-overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Dashboards aren’t reports&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Dashboards are different form reports in that: they’re built for &lt;strong&gt;decisions&lt;/strong&gt;, not deep exploration.&lt;/p&gt;

&lt;p&gt;A good dashboard fits on a single screen. Additionally, it highlights the most important numbers first, shows key trends and makes it obvious where things are going well and where they aren’t.&lt;/p&gt;

&lt;p&gt;You should be able to glance at it and immediately know whether attention is needed or not.&lt;/p&gt;

&lt;p&gt;The charts you created can now be used in your dashboard for quick decision drawing purposes. Most importantly, it answers a small number of critical questions.&lt;/p&gt;

&lt;p&gt;So, if users have to scroll endlessly or guess what a visual means, the dashboard has already failed, no matter how accurate the data behind it is.&lt;/p&gt;

&lt;p&gt;You can learn more about dashboards and reports &lt;a href="https://learn.microsoft.com/en-us/power-bi/create-reports/service-dashboards" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Turn Insights into Action&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We did not do all these just to produce nice-looking models and dashboards, no.&lt;/p&gt;

&lt;p&gt;Insights have to lead somewhere.&lt;/p&gt;

&lt;p&gt;A well-designed Power BI dashboard and report help decision-makers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spot underperforming areas early
&lt;/li&gt;
&lt;li&gt;catch trends before they become serious problems
&lt;/li&gt;
&lt;li&gt;use resources more efficiently
&lt;/li&gt;
&lt;li&gt;track progress against targets
&lt;/li&gt;
&lt;li&gt;ask better follow-up questions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, your role as the analyst shifts. You’re no longer just building charts. You’re deciding what deserves attention. You’re making risks hard to ignore. You’re helping leaders act with information instead of reacting under pressure.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;How It All Comes Together&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The Power BI architecture reflects this exact workflow.&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.amazonaws.com%2Fuploads%2Farticles%2Fefvicudcm8x8nqvosuse.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fefvicudcm8x8nqvosuse.webp" alt="Power BI architecture" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Data first comes from different sources such as Excel files, databases, and text files. Power Query handles the extract, transform, and load (ETL) process, cleaning and shaping the data. The cleaned data is stored in a structured model where relationships and calculations using DAX are applied. From there, visuals and dashboards are built and shared with business users.&lt;/p&gt;

&lt;p&gt;Each step depends on the one before it.&lt;/p&gt;

&lt;p&gt;If the data is messy, the model breaks.&lt;br&gt;&lt;br&gt;
If the model is weak, DAX results are unreliable.&lt;br&gt;&lt;br&gt;
If visuals are poorly chosen, decisions suffer.&lt;/p&gt;

&lt;p&gt;But understanding this end-to-end process makes it clear that Power BI isn’t just a reporting tool. Used properly, it becomes a powerful way to turn &lt;strong&gt;data into insight&lt;/strong&gt; and &lt;strong&gt;insight into action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I highly recommend for anyone looking to deepen their understanding, the &lt;a href="https://learn.microsoft.com/en-us/power-bi/" rel="noopener noreferrer"&gt;Microsoft Power BI documentation&lt;/a&gt; is an excellent place to start.  &lt;/p&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>datascience</category>
      <category>microsoft</category>
    </item>
    <item>
      <title>Schemas and Data Modelling in Power BI</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Mon, 02 Feb 2026 19:08:38 +0000</pubDate>
      <link>https://forem.com/sharon_m/schemas-and-data-modelling-in-power-bi-4jba</link>
      <guid>https://forem.com/sharon_m/schemas-and-data-modelling-in-power-bi-4jba</guid>
      <description>&lt;p&gt;When most people hear &lt;strong&gt;Power BI&lt;/strong&gt;, they immediately think of dashboards, &lt;em&gt;colorful charts, large numeric cards, clickable slicers, and polished visuals&lt;/em&gt;. These elements are what users interact with, so it is natural to assume they are what makes a report “good.”&lt;/p&gt;

&lt;p&gt;In reality, however, dashboards are only the &lt;strong&gt;final layer&lt;/strong&gt;. Long before any visual appears on the screen, critical decisions have already been made. These decisions determine whether a report is &lt;strong&gt;fast or slow&lt;/strong&gt;, &lt;strong&gt;reliable or misleading&lt;/strong&gt;, &lt;strong&gt;intuitive or frustrating&lt;/strong&gt;. That earlier and often invisible stage is &lt;strong&gt;data modelling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data modelling in Power BI&lt;/strong&gt; is the process of organizing data so that Power BI understands what the data represents and how different pieces of information relate to one another. It involves deciding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which tables are needed?
&lt;/li&gt;
&lt;li&gt;what each table should contain?
&lt;/li&gt;
&lt;li&gt;how those tables should be connected?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When this structure is well designed, Power BI feels &lt;strong&gt;logical and predictable&lt;/strong&gt;. When it is not, even simple questions can return &lt;strong&gt;confusing or incorrect results&lt;/strong&gt;. In other words, the quality of a Power BI report is decided &lt;em&gt;before&lt;/em&gt; the first chart is ever created.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;What “Schema” Means in Power BI&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In Power BI, a &lt;strong&gt;schema&lt;/strong&gt; refers to the overall structure of the data model. This is not a theoretical concept, it is the &lt;strong&gt;actual layout you see in Model view&lt;/strong&gt;, including the tables and the relationships between them.&lt;/p&gt;

&lt;p&gt;A schema answers very practical questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What tables exist in the model?
&lt;/li&gt;
&lt;li&gt;Which tables store measurements, and which store descriptions?
&lt;/li&gt;
&lt;li&gt;When a user clicks a slicer, how does Power BI know which data to include?
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Power BI does not “reason” about data in a human way. Instead, it &lt;strong&gt;follows the paths you define&lt;/strong&gt;. The schema determines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how filters move from one table to another,
&lt;/li&gt;
&lt;li&gt;how totals and averages are calculated,
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and how fast visuals respond when users interact with the report.&lt;br&gt;&lt;br&gt;
Two schema patterns appear most frequently in Power BI models:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Star schema&lt;/strong&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Snowflake schema&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7yw57mthecm5zez64tv.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.amazonaws.com%2Fuploads%2Farticles%2Fb7yw57mthecm5zez64tv.jpg" alt="Star Schema Vs Snowflake Schema" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Understanding the difference between these two explains why some Power BI models feel &lt;strong&gt;simple and trustworthy&lt;/strong&gt;, while others feel &lt;strong&gt;fragile and unpredictable&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Fact Tables and Dimension Tables: Understanding the Roles of Tables&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Most Power BI models are built using &lt;strong&gt;two types of tables&lt;/strong&gt;. Understanding what each one does is the foundation of data modelling.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Fact Tables: Recording What Happened&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;fact table&lt;/strong&gt; records events. Each row represents something that actually occurred.&lt;/p&gt;

&lt;p&gt;In a dataset such as &lt;em&gt;Kenya crops data&lt;/em&gt;, a single row in the fact table might represent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a specific crop,
&lt;/li&gt;
&lt;li&gt;grown in a specific county,
&lt;/li&gt;
&lt;li&gt;during a specific year or season,
&lt;/li&gt;
&lt;li&gt;with a measurable outcome such as &lt;strong&gt;yield in kilograms&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because these events are recorded repeatedly over time, fact tables typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grow very large,
&lt;/li&gt;
&lt;li&gt;repeat the same crops or counties many times,
&lt;/li&gt;
&lt;li&gt;focus on numeric values that can be &lt;strong&gt;summed, averaged, or counted&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fact table does &lt;strong&gt;not&lt;/strong&gt; explain what a crop is or where a county is located. It simply records that something happened.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Dimension Tables: Giving Meaning to the Events&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dimension tables&lt;/strong&gt; exist to describe and contextualize the facts. Instead of repeating names and descriptions in every row of the fact table, that information is stored &lt;strong&gt;once&lt;/strong&gt; in separate tables, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;Crop&lt;/strong&gt; table that stores crop names and types,
&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;County&lt;/strong&gt; table containing county names,
&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Date&lt;/strong&gt; table containing years or seasons.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dimension tables typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change slowly compared to fact tables,
&lt;/li&gt;
&lt;li&gt;contain descriptive rather than numerical data,
&lt;/li&gt;
&lt;li&gt;are used to filter, group, and label results in reports.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you select a county or crop in a slicer, Power BI relies on the &lt;strong&gt;dimension table&lt;/strong&gt; to determine which rows in the fact table should be included. This separation is what makes analysis &lt;strong&gt;both efficient and accurate&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;The Star Schema: A Structure That Matches How Power BI Thinks&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;star schema&lt;/strong&gt; is the most effective and widely recommended structure for Power BI models.&lt;/p&gt;

&lt;p&gt;In a star schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one &lt;strong&gt;fact table&lt;/strong&gt; sits at the center (for example, crop yield records),
&lt;/li&gt;
&lt;li&gt;each &lt;strong&gt;dimension table&lt;/strong&gt; connects directly to that fact table (crop, county, date), &lt;/li&gt;
&lt;li&gt;dimension tables do &lt;strong&gt;not&lt;/strong&gt; connect to each other.
&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.amazonaws.com%2Fuploads%2Farticles%2Frwloeunodgaz859c9kmc.webp" alt="Star Schema" width="512" height="384"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure aligns closely with how Power BI processes filters.&lt;/p&gt;

&lt;p&gt;When you selects a county in a slicer, Power BI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Looks at the &lt;strong&gt;County&lt;/strong&gt; table.
&lt;/li&gt;
&lt;li&gt;Identifies the selected county’s unique key.
&lt;/li&gt;
&lt;li&gt;Follows the relationship directly to the fact table.
&lt;/li&gt;
&lt;li&gt;Keeps only the matching rows.
&lt;/li&gt;
&lt;li&gt;Performs calculations using those rows.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because each dimension connects straight to the fact table: filters move directly to the data being analyzed and Power BI does not need to pass through intermediary tables which leads to calculations behaving consistently.  &lt;/p&gt;

&lt;p&gt;This makes much of the analytical &lt;strong&gt;logic to be handled by the structure itself&lt;/strong&gt;, reducing the need for complex formulas later.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why the Star Schema Performs Better in Power BI&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Power BI stores data in &lt;strong&gt;columns&lt;/strong&gt; and is optimized for fast aggregation. It performs best when relationships are &lt;strong&gt;simple and unambiguous&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a star schema, you will observe that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Power BI follows one clear relationship path,
&lt;/li&gt;
&lt;li&gt;fewer joins are required to answer questions,
&lt;/li&gt;
&lt;li&gt;the model is easier to understand and debug.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, reports load faster, slicers respond more smoothly and DAX formulas tend to be shorter and easier to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;The Snowflake Schema: A bit more complex&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;snowflake schema&lt;/strong&gt; starts with the same idea as a star schema but splits descriptive information across multiple related tables.&lt;/p&gt;

&lt;p&gt;For example, instead of storing all location details in a single &lt;strong&gt;County&lt;/strong&gt; table, the data might be organized as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;County&lt;/strong&gt; table stores county information, &lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Region&lt;/strong&gt; table stores regional information,&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;Country&lt;/strong&gt; table stores country information.
When a user selects a country, Power BI must follow a longer path before reaching the data. For Example,
Start at the Country table. Then, Move to the Region table. Then move to the County table. Finally reach the &lt;strong&gt;fact table&lt;/strong&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndlq89emlj6j2id6uoue.webp" 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.amazonaws.com%2Fuploads%2Farticles%2Fndlq89emlj6j2id6uoue.webp" alt="Snowflake Schema" width="512" height="384"&gt;&lt;/a&gt;&lt;br&gt;
Each additional step increases processing work for Power BI and increases the chance of errors if any relationship is incorrect. &lt;/p&gt;

&lt;p&gt;While snowflake schemas reduce duplicated data, they create challenges in Power BI because filters must travel through multiple tables, more relationships must be managed. Hence, it becomes harder to predict how calculations will behave.&lt;/p&gt;

&lt;p&gt;For this reason, snowflake schemas are common in source systems but are often reshaped into star schemas for reporting.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Relationships: How Tables Actually Work Together&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Relationships define how tables communicate and how filters flow.&lt;/p&gt;

&lt;p&gt;When you select a county, crop, or year in a slicer, Power BI does not search the fact table directly. It looks at the dimension table, then identifies the matching key, then it follows the relationship to the fact table and filters the fact rows accordingly. &lt;/p&gt;

&lt;p&gt;In a well-designed model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each dimension table contains &lt;strong&gt;unique values&lt;/strong&gt; (each crop or county appears once),
&lt;/li&gt;
&lt;li&gt;fact tables contain &lt;strong&gt;many related records&lt;/strong&gt; linked to those values,
&lt;/li&gt;
&lt;li&gt;filters flow from &lt;strong&gt;dimension tables to the fact table.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mirrors real-world logic: one county can have many crop records, and one crop can appear across many years.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Cardinality: Understanding “One” and “Many”&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cardinality&lt;/strong&gt; describes how many rows in one table relate to rows in another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-to-Many&lt;/strong&gt; means one row in a dimension table relates to many rows in the fact table.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-to-One&lt;/strong&gt; means one row matches exactly one row in another table. &lt;em&gt;(rare in reporting)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many-to-Many&lt;/strong&gt; means multiple rows relate to multiple rows &lt;em&gt;(can cause duplicated totals if not handled carefully)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Incorrect cardinality may still produce a result but those results may &lt;strong&gt;not represent reality&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why Good Data Modelling Matters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Data modelling affects every Power BI report in &lt;strong&gt;three key ways&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Simple structures reduce processing work, resulting in faster visuals and smoother interaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Accuracy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Correct relationships ensure each fact is counted once, preventing inflated totals and misleading averages.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simplicity&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Clear models make reports easier to build, understand, and maintain. Complex DAX is often a sign of a model that needs improvement.&lt;/p&gt;

&lt;p&gt;Effective models typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate measurements from descriptions,
&lt;/li&gt;
&lt;li&gt;use star schemas where possible,
&lt;/li&gt;
&lt;li&gt;define relationships clearly,
&lt;/li&gt;
&lt;li&gt;rely on the model to handle logic instead of forcing visuals to compensate.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When this foundation is solid, Power BI becomes easier to use and easier to trust the results. Schemas and data modelling directly determine whether Power BI produces &lt;strong&gt;reliable insight&lt;/strong&gt; or &lt;strong&gt;confusing results&lt;/strong&gt;. By understanding fact and dimension tables, choosing appropriate schemas, and defining relationships carefully, analysts create reports that are &lt;strong&gt;fast, accurate, and understandable&lt;/strong&gt;.For more information, feel free to visit &lt;a href="https://learn.microsoft.com/en-us/power-bi/guidance/" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt; on more information about PowerBI. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also Feel free to leave a comment sharing how you approach data modelling in your own Power BI projects. Discussion and different perspectives are always welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>powerbi</category>
      <category>windows</category>
      <category>beginners</category>
      <category>schema</category>
    </item>
    <item>
      <title>Understanding Git Version Control, Push &amp; Pull (A Beginner’s Guide)</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Sun, 18 Jan 2026 14:24:30 +0000</pubDate>
      <link>https://forem.com/sharon_m/understanding-git-version-control-track-changes-commit-push-pull-beginner-guide-48jp</link>
      <guid>https://forem.com/sharon_m/understanding-git-version-control-track-changes-commit-push-pull-beginner-guide-48jp</guid>
      <description>&lt;h2&gt;
  
  
  What is covered in this part
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What version control, push, and pull mean&lt;/li&gt;
&lt;li&gt;Version control (Git)&lt;/li&gt;
&lt;li&gt;Push&lt;/li&gt;
&lt;li&gt;Pull&lt;/li&gt;
&lt;li&gt;The main stages of Git&lt;/li&gt;
&lt;li&gt;A Practical Example(Part 4)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Do Version Control, Push, and Pull Mean?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Version control (Git)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Version control&lt;/strong&gt; is a system that records changes to your files over time.&lt;br&gt;&lt;br&gt;
In simple terms, it’s like having a &lt;em&gt;save history&lt;/em&gt; for your project. With Git, you create checkpoints called &lt;strong&gt;commits&lt;/strong&gt;. Each commit becomes part of your project history, allowing you to see &lt;em&gt;what changed&lt;/em&gt;, know &lt;em&gt;when it changed&lt;/em&gt; and &lt;em&gt;go back to an earlier version&lt;/em&gt; if needed. &lt;br&gt;
So if something breaks or you realize you preferred an older version, you can easily go back to it instead of starting over.&lt;/p&gt;

&lt;p&gt;Here are some commands to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git status&lt;/code&gt; — Shows which files have changed and whether those changes have already been committed. Only committed changes can be pushed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git add .&lt;/code&gt; — Tells Git to include all changed files in the next commit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit -m "..."&lt;/code&gt; — Creates the checkpoint (commit) with a message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git log&lt;/code&gt; — Displays a list of all commits. It displays who made the change, when, and the commit message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git show&lt;/code&gt; or &lt;code&gt;git show &amp;lt;commit-id&amp;gt;&lt;/code&gt; — Shows the actual lines that were added or removed. It is useful when you want to understand what changed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git restore filename&lt;/code&gt;(&lt;em&gt;e.g. &lt;code&gt;notes.txt&lt;/code&gt;&lt;/em&gt;) — Reverts the file back to the last saved commit. It is useful if you made a mistake and want to undo it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout &amp;lt;commit-id&amp;gt;&lt;/code&gt;(&lt;em&gt;e.g. &lt;code&gt;a1b2c3d&lt;/code&gt;&lt;/em&gt;) — Lets you look at an older version of the project without deleting your work. It is useful when learning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout main&lt;/code&gt; — Lets you return to the latest version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git restore filename&lt;/code&gt; — Lets you undo changes to a file (i.e. go back to last saved version).&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Push
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Push&lt;/strong&gt; means sending your saved commits from your computer (&lt;strong&gt;local repository&lt;/strong&gt;) to GitHub (&lt;strong&gt;remote repository&lt;/strong&gt;). When you push:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your work and changes are uploaded to GitHub.&lt;/li&gt;
&lt;li&gt;They are backed up online.&lt;/li&gt;
&lt;li&gt;They become available for sharing or collaboration.
If you don’t push, your work remains only on your computer and isn’t visible or backed up on GitHub.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some commands to keep in mind:&lt;/p&gt;

&lt;p&gt;If it is your &lt;strong&gt;first time&lt;/strong&gt; pushing a repository, you may need: &lt;br&gt;
&lt;code&gt;git push -u origin main&lt;/code&gt;(&lt;em&gt;Replace main with master if your branch is named master&lt;/em&gt;). &lt;br&gt;
Use &lt;code&gt;git branch&lt;/code&gt;(or &lt;code&gt;git status&lt;/code&gt;) if you want to check which branch you are currently working on. &lt;br&gt;
If you want to switch to &lt;em&gt;main&lt;/em&gt; branch, use:&lt;br&gt;
&lt;code&gt;git branch -M main&lt;/code&gt; command. &lt;/p&gt;

&lt;p&gt;After your first push, you can just use &lt;code&gt;git push&lt;/code&gt;.&lt;br&gt;
You should also note that before pushing, changes must be saved as commits using &lt;code&gt;git add&lt;/code&gt; and &lt;code&gt;git commit&lt;/code&gt;. When you push, your local commits are uploaded to GitHub so they are stored online, backed up, and available for others to see.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pull
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pull&lt;/strong&gt; means downloading the latest commits from GitHub (&lt;strong&gt;remote repository&lt;/strong&gt;) to your computer (&lt;strong&gt;local repository&lt;/strong&gt;).&lt;br&gt;
This is important when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You work on multiple computers and need the same files everywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You collaborate with others and want their latest changes. For example, group or teams projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updates were made directly on GitHub and you need them on your local machine.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some commands to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git pull&lt;/code&gt;- Allows you to download the latest commits from GitHub and updates your local files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git fetch&lt;/code&gt;- Checks for updates without installing them.
This command lets you download updates from GitHub without changing your local files. It simply checks for new commits and brings that information into your local repository so you can see what has changed.
Unlike &lt;code&gt;git pull&lt;/code&gt;, fetching does not automatically update your working files. This makes it useful when you want to review updates first or confirm that changes exist before applying them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git merge&lt;/code&gt;- Lets you now combine the updates fetched(&lt;code&gt;git fetch&lt;/code&gt;) with your files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are other command prompts, such as &lt;code&gt;git pull --rebase&lt;/code&gt;, &lt;code&gt;git restore&lt;/code&gt;, &lt;code&gt;git reset --hard&lt;/code&gt;, which will be covered later in the series.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Happens at Each Stage
&lt;/h2&gt;

&lt;p&gt;When you are working with Git, your files move through a few simple stages. Once you understand these stages, many Git commands start to make much more sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working Directory - Where changes live.
&lt;/h3&gt;

&lt;p&gt;This is your project folder on your computer. Any time you open a file and make changes, those changes happen here first.&lt;br&gt;
At this point, Git can detect that something has changed, but nothing has been saved yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staging Area - How changes move.
&lt;/h3&gt;

&lt;p&gt;When you run &lt;code&gt;git add&lt;/code&gt;, you move specific &lt;strong&gt;changes&lt;/strong&gt; into the &lt;strong&gt;staging area&lt;/strong&gt;. It states: &lt;em&gt;“These are the changes I want to include in my next save.”&lt;/em&gt;&lt;br&gt;
Do not panic, you’re still in control it is only that the changes you add get staged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Repository (Commits) - How Git stores history
&lt;/h3&gt;

&lt;p&gt;When you run &lt;code&gt;git commit&lt;/code&gt;, Git takes everything in the staging area and saves it as a permanent checkpoint in your &lt;strong&gt;local repository&lt;/strong&gt; (aka. your computer).&lt;br&gt;
At this point, your work is safely saved on your computer, but it hasn’t been sent online yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remote Repository (GitHub) - How local and remote copies relate
&lt;/h3&gt;

&lt;p&gt;When you run &lt;code&gt;git push&lt;/code&gt;, Git sends your local commits to &lt;strong&gt;GitHub&lt;/strong&gt;.&lt;br&gt;
You can now breathe because this is when your work is backed up online and becomes visible to others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pulling Updates from GitHub
&lt;/h3&gt;

&lt;p&gt;When you run &lt;code&gt;git pull&lt;/code&gt;, Git downloads the latest commits from GitHub and applies them to your local project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Git Workflow Cycle
&lt;/h3&gt;

&lt;p&gt;After pulling, you’re back in the &lt;strong&gt;working directory&lt;/strong&gt;, ready to continue editing files. From here, the same cycle repeats:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;edit files → stage → commit → push → pull → repeat&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you understand these stages, Git commands start to make a lot more sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Example: Tracking Changes and Pushing to GitHub and Pulling from GitHub (Pending)
&lt;/h2&gt;

&lt;p&gt;Now that we understand what version control, push, and pull mean, let’s walk through a simple example to see how these commands work together in practice in &lt;strong&gt;Part 4&lt;/strong&gt; of the series, where we will also look into additional Git commands.&lt;/p&gt;




&lt;p&gt;As always, feel free to comment below. I welcome feedback and discussion in the comments.&lt;/p&gt;

</description>
      <category>gitbash</category>
      <category>github</category>
      <category>beginners</category>
      <category>windows</category>
    </item>
    <item>
      <title>Connecting Git Bash to GitHub (SSH Key): A Beginner’s Guide</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Sun, 18 Jan 2026 08:29:11 +0000</pubDate>
      <link>https://forem.com/sharon_m/connecting-git-bash-to-github-a-beginners-guide-4lb0</link>
      <guid>https://forem.com/sharon_m/connecting-git-bash-to-github-a-beginners-guide-4lb0</guid>
      <description>&lt;h2&gt;
  
  
  What is covered in this part
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why connecting Git Bash to GitHub matters&lt;/li&gt;
&lt;li&gt;How to connect Git Bash to GitHub using an SSH Key&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why connecting Git Bash to GitHub is important
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;&lt;a href="https://dev.to/mwangi_sharon/installing-git-bash-on-windows-a-beginners-guide-2k9p"&gt;Part 1&lt;/a&gt;&lt;/strong&gt;, we covered the basics of Git Bash and GitHub. Now, we’ll take the next step by connecting Git Bash to GitHub.&lt;/p&gt;

&lt;p&gt;So why is connecting Git Bash to GitHub important? Connecting &lt;strong&gt;Git Bash&lt;/strong&gt; to &lt;strong&gt;GitHub&lt;/strong&gt; allows you to move your work from your computer to GitHub and back again. Without this connection, Git can only track changes on your own computer, which means your work stays &lt;strong&gt;local&lt;/strong&gt; and can’t be shared or backed up &lt;strong&gt;online&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Once Git Bash is connected to GitHub, you can &lt;strong&gt;push&lt;/strong&gt; your saved changes to GitHub, where your project is stored safely &lt;strong&gt;online&lt;/strong&gt;. &lt;br&gt;
You can also &lt;strong&gt;pull&lt;/strong&gt; changes from GitHub, which is important when working on projects across multiple devices or with other people.&lt;/p&gt;

&lt;p&gt;Yes, you read that right. Multiple people can work on the same project without overwriting each other’s work, because Git keeps a clear history of changes and GitHub shows who made each update and when.&lt;/p&gt;


&lt;h2&gt;
  
  
  Connecting Git Bash to GitHub
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Create a GitHub account
&lt;/h3&gt;

&lt;p&gt;You can create your GitHub account &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Configure your Git identity (name and email)
&lt;/h3&gt;

&lt;p&gt;Git needs to know who you are so that it can label your commits correctly.&lt;br&gt;
Now open Git Bash and type in your terminal:&lt;br&gt;
To set your name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"yourname"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To set your email&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"youremail@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To display all Git settings that are currently configured on your computer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be able to see your &lt;strong&gt;username&lt;/strong&gt; and &lt;strong&gt;user-email&lt;/strong&gt; displayed in the output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Generate a New SSH Key
&lt;/h3&gt;

&lt;p&gt;You are probably wondering what is an SSH Key?&lt;/p&gt;

&lt;p&gt;Simply, an SSH key is a secure digital identity that allows Git Bash to connect to GitHub without typing your password every time.&lt;br&gt;
And this is how we generate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"youremail@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Press Enter to accept the default path/ file location.&lt;/li&gt;
&lt;li&gt;Press Enter again to skip passphrase (or add one, optional).
Example output:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your public key has been saved in /c/Users/your-username/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also check if you have an already existing key using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; ~/.ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id_ed25519
id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Start the SSH Agent
&lt;/h3&gt;

&lt;p&gt;This will start a background helper that securely holds your key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh-agent &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent pid 293
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Add Your Key to the Agent
&lt;/h3&gt;

&lt;p&gt;Essentially, this tells the SSH agent which private key it should use to authenticate you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-add ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identity added: /c/Users/your-username/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Copy Your Public Key
&lt;/h3&gt;

&lt;p&gt;At this point, you need to copy your &lt;strong&gt;public SSH key&lt;/strong&gt; and add it to your GitHub account.&lt;/p&gt;




&lt;h4&gt;
  
  
  Method 1: Copy the key using Git Bash
&lt;/h4&gt;

&lt;p&gt;You can now use this command to display your public key directly in the terminal. So, you can copy it and add it to GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-ed25519 [YOUR_PUBLIC_KEY_HERE]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Method 2: Copy the key manually from the file (alternative)
&lt;/h4&gt;

&lt;p&gt;You can also access the same public key manually using the file path that was generated in &lt;strong&gt;Step 3&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;.ssh&lt;/strong&gt; folder on your computer (for example:
&lt;code&gt;C:\Users\your-username\.ssh&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;.ssh&lt;/strong&gt; folder by double-clicking it.&lt;/li&gt;
&lt;li&gt;Inside the folder, locate the file named &lt;strong&gt;id_ed25519.pub&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Open this file using any &lt;em&gt;text editor&lt;/em&gt;, such as &lt;strong&gt;Notepad&lt;/strong&gt; or &lt;strong&gt;Visual Studio Code&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You will see a line of text that starts with &lt;code&gt;ssh-ed25519&lt;/code&gt; (&lt;em&gt;refer to the example output above&lt;/em&gt;). This is your &lt;strong&gt;public key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the entire line.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After copying this public key, navigate to your GitHub account. Then paste it into &lt;strong&gt;GitHub&lt;/strong&gt; → &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;SSH and GPG keys&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcoz28tw6vj384z88hwy8.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.amazonaws.com%2Fuploads%2Farticles%2Fcoz28tw6vj384z88hwy8.png" alt="Settings" width="282" height="469"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77dgnyztfqr8qvgburov.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.amazonaws.com%2Fuploads%2Farticles%2F77dgnyztfqr8qvgburov.png" alt="Where to paste your key" width="800" height="186"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 7: Test the connection
&lt;/h3&gt;

&lt;p&gt;This code helps you to confirm if Git Bash is now connected to GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-T&lt;/span&gt; git@github.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hi your-username! You've successfully authenticated...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👌Your GitHub and Git Bash are now connected.&lt;br&gt;
&lt;strong&gt;Quick Tip&lt;/strong&gt;: You only need to set up SSH once per computer and after this, GitHub authentication becomes seamless.&lt;/p&gt;

&lt;p&gt;Feel free to engage with this post in the comment section. You can also check out &lt;strong&gt;&lt;a href="https://dev.to/mwangi_sharon/understanding-git-version-control-track-changes-commit-push-pull-beginner-guide-48jp"&gt;Part 3&lt;/a&gt;&lt;/strong&gt; of the series. &lt;/p&gt;

</description>
      <category>gitbash</category>
      <category>github</category>
      <category>beginners</category>
      <category>ssh</category>
    </item>
    <item>
      <title>How to Install Git Bash on Windows: Step-by-Step Guide for Beginners</title>
      <dc:creator>Sharon M.</dc:creator>
      <pubDate>Sun, 18 Jan 2026 06:35:24 +0000</pubDate>
      <link>https://forem.com/sharon_m/installing-git-bash-on-windows-a-beginners-guide-2k9p</link>
      <guid>https://forem.com/sharon_m/installing-git-bash-on-windows-a-beginners-guide-2k9p</guid>
      <description>&lt;h2&gt;
  
  
  What You’ll Learn in This Part
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What Is Git Bash?&lt;/li&gt;
&lt;li&gt;How to Download and Install Git Bash on Windows&lt;/li&gt;
&lt;li&gt;How to verify your Git Bash installation&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Here is a little explanation to get us started:&lt;br&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; and &lt;strong&gt;Git Bash&lt;/strong&gt; are tools used to create, manage, and collaborate on digital projects—especially projects that involve &lt;em&gt;code&lt;/em&gt;, &lt;em&gt;data&lt;/em&gt;, or &lt;em&gt;files that change over time&lt;/em&gt;. &lt;br&gt;
These tools are among many others that are widely used by software developers, data analysts, data scientists, programmers, and even non-programmers. These tools provide a reliable way to track work, avoid losing progress, and collaborate with others.&lt;/p&gt;


&lt;h2&gt;
  
  
  What is Git Bash?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Git Bash&lt;/strong&gt; is a tool you use to communicate with &lt;strong&gt;Git&lt;/strong&gt;, the version control system that tracks changes in your files. It provides a simple terminal where you can: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate folders&lt;/li&gt;
&lt;li&gt;Create files&lt;/li&gt;
&lt;li&gt;Record changes 
using clear, text-based commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;, on the other hand, is an online platform where those recorded changes are stored, shared, and displayed. When you use Git Bash, you save your work &lt;em&gt;locally&lt;/em&gt; as commits and then send them to GitHub, where your projects are safely stored &lt;em&gt;online&lt;/em&gt; and can be accessed from anywhere.&lt;/p&gt;


&lt;h2&gt;
  
  
  Steps to download and set up Git Bash on Windows
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Git on your PC. You can download it &lt;a href="https://git-scm.com/install/windows" rel="noopener noreferrer"&gt;here&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fcnrchv8ot9vu9jdrw6wq.png" alt="Git for Windows download page image" width="800" height="548"&gt;
&lt;/li&gt;
&lt;li&gt;Once Git Bash is downloaded, navigate to your &lt;strong&gt;Downloads&lt;/strong&gt; folder on your PC (or click the download icon at the top of your browser, usually shown as a downward arrow). Locate and double-click the downloaded &lt;strong&gt;.exe&lt;/strong&gt; file to run it.
When you get a prompt saying, “Do you want to allow this app to make changes to your device?” Click &lt;strong&gt;Yes&lt;/strong&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.amazonaws.com%2Fuploads%2Farticles%2F5qc10e2x996tykbhfion.png" alt="Downloads" width="555" height="134"&gt;
&lt;/li&gt;
&lt;li&gt;Use the default settings throughout the installation process.
Click &lt;strong&gt;Next&lt;/strong&gt; until you reach &lt;strong&gt;Install&lt;/strong&gt;, then click &lt;strong&gt;Install&lt;/strong&gt;. After the installation is complete, You may choose to Launch Git Bash, then click &lt;strong&gt;Finish&lt;/strong&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.amazonaws.com%2Fuploads%2Farticles%2Fnr6w3t7k31kea67wsjjp.png" alt="Setup Guide image" width="597" height="461"&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.amazonaws.com%2Fuploads%2Farticles%2Fk3ajjkt8l7yf5upika2i.png" alt=" " width="605" height="464"&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.amazonaws.com%2Fuploads%2Farticles%2Fkccnjkz80zzpmwth0atw.png" alt=" " width="608" height="468"&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.amazonaws.com%2Fuploads%2Farticles%2F4bb4zg5oyejr3cqovif3.png" alt=" " width="603" height="466"&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.amazonaws.com%2Fuploads%2Farticles%2Fo8rxc0ro6kne0fpthxg1.png" alt=" " width="604" height="465"&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.amazonaws.com%2Fuploads%2Farticles%2Fb9me91vwkcoppfl6s3mc.png" alt=" " width="602" height="470"&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.amazonaws.com%2Fuploads%2Farticles%2F460r4jy912rwh3zewuf0.png" alt=" " width="606" height="469"&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.amazonaws.com%2Fuploads%2Farticles%2Fqag2iaybz3cooyc3dvwo.png" alt=" " width="605" height="469"&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.amazonaws.com%2Fuploads%2Farticles%2Fiaf1ruadrtp2h74qlolb.png" alt=" " width="603" height="468"&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.amazonaws.com%2Fuploads%2Farticles%2Fq3z3dpen3tp9j3g2w311.png" alt=" " width="603" height="474"&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.amazonaws.com%2Fuploads%2Farticles%2Fvn1vbmxpskmlp74rs8x6.png" alt=" " width="602" height="466"&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.amazonaws.com%2Fuploads%2Farticles%2F0ynm6lypxsymce8yihn5.png" alt=" " width="603" height="466"&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.amazonaws.com%2Fuploads%2Farticles%2Fhy3yayg73y44aa7npwy9.png" alt=" " width="603" height="468"&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.amazonaws.com%2Fuploads%2Farticles%2Fzcaditty8ucvcwxpn5so.png" alt=" " width="600" height="462"&gt;
&lt;/li&gt;
&lt;li&gt;After installing Git on Windows, if Git Bash does not launch automatically, press the &lt;strong&gt;Windows key&lt;/strong&gt; on your keyboard, type &lt;strong&gt;Git Bash&lt;/strong&gt;, and click &lt;strong&gt;Git Bash&lt;/strong&gt; from the search results.
You should see a terminal window with:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;username@hostname MINGW64 /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to verify your installation
&lt;/h2&gt;

&lt;p&gt;In the Git Bash terminal, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Git is installed correctly, you’ll see output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git version 2.x.x.windows.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this guide was helpful. Let me know in the comments how you set up your Git Bash to suit your needs. Next, we’ll look at how to connect Git Bash to GitHub, so head over to &lt;strong&gt;&lt;a href="https://dev.to/mwangi_sharon/connecting-git-bash-to-github-a-beginners-guide-4lb0"&gt;Part 2&lt;/a&gt;&lt;/strong&gt; of the series.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
