<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Njuguna Gichia</title>
    <description>The latest articles on DEV Community by Njuguna Gichia (@9ichia).</description>
    <link>https://dev.to/9ichia</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4070940%2Fb1cb3d2c-ac47-4a09-813b-f0c007f3a777.png</url>
      <title>DEV Community: Njuguna Gichia</title>
      <link>https://dev.to/9ichia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/9ichia"/>
    <language>en</language>
    <item>
      <title>Introduction to SQL Window Functions, CTEs, Joins &amp; Subqueries</title>
      <dc:creator>Njuguna Gichia</dc:creator>
      <pubDate>Sun, 18 Oct 2026 13:26:07 +0000</pubDate>
      <link>https://dev.to/9ichia/introduction-to-sql-window-functions-ctes-joins-subqueries-2nm4</link>
      <guid>https://dev.to/9ichia/introduction-to-sql-window-functions-ctes-joins-subqueries-2nm4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to SQL
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SQL&lt;/strong&gt; (pronounced &lt;em&gt;"Sequel"&lt;/em&gt;) stands for &lt;strong&gt;Structured Query Language&lt;/strong&gt; and it is the standard programming language for managing, querying, and manipulating relational databases. SQL is declarative, meaning you tell the database what data you want, and then the database figures out the best way to get it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is SQL Used For in Data?
&lt;/h3&gt;

&lt;p&gt;In data analytics, engineering, and science, SQL is used to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Query and Retrieve Data:&lt;/strong&gt; Pull specific records out of millions of rows using filters, sorting, and conditional logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Aggregate and Summarize:&lt;/strong&gt; Calculate metrics like totals, averages, minimums, and maximums using functions like &lt;code&gt;SUM()&lt;/code&gt;, &lt;code&gt;AVG()&lt;/code&gt;, and &lt;code&gt;COUNT()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Join Disparate Datasets:&lt;/strong&gt; Connect related tables (such as matching doctors to their patient visits or departments to revenue targets) using &lt;code&gt;JOIN&lt;/code&gt; operations.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Transform and Clean:&lt;/strong&gt; Reshape raw data into clean, structured formats ready for reporting, machine learning, or dashboards.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Basic Anatomy of a SQL Query
&lt;/h3&gt;

&lt;p&gt;At its core, a standard SQL query follows a predictable logical structure. &lt;/p&gt;

&lt;p&gt;SQL&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;column_1&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;column_2&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_metric&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'value'&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;column_1&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_metric&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;SELECT&lt;/code&gt;&lt;/strong&gt;: Specifies the exact columns or calculated values you want to see in your final output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;FROM&lt;/code&gt;&lt;/strong&gt;: Identifies the table or tables where the data lives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;WHERE&lt;/code&gt;&lt;/strong&gt;: Filters the rows before any groupings happen based on specific criteria.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;GROUP BY&lt;/code&gt;&lt;/strong&gt;: Bundles rows together that share a common attribute so you can perform aggregate calculations on them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ORDER BY&lt;/code&gt;&lt;/strong&gt;: Sorts your final results (e.g., highest revenue to lowest).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Window Functions
&lt;/h3&gt;

&lt;p&gt;In SQL Window functions perform calculations across a set of table rows that are somehow related to the current row.&lt;/p&gt;

&lt;p&gt;Window functions also keep all individual rows intact while adding the calculated result as a new column in each row.&lt;/p&gt;

&lt;p&gt;A very important component of window functions is the &lt;code&gt;OVER()&lt;/code&gt; function that tells PostgreSQL how to group and sort the rows for a specific operation.&lt;/p&gt;

&lt;p&gt;We use two major tools &lt;code&gt;Partition by:&lt;/code&gt; and &lt;code&gt;Order by:&lt;/code&gt; inside the &lt;code&gt;Over()&lt;/code&gt; function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Partition by:&lt;/code&gt;&lt;br&gt;
Divides rows into groups similar to &lt;code&gt;GROUP BY&lt;/code&gt; without collapsing them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Order by:&lt;/code&gt;&lt;br&gt;
Sorts the rows within each partition.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Types of Window Functions
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Ranking Functions
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ROW_NUMBER()&lt;/code&gt;
Gives every row a unique sequential number even if the values are identical.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hospital's patient retention team wants to audit patient records and identify every patient's absolute &lt;strong&gt;most recent visit&lt;/strong&gt; to the clinic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;row_number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;partition&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_date&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;visit_recency_rank&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;visits&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;dim_patients&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_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;em&gt;TASK&lt;/em&gt;&lt;br&gt;
This query ranks patients visits chronologically, assigns &lt;code&gt;1&lt;/code&gt; to the most recent visit, &lt;code&gt;2&lt;/code&gt; to the second most recent (and so on) while pulling descriptive patient information from the patients dimension table, and joins the fact table (&lt;code&gt;visits&lt;/code&gt;) with the dimension table &lt;code&gt;patients&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;RANK()&lt;/code&gt;&lt;br&gt;
Gives identical values the same rank but leaves gaps in the numbering by skipping one value&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DENSE_RANK()&lt;/code&gt;&lt;br&gt;
Assigns sequential rank to rows within a partition without any gaps in the ranking number.&lt;br&gt;
The next rank is the immediate integer unlike &lt;code&gt;rank()&lt;/code&gt; where it skips a number after a tie.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The medical director is evaluating doctors based on their &lt;strong&gt;average patient satisfaction score&lt;/strong&gt;. Where there's a tie, we want to see how standard ranking handles ties versus dense ranking.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_satisfaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&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;satisfaction_rank&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DENSE_RANK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&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;satisfaction_dense_rank&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Dim_Doctors&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_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;doctor_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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;br&gt;
This query calculates the average patient satisfaction score for each doctor, and then ranks the doctors from highest satisfaction to lowest. The query rolls up all individual patient visits per doctor, computes an aggregate average and then uses window functions &lt;code&gt;RANK&lt;/code&gt; and &lt;code&gt;DENSE_RANK&lt;/code&gt; to establish a leaderboard of physician performance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DENSE_RANK()&lt;/code&gt;&lt;br&gt;
Assigns sequential rank to rows within a partition without any gaps in the ranking number.&lt;br&gt;
The next rank is the immediate integer unlike &lt;code&gt;rank()&lt;/code&gt; where it skips a number after a tie.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;NTILE(n)&lt;/code&gt;&lt;br&gt;
Distributes the rows in a partition into a specified number of roughly equal groups (buckets) and the designated number of buckets is passed as an argument e.g., n=4 for quartiles is &lt;code&gt;NTILE(4)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;br&gt;
The finance department wants to segment all 30 hospital visits into &lt;strong&gt;4 equal financial tiers (quartiles)&lt;/strong&gt; based on &lt;code&gt;total_bill_kes&lt;/code&gt; to identify top-tier high-billing visits versus routine lower-cost visits.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;patient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NTILE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_bill_kes&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;billing_quartile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;br&gt;
This query segments all hospital visits into 4 equal financial tiers from highest billing (&lt;code&gt;1&lt;/code&gt;) to lowest billing (&lt;code&gt;4&lt;/code&gt;)&lt;/p&gt;
&lt;h5&gt;
  
  
  2. Aggregate Window Functions
&lt;/h5&gt;

&lt;p&gt;When &lt;code&gt;Over()&lt;/code&gt; is attached to an aggregate function it turns into a window function.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;br&gt;
Management wants a master ledger of all visits that shows individual transaction amounts alongside cumulative running totals and departmental averages without collapsing rows.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visit_date&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;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Running total of all hospital revenue chronologically&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;total_bill_kes&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;visit_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visit_id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cumulative_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Average bill within the same department&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_id&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dept_average_bill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Cumulative count of visits over time&lt;/span&gt;
    &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visit_id&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;visit_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;visit_id&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;running_visit_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Min and Max bills recorded within that department so far&lt;/span&gt;
    &lt;span class="k"&gt;MIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_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;visit_date&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;running_min_dept_bill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_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;visit_date&lt;/span&gt; &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;CURRENT&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;running_max_dept_bill&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;br&gt;
Combines multiple aggregate functions with &lt;code&gt;OVER()&lt;/code&gt; to show running totals, department visit counts, and departmental price extremes alongside every individual row.&lt;/p&gt;
&lt;h5&gt;
  
  
  3. Value Window Functions
&lt;/h5&gt;

&lt;p&gt;Value window functions are analytical tools that let you copy a specific value from a different row and display it alongside a current row.&lt;/p&gt;

&lt;p&gt;Value window functions look into a data set and pull out actual existing data values and allow us to perform comparative, time-based analysis without having to rely on complex self-joins or subqueries.&lt;/p&gt;
&lt;h6&gt;
  
  
  Types of Value Window Functions
&lt;/h6&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Lag()&lt;/code&gt;&lt;br&gt;
Allows us to access data from a previous row&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Lead()&lt;/code&gt;&lt;br&gt;
Allows us to access data from the following row.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;br&gt;
The hospital wants to look at a patient's sequential visit history to see how much time elapsed between their current visit and their previous visit, as well as how their total bill changed compared to the next visit.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;patient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;visit_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visit_date&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;patient_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;visit_date&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;previous_visit_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LEAD&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&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;patient_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;visit_date&lt;/span&gt; &lt;span class="k"&gt;ASC&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;next_visit_bill&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;LAG()&lt;/code&gt; to look backward at the previous visit date for the same patient, and &lt;code&gt;LEAD()&lt;/code&gt; to look forward at the next visit's total bill.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;First_Value()&lt;/code&gt;&lt;br&gt;
Pulls the value from the very first row of the current window frame&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Last_Value()&lt;/code&gt;&lt;br&gt;
Grabs the Value from the last row of the window frame&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hospital administrators want to inspect every individual visit in each department alongside two benchmarks: the &lt;strong&gt;highest total bill&lt;/strong&gt; generated in that department and the &lt;strong&gt;lowest total bill&lt;/strong&gt; generated in that department.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dept&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FIRST_VALUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; 
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&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;highest_bill_in_dept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LAST_VALUE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; 
        &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
        &lt;span class="k"&gt;ROWS&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;PRECEDING&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;UNBOUNDED&lt;/span&gt; &lt;span class="k"&gt;FOLLOWING&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;lowest_bill_in_dept&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;Dim_Departments&lt;/span&gt; &lt;span class="n"&gt;dept&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;v&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;dept&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;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pull the highest bill (&lt;code&gt;FIRST_VALUE&lt;/code&gt;) and lowest bill (&lt;code&gt;LAST_VALUE&lt;/code&gt;) for each department onto every individual visit row.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Joins
&lt;/h3&gt;

&lt;p&gt;In SQL, the join clause is used to combine rows from two or more tables based on a related column between them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Types of Joins
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Inner Join
&lt;/h5&gt;

&lt;p&gt;This join returns only the rows that have matching values in both tables. If a record exists in one table but it doesn't have a match in another table, it is excluded entirely.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;List all completed patient visits, the name of the doctor who handled them and the department they visited.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_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;doctor_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dept&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&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;Patients&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&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;Doctors&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_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;doctor_id&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;dept&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;v&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;dept&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;h5&gt;
  
  
  Left (Outer) Join
&lt;/h5&gt;

&lt;p&gt;This join returns all rows from the left table and only the matched rows from the right table. If there is no match in the right table, the result is &lt;code&gt;NULL&lt;/code&gt; for the right table's columns.&lt;/p&gt;

&lt;h5&gt;
  
  
  Right (Outer) Join
&lt;/h5&gt;

&lt;p&gt;This join returns all rows from the right table and only the matched rows from the left table. If there is no match in the left table, the result is &lt;code&gt;NULL&lt;/code&gt; for the left table's columns.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;br&gt;
List all doctors alongside any visits they managed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_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;doctor_specialty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&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;Doctors&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;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_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;doctor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h5&gt;
  
  
  Full (Outer) Join
&lt;/h5&gt;

&lt;p&gt;Returns all records when there is a match in either the left or the right table. Rows with no matching values on either side will show &lt;code&gt;NULL&lt;/code&gt; values on the unmatched side.ur&lt;/p&gt;

&lt;h5&gt;
  
  
  Self Join
&lt;/h5&gt;

&lt;p&gt;This is a regular join where a table is joined with itself and is used when a table contains hierarchical or comparative relationships within it's own rows.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;br&gt;
Compare doctors within the same specialty e.g., to pair up doctors who share the exact same medical specialty.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;doctor_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;doctor_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_specialty&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Doctors&lt;/span&gt; &lt;span class="n"&gt;d1&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;Dim_Doctors&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt; 
    &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_specialty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_specialty&lt;/span&gt; 
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- Prevents duplicate reverse pairs and self-matching&lt;/span&gt;

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

&lt;/div&gt;



&lt;h5&gt;
  
  
  Cross Join
&lt;/h5&gt;

&lt;p&gt;This returns the cartesian product of two or more tables by combining every row from the first table with every row from the second table. &lt;/p&gt;

&lt;h3&gt;
  
  
  Common Table Expressions (CTEs)
&lt;/h3&gt;

&lt;p&gt;Common Table expressions are temporary, named result sets created within the execution scope of a single SQL Statement using a &lt;code&gt;WITH&lt;/code&gt; clause.&lt;/p&gt;

&lt;p&gt;They act like temporary views that exist only for the duration of a specific query.&lt;/p&gt;

&lt;h4&gt;
  
  
  CTE Types
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Basic CTEs
&lt;/h5&gt;

&lt;p&gt;This is a single temporary result set right before the main &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt; or &lt;code&gt;UPDATE&lt;/code&gt; statement that makes queries much easier to read by breaking them into logical, readable blocks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hospital management wants to review all patient visits where the total bill is greater than the average hospital bill.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;WITH&lt;/span&gt; &lt;span class="n"&gt;AvgHospitalBill&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_bill&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Fact_Visits&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_bill&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&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;AvgHospitalBill&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_bill&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Flag high cost visits relative to the hospital wide average by writing a CTE named &lt;code&gt;AvgHospitalBill&lt;/code&gt; to calculate the average billing amount, then query visits exceeding that average.&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Multiple CTEs
&lt;/h5&gt;

&lt;p&gt;These allow is to define more than one temporary result set within a single SQL query. They are particularly useful when a problem requires multiple stages of data processing where each CTE represents a distinct stage.&lt;/p&gt;

&lt;p&gt;There are two ways to use multiple CTEs.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;strong&gt;Independent CTEs (Parallel Logic)&lt;/strong&gt;
&lt;/h6&gt;

&lt;p&gt;This approach involves defining multiple unrelated datasets to combine later into a final query e.g., by performing a &lt;code&gt;JOIN&lt;/code&gt; or a &lt;code&gt;UNION&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hospital management wants to compare every individual visit's billing amount against the &lt;strong&gt;overall average hospital bill&lt;/strong&gt;, while simultaneously comparing it against the &lt;strong&gt;overall average patient satisfaction score&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;WITH&lt;/span&gt; &lt;span class="n"&gt;GlobalAverageBill&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_hospital_bill&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;GlobalAverageSatisfaction&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_hospital_satisfaction&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_hospital_bill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;benchmark_avg_bill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_hospital_satisfaction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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;benchmark_avg_satisfaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;-- Compare visit bill to the independent average bill CTE&lt;/span&gt;
    &lt;span class="k"&gt;CASE&lt;/span&gt; 
        &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_hospital_bill&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'Above Average Bill'&lt;/span&gt;
        &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'Below Average Bill'&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;billing_status&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&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;GlobalAverageBill&lt;/span&gt; &lt;span class="n"&gt;gab&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;GlobalAverageSatisfaction&lt;/span&gt; &lt;span class="n"&gt;gas&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two separate non-dependent CTEs &lt;code&gt;GlobalAverageBill&lt;/code&gt; which calculates the global average bill and &lt;code&gt;GlobalAverageSatisfaction&lt;/code&gt; which calculates the average satisfaction score cross join the results against the main &lt;code&gt;Visits&lt;/code&gt; table to benchmark every visit. &lt;/p&gt;

&lt;h6&gt;
  
  
  *&lt;em&gt;Dependent / Chained CTEs (Sequential Logic)&lt;/em&gt;
&lt;/h6&gt;

&lt;p&gt;This approach involves a CTE referencing a previously defined CTE in the same statement where each step refines the last. &lt;/p&gt;

&lt;p&gt;An important caveat to note with this approach is that forward referencing. A CTE cannot reference another CTE defined after it.  i.e. &lt;code&gt;cte_1&lt;/code&gt; can not read date from &lt;code&gt;cte_2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Isolate high-performing doctors and match them to their respective patient visits.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;WITH&lt;/span&gt; &lt;span class="n"&gt;DoctorRevenue&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;doctor_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_revenue&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&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;doctor_id&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;AverageRevenueBenchmark&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_doctor_revenue&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DoctorRevenue&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doctor_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_doctor_revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DoctorRevenue&lt;/span&gt; &lt;span class="n"&gt;dr&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;AverageRevenueBenchmark&lt;/span&gt; &lt;span class="n"&gt;arb&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;arb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_doctor_revenue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;CTE_1 &lt;code&gt;AverageRevenueBenchmark&lt;/code&gt; calculates total revenue per doctor and CTE_2 &lt;code&gt;DoctorRevenue&lt;/code&gt; finds the average revenue across all doctors. A final query filters doctors above that threshold. &lt;/p&gt;

&lt;h5&gt;
  
  
  3. Recursive CTEs
&lt;/h5&gt;

&lt;p&gt;A Recursive CTE calls itself repeatedly until it returns a result that no longer meets it's continuation condition.&lt;/p&gt;

&lt;p&gt;It consists of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anchor Member&lt;/strong&gt; - This is the base query that executes &lt;em&gt;first&lt;/em&gt; and only once and establishes the starting dataset, baseline rows or data for the recursion. It does not reference the name of the CTE&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Recursive Member&lt;/strong&gt; - This is the query that references the CTE itself to generate subsequent rows. It takes the output generated by the previous step, performs an operation on it, and feeds it back into the loop.  It must also reference the CTE name and include a terminating condition for the loop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Generate a series of 7 consecutive dates for a weekly operational schedule report.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;WITH&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; &lt;span class="n"&gt;DateSeries&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;-- Anchor Member: Runs once to set the Starting Point (June 1)&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2026-06-01'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;audit_date&lt;/span&gt;

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

&lt;span class="c1"&gt;-- Recursive Member:  References 'DateSeries' to add 1 day iteratively.&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;audit_date&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 day'&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DateSeries&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;audit_date&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt; &lt;span class="s1"&gt;'2026-06-07'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;audit_date&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DateSeries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A recursive CTE to increment dates day-by-day starting from June 1, 2026.&lt;/p&gt;

&lt;h5&gt;
  
  
  4. CTEs with Joins and Aggregations.
&lt;/h5&gt;

&lt;p&gt;CTEs can also handle heavy aggregations first, and then join those summarized results cleanly with other dimension tables.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A report showing each department's name, total patient visits, and total revenue generated, joined directly with department profiles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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="c1"&gt;-- Scenario: Summarizing departmental metrics using a CTE&lt;/span&gt;
&lt;span class="k"&gt;sql&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;DepartmentMetrics&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; 
        &lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_visits&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;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;department_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patient_satisfaction_score&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_satisfaction&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_visits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_revenue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;dm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_satisfaction&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DepartmentMetrics&lt;/span&gt; &lt;span class="n"&gt;dm&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;Dim_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;dm&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;&lt;em&gt;TASK&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Aggregate visit metrics in CTE &lt;code&gt;DepartmentMetrics&lt;/code&gt; and then &lt;code&gt;INNER JOIN&lt;/code&gt; it with &lt;code&gt;Departments&lt;/code&gt; table.&lt;/p&gt;

&lt;h6&gt;
  
  
  CTEs Vs Subqueries
&lt;/h6&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Common Table Expressions (CTEs)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Subqueries (Inline / Derived Tables)&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High. Defined at the top of the query block like a named variable.&lt;/td&gt;
&lt;td&gt;Low to Moderate. Often nested deep inside &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;FROM&lt;/code&gt;, or &lt;code&gt;SELECT&lt;/code&gt; clauses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can be referenced &lt;strong&gt;multiple times&lt;/strong&gt; within the main query body.&lt;/td&gt;
&lt;td&gt;Must be re-written or duplicated if needed in multiple places.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recursion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Supports recursion&lt;/strong&gt; (vital for hierarchical data like organizational charts or date series).&lt;/td&gt;
&lt;td&gt;Does not support recursion natively.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In modern PostgreSQL versions, CTEs can act as optimization fences or be automatically inlined depending on usage.&lt;/td&gt;
&lt;td&gt;Highly optimized by the query planner as inline expressions.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  SQL Subqueries
&lt;/h3&gt;

&lt;p&gt;Subqueries, also called inner queries or nested queries, are queries nested inside larger SQL statements (such as &lt;code&gt;SELECT&lt;/code&gt; , &lt;code&gt;INSERT&lt;/code&gt; OR &lt;code&gt;UPDATE&lt;/code&gt;)&lt;/p&gt;

&lt;h4&gt;
  
  
  Subquery Types
&lt;/h4&gt;

&lt;h5&gt;
  
  
  1. Non-Correlated Subqueries
&lt;/h5&gt;

&lt;p&gt;These are independent subqueries that run independently on their own before the outer query runs. They evaluate once and its result is passed up to the outer query.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Find all visits where the total bill is greater than the overall average hospital bill.&lt;/p&gt;

&lt;p&gt;*QUERY&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_bill_kes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;total_bill_kes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;h5&gt;
  
  
  2. Correlated Subqueries
&lt;/h5&gt;

&lt;p&gt;These are dependent subqueries that reference columns from the outer query. These subqueries cannot run independently and must re-evaluate row-by-row for every single record processed by the outer query. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Find every visit where a patient's bill is higher than the &lt;em&gt;average bill for that specific department&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v1&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;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;v2&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;v1&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;h5&gt;
  
  
  3. Scalar Subqueries
&lt;/h5&gt;

&lt;p&gt;These are subqueries that return exactly one row and one column and can be used anywhere a single value is expected as a result.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;SCENARIO&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Display every visit's total bill alongside the absolute maximum hospital bill as a static comparison column.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;visit_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&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;max_hospital_bill&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h5&gt;
  
  
  4. Nested Subqueries
&lt;/h5&gt;

&lt;p&gt;These are subqueries nested deep inside other subqueries. &lt;/p&gt;

&lt;h4&gt;
  
  
  Subquery Operators.
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;IN&lt;/code&gt;
This operator checks if a value matches any value returned by a list or a subquery.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;QUERY&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="c1"&gt;-- Find visits for patients who are from specific high-volume counties&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;patient_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;patient_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Patients&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;county&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Nairobi'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;EXISTS&lt;/code&gt;
Tests for the &lt;em&gt;existence&lt;/em&gt; of any rows returned by the subquery and returns &lt;strong&gt;&lt;em&gt;true&lt;/em&gt;&lt;/strong&gt; as soon as it finds at least one matching row. (very efficient for correlated checks)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;QUERY&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="c1"&gt;-- Find departments that have at least one emergency admission&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Departments&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;department_id&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;admission_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Emergency'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ANY / ALL&lt;/code&gt;
Compares a value to a set of values returned by a subquery. &lt;code&gt;&amp;gt; ANY&lt;/code&gt; means greater than the minimum while &lt;code&gt;&amp;gt; ALL&lt;/code&gt; means greater than the maximum.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Placement of Subqueries (&lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;FROM&lt;/code&gt;, &lt;code&gt;WHERE&lt;/code&gt;)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;WHERE&lt;/code&gt; &lt;strong&gt;Clause (Filtering)&lt;/strong&gt;&lt;br&gt;
Subqueries in the &lt;code&gt;WHERE&lt;/code&gt; clause act as dynamic filters based on aggregate calculations or cross-table criteria e.g., in filtering visits with above-average length of stay&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;FROM&lt;/code&gt; &lt;strong&gt;Clause (Derived Tables / Inline Views)&lt;/strong&gt;&lt;br&gt;
A subquery in the &lt;code&gt;FROM&lt;/code&gt; clause results in the treatment of the result set of that subquery as a temporary table that your main query can select from or join against. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;EXAMPLE&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Calculate the average department bills first, then categorize them in the outer query.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;QUERY&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;dept_summary&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;dept_summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_bill&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;department_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_bill_kes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;avg_bill&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Visits&lt;/span&gt;
    &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;department_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;dept_summary&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;dept_summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg_bill&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;SELECT&lt;/code&gt; &lt;strong&gt;Clause (Computed Columns)&lt;/strong&gt;
These allow for the attachment of scalar contextual calculations or aggregate metrics directly onto every individual row output e.g., in adding a column showing the global average bill nest to every individual visit row.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>data</category>
      <category>database</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Data Modelling, Relationships &amp; Joins in Power BI</title>
      <dc:creator>Njuguna Gichia</dc:creator>
      <pubDate>Sun, 13 Sep 2026 12:55:12 +0000</pubDate>
      <link>https://dev.to/9ichia/data-modelling-relationships-joins-in-power-bi-17a9</link>
      <guid>https://dev.to/9ichia/data-modelling-relationships-joins-in-power-bi-17a9</guid>
      <description>&lt;p&gt;Data modelling is the process of analyzing and defining different data types, as well as the relationships between those bits of data. In Power BI, data modelling is the process of organizing multiple tables and defining the relationships between them so that power BI can analyze and visualize business data. &lt;/p&gt;

&lt;p&gt;A good data model improves report performance, makes DAX calculations easier, supports accurate business insights, simplifies maintenance and provides better scalability as the amount of data and reporting requirements grow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Modelling Approaches.
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.Flat Table.
&lt;/h4&gt;

&lt;p&gt;A flat table / flat data model organizes data into a single table where each row represents a record and each column represents an attribute. Flat tables approach consolidates all the necessary data into a single denormalized table before it gets to the BI tool which sometimes means one table with all the columns (Facts and Attributes) already joined and ready for use. &lt;/p&gt;

&lt;p&gt;Advantages&lt;br&gt;
    - It isn't tied to a specific tool since all transformations happen at a database level and so moving across tools becomes easier.&lt;br&gt;
    - It minimized the risk of discrepancies between reports and KPI's by ensuring a single source of data&lt;br&gt;
    - It makes it easier to track changes, roll back updates and maintain data integrity by allowing for version control&lt;br&gt;
    - It makes querying easier without needing to understand complex relationships between tables.&lt;/p&gt;

&lt;p&gt;Disadvantages.&lt;br&gt;
    - Can become large and occasionally slower to query when dealing with massive datasets or frequent refresh cycles.&lt;br&gt;
    - May struggle in high volume environments, when powering multiple dashboards or when using databases that aren't optimized for analytical workloads.&lt;br&gt;
    - Risk of increasing storage costs and complicating updates due to data duplication.&lt;br&gt;
    - Very costly to optimize especially because they become inefficient as datasets grow.&lt;/p&gt;

&lt;p&gt;Situations where appropriate.&lt;br&gt;
    -When your dataset has only a few thousand rows, making performance slowdowns unnoticeable. &lt;br&gt;
    - When individual users or small teams manage data in simple tools like Excel or CSV files.&lt;br&gt;
    - When you need to test an idea rapidly without spending time building complex table relationships.&lt;br&gt;
    - When denormalized data speeds up read operations in specific reporting layers or data pipelines by avoiding table joins. &lt;br&gt;
    - When you must easily share raw data across different apps, platforms, or serverless functions without a database engine.&lt;/p&gt;

&lt;p&gt;Implications for Power BI performance and model complexity.&lt;br&gt;
    - Using a flat table data model in Power BI severely undermines performance and increases model complexity because it conflicts directly with the tool's underlying VertiPaq columnar storage engine. By forcing data into one massive, denormalized table, you suffer from inefficient column compression and a high memory footprint due to repeated text values, which triggers sluggish slicers and slow DAX scan times as Power BI is forced to query millions of rows rather than tiny dimension tables. Furthermore, while a single table eliminates relationships, it introduces development complexity through cluttered field lists, rigid models that cannot scale to new business processes, and high-risk DAX bugs like Auto-Exist, which can silently cause incorrect report totals when multiple columns are filtered together.&lt;/p&gt;

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

&lt;p&gt;The star schema features a central fact table that contains measurable quantitative data surrounded by dimension tables that contain descriptive attributes related to the fact data. The structure looks like a star, with a single dense table sitting at the center, and connected to surrounding lookup tables using simple relationships.&lt;/p&gt;

&lt;p&gt;This schema contains two distinct table types.&lt;br&gt;
    - Fact Tables.&lt;br&gt;
    These are at the center of the model. They record actual observations, metrics or transactional events (e.g., &lt;code&gt;Fact_Sales&lt;/code&gt;) and consist of continuous numerical values and a collection of Foreign Keys which act as pointers linking back to the surrounding descriptive tables.&lt;br&gt;
    - Dimension Tables.&lt;br&gt;
    These are tables surrounding the fact table that contain descriptive context or attributes related to the events recorded in the fact table (e.g., &lt;code&gt;Dim_Product&lt;/code&gt;, &lt;code&gt;Dim_Date&lt;/code&gt;, &lt;code&gt;Dim_Customer&lt;/code&gt;). Each Dimension table contains a unique Primary Key that anchors a one to many relationship flowing directly into the fact table. They also contain highly denormalized, flattened, repeated attributes to avoid deep branching relationships.&lt;/p&gt;

&lt;p&gt;Advantages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yield rapid query responses because analytical databases scan short, wide tables to filter rows in long narrow tables.&lt;/li&gt;
&lt;li&gt;The layout is easy for business analysts, report developers and end users to understand and navigate.&lt;/li&gt;
&lt;li&gt;Aggregations run quickly because calculations are grouped by highly indexed dimensional attributes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disadvantages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because the dimensional tables are denormalized and repeat text fields, they increase the physical storage size of those dimensions.&lt;/li&gt;
&lt;li&gt;This schema does not naturally handle complex hierarchy variations natively without forcing them into flat structures.&lt;/li&gt;
&lt;li&gt;Because a single star schema focuses around one core process, combining multiple distinct processes requires creating independent fact tables that share dimensions. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; Situations where appropriate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When stakeholders use tools like Tableau or PowerBI to build their own reports without needing to write complex SQL queries.&lt;/li&gt;
&lt;li&gt;When tracking performance metrics across consistent structural or Temporal intervals is required such as daily sales or machinery sensor output.&lt;/li&gt;
&lt;li&gt;When the data warehouses or data lakes process millions or billions of rows of historical data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; Implications for Power BI performance and model complexity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adopting a &lt;strong&gt;star-schema architecture&lt;/strong&gt; is the single most effective way to maximize Power BI performance and streamline model complexity because it aligns perfectly with the underlying &lt;strong&gt;VertiPaq in-memory columnar storage engine&lt;/strong&gt;. By separating transactional metrics into a central fact table and descriptive attributes into dedicated, highly compressed dimension tables, Power BI achieves &lt;strong&gt;superior data compression, smaller file sizes, and accelerated DAX query scans&lt;/strong&gt;. When users interact with report elements, visual rendering is nearly instant because slicers only need to query small, indexed dimension tables to dynamically pass filters down a clean &lt;strong&gt;one-to-many relationship path&lt;/strong&gt; to the fact table. From a usability standpoint, this structural layout reduces model complexity by eliminating confusing relationship loops and replacing multi-table snowflake structures with an &lt;strong&gt;intuitive field list&lt;/strong&gt;. Consequently, DAX formulas become remarkably simple, clean, and elegant, freeing report developers from writing defensive code to handle mismatched data granularities or fighting calculation defects like the &lt;strong&gt;Auto-Exist bug&lt;/strong&gt;, resulting in highly accurate calculations and faster report development.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This is a multi-dimensional data model where centralized, quantitative fact tables connect to dimension tables that are broken down into further lookup tables. The complex branching relationships extend outward from the center, resembling a snowflake.&lt;/p&gt;

&lt;p&gt;Built using three distinct structural layers. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Central Fact Tables.&lt;/strong&gt;
Contains transaction-level keys, numbers and facts - (e.g., &lt;code&gt;Fact_Sales&lt;/code&gt; containing metrics like &lt;code&gt;Quantity&lt;/code&gt; and &lt;code&gt;Revenue&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Primary Dimension Tables.&lt;/strong&gt;
These are tables that map directly to the fact table via a foreign key. They store a secondary foreign key pointing further down the line (e.g., &lt;code&gt;Dim_Product&lt;/code&gt; contains a key for categories rather than the category text name).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-Dimension Tables.&lt;/strong&gt;
These are normalized lookup tables connecting strictly to primary dimensions tables rather than the core fact table (e.g., a standalone &lt;code&gt;Dim_Category&lt;/code&gt; table or a separate &lt;code&gt;Dim_Geography&lt;/code&gt; table containing city/country data).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages.&lt;br&gt;
    - because of data normalization rules, text labels are never repeated across rows which minimizes storage in relational databases.&lt;br&gt;
    - Changing a group name or an attribute hierarchy only requires updating a singe record in a sub-dimension table instead of bulk-row modifications.&lt;br&gt;
    - Eliminates the risk of typing or recording anomalies since attributes are isolated in single, authoritative lookup sources.&lt;/p&gt;

&lt;p&gt;Disadvantages.&lt;br&gt;
    - Query engines must evaluate complex multi-table joins to trace a filter from a sub-dimension down to a transactional fact table.&lt;br&gt;
    - The visual layout is much more crowded and difficult to parse at a glance compared to a clean star structure.&lt;br&gt;
    - Keeping track of sequential, multi-layered keys increases development friction when writing ETL or data pipeline logic.&lt;/p&gt;

&lt;p&gt; Situations where appropriate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environments where reducing physical storage footprints and ensuring total database normalization are the absolute priorities.&lt;/li&gt;
&lt;li&gt;When a single branch of dimensions (like a complex, multi-tiered geography master list) is shared globally across multiple entirely different business lines or separate fact tables.&lt;/li&gt;
&lt;li&gt;Systems where data is strictly read directly via a traditional SQL engine optimized to parse multi-table relational joins natively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; Implications for Power BI performance and model complexity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In &lt;strong&gt;Power BI&lt;/strong&gt;, a snowflake schema should generally be avoided or flattened into a Star Schema during the ingestion phase due to several architectural friction points:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A snowflake schema significantly degrades Power BI performance and adds immense development overhead because it introduces &lt;strong&gt;multi-hop relationship chains&lt;/strong&gt; that force the &lt;strong&gt;VertiPaq columnar engine&lt;/strong&gt; to calculate complex cross-table joins during runtime. While it saves minimal physical disk storage, this design undermines Power BI’s native column compression algorithms, resulting in &lt;strong&gt;sluggish slicer response times&lt;/strong&gt; as filters must cascade through multiple intermediate lookup tables before arriving at the core fact table. From a usability standpoint, the model complexity spikes dramatically, loading the user interface with a &lt;strong&gt;cluttered, multi-table field list&lt;/strong&gt; that confuses report creators. This intricate web of relationships forces developers to write highly complex, defensive &lt;strong&gt;DAX measures&lt;/strong&gt; to accurately manage filter context across varying table granularities, drastically increasing the likelihood of development bottlenecks, unoptimized query paths, and frustrating calculation bugs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Fact tables are quantifiable events of businesses that are recorded at a certain degree of detail. It exclusively records &lt;strong&gt;measurable, quantitative business events&lt;/strong&gt; at the exact moment they occur. The information stored inside consists entirely of numeric data points known as &lt;strong&gt;measures&lt;/strong&gt; and structural foreign keys (FK) used to identify when, where, and how the event happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measures (Fact Tables)&lt;/strong&gt; are numeric values meant to be aggregated using mathematical operations such as &lt;code&gt;SUM&lt;/code&gt;, &lt;code&gt;AVERAGE&lt;/code&gt;, &lt;code&gt;MIN&lt;/code&gt;, or &lt;code&gt;MAX&lt;/code&gt;. Examples include physical units sold, gross profit margins, account balances, or sensor temperature readings.&lt;/p&gt;
&lt;h4&gt;
  
  
  Dimension Tables.
&lt;/h4&gt;

&lt;p&gt;Facts are given a descriptive context by dimension tables. They specify the way users slice, filter, group, and drill into measures. They contain the textual, categorical, or qualitative information that answers the "who, what, where, and when" of a transaction. Dimension rows are relatively stable and slow-moving compared to the rapidly expanding rows of a fact table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Descriptive Attributes (Dimension Tables)&lt;/strong&gt; are textual descriptors, IDs, or dates used to &lt;strong&gt;filter, group, slice, and slice-and-dice&lt;/strong&gt; measures in a report. Examples include an employee’s job title, a product’s color, a customer's country, or a calendar month name.&lt;/p&gt;
&lt;h4&gt;
  
  
  Granularity.
&lt;/h4&gt;

&lt;p&gt;Grain defines exactly what one row represents in a fact table. All foreign keys and measures inside the fact table have to match this chosen grain to prevent double counting or data corruption during reporting.&lt;/p&gt;

&lt;p&gt;Fact Table Example.&lt;/p&gt;

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

&lt;p&gt;Dimension Table Example.&lt;/p&gt;

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

&lt;p&gt;Table plain Text. &lt;/p&gt;

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

&lt;p&gt;A &lt;strong&gt;relationship&lt;/strong&gt; in Power BI defines a logical link between two tables based on a shared column. In a relational data model, data is split into specialized tables to eliminate redundancy and improve performance (normalization). Relationships allow Power BI to filter and slice metrics in transaction tables using descriptive attributes from dimension tables without combining everything into a single massive, flat table.&lt;/p&gt;
&lt;h2&gt;
  
  
  Core Data Modeling Concepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Primary Key (PK):&lt;/strong&gt; A column (or set of columns) in a dimension table where every value is completely unique. It serves as the master identifier for a single entity (e.g., &lt;code&gt;CustomerKey&lt;/code&gt; in &lt;code&gt;Dim_Customer&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Foreign Key (FK):&lt;/strong&gt; A column in a table that references a Primary Key in another table. It allows multiple records to map back to a single primary entity (e.g., &lt;code&gt;CustomerKey&lt;/code&gt; in &lt;code&gt;Fact_Sales&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unique Values:&lt;/strong&gt; Ensures that each key appears exactly once on the "One" side of a relationship. If duplicates exist where Power BI expects unique values, data integrity is lost, leading to calculation errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cardinality:&lt;/strong&gt; Refers to the numerical mapping between rows of two related tables (e.g., 1:*, 1:1, &lt;em&gt;:&lt;/em&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Referential Integrity:&lt;/strong&gt; The rule ensuring that every Foreign Key value in a fact table exists as a matching Primary Key in the corresponding dimension table. If a sale records &lt;code&gt;CustomerKey = 999&lt;/code&gt;, but key &lt;code&gt;999&lt;/code&gt; is missing from &lt;code&gt;Dim_Customer&lt;/code&gt;, referential integrity is broken, resulting in blank/unmatched categories in reports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Active vs. Inactive Relationships:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active Relationship:&lt;/strong&gt; The default link between two tables used to propagate filter context automatically across visuals. Only &lt;strong&gt;one&lt;/strong&gt; active relationship can exist between any two tables at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inactive Relationship:&lt;/strong&gt; A dormant connection created when multiple paths exist between two tables (e.g., &lt;code&gt;Dim_Date&lt;/code&gt; linked to both &lt;code&gt;OrderDate&lt;/code&gt; and &lt;code&gt;ShipDate&lt;/code&gt; in &lt;code&gt;Fact_Sales&lt;/code&gt;). It remains unused until explicitly activated in DAX measures using functions like &lt;code&gt;USERELATIONSHIP()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Example from Snowflake-Schema-Main
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Dim_Customer&lt;/code&gt; Table:&lt;/strong&gt; &lt;code&gt;CustomerKey&lt;/code&gt; is the &lt;strong&gt;Primary Key&lt;/strong&gt;. It contains strictly &lt;strong&gt;unique values&lt;/strong&gt; because each customer is registered only once in master records.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Fact_Sales&lt;/code&gt; Table:&lt;/strong&gt; &lt;code&gt;CustomerKey&lt;/code&gt; acts as a &lt;strong&gt;Foreign Key&lt;/strong&gt;. A single customer can make multiple purchases over time, so their &lt;code&gt;CustomerKey&lt;/code&gt; appears &lt;strong&gt;multiple times&lt;/strong&gt; (many rows) across transactional records.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Relationship Cardinalities
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. One-to-Many (1:*)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; One row in the dimension table connects to zero, one, or multiple rows in the fact table. Filters automatically flow from the "One" side (dimension) to refine the "Many" side (fact).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; &lt;code&gt;Dim_Product&lt;/code&gt; (&lt;code&gt;1&lt;/code&gt;) connects to &lt;code&gt;Fact_Sales&lt;/code&gt; (&lt;code&gt;*&lt;/code&gt;) via &lt;code&gt;ProductKey&lt;/code&gt;. Filtering for "Laptops" filters all transaction records for laptops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; This is the &lt;strong&gt;standard best practice&lt;/strong&gt; for dimensional data modeling (Star Schema / Snowflake Schema).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When NOT to Use:&lt;/strong&gt; Never use if both tables contain non-unique, repeating key values.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Dim_Product ] (1)  &amp;lt;---------&amp;gt;  (*) [ Fact_Sales ]
(ProductKey: PK)                      (ProductKey: FK)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; One row in Table A links directly to exactly one matching row in Table B.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Linking a &lt;code&gt;Dim_Customer&lt;/code&gt; table to a separate &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; table where sensitive user details are stored on a 1-to-1 basis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Useful when splitting large tables for security purposes, performance optimization, or separating frequently accessed columns from rarely used ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When NOT to Use:&lt;/strong&gt; Avoid when unnecessary, as two 1:1 tables can almost always be merged into a single table, reducing model complexity.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Dim_Customer ] (1)  &amp;lt;---------&amp;gt;  (1) [ Dim_CustomerSecurity ]
(CustomerKey)                           (CustomerKey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Neither table has unique values in the key column. Rows on both sides connect to multiple rows in the opposite table, requiring bi-directional filtering by default or bridge handling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Linking a &lt;code&gt;Fact_Sales&lt;/code&gt; table directly to a &lt;code&gt;Fact_Targets&lt;/code&gt; table on &lt;code&gt;DateKey&lt;/code&gt; when both tables contain multiple rows for each date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When to Use:&lt;/strong&gt; Use sparingly when modeling direct relationship scenarios where unique keys cannot be established.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When NOT to Use:&lt;/strong&gt; Avoid using as a quick fix for duplicate key issues. &lt;em&gt;: Relationships create complex DAX calculations, introduce ambiguity, and can produce unpredictable visual aggregations. Instead, build a intermediate bridge/dimension table with unique values to split it into two 1:&lt;/em&gt; relationships.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Fact_Sales ] (*)  &amp;lt;--- (Bridge / Intermediate) ---&amp;gt;  (*) [ Fact_Targets ]
(DateKey)                                                   (DateKey)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;p&gt;When you select a value in a report visual or slicer (such as choosing a specific year or product category), Power BI creates a &lt;strong&gt;filter context&lt;/strong&gt;. This filter context flows automatically across active relationships between tables to isolate the relevant transaction rows in your fact table.&lt;/p&gt;
&lt;h3&gt;
  
  
  How Filter Context Flows
&lt;/h3&gt;

&lt;p&gt;Filters flow along the relationship line between tables, moving from the table where the selection was made to the target table. The direction in which this filter context is allowed to travel depends on the &lt;strong&gt;Filter Direction&lt;/strong&gt; setting of the relationship.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Dimension Table ]  ─── (Filter Direction Arrow) ───&amp;gt;  [ Fact Table ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Single-Direction Filtering (Default &amp;amp; Standard)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Filters travel in &lt;strong&gt;one direction only&lt;/strong&gt;—from the "One" side (Dimension) to the "Many" side (Fact).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Behavior:&lt;/strong&gt; Selecting an entity in a dimension table filters the records in the fact table. However, filtering a fact table does &lt;strong&gt;not&lt;/strong&gt; filter or restrict the items shown in the dimension table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model View Indicator:&lt;/strong&gt; A single arrowhead pointing from the dimension table toward the fact table (&lt;code&gt;1 ──&amp;gt; *&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  2. Bidirectional / Both Filtering
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Filters travel in &lt;strong&gt;both directions&lt;/strong&gt; across the relationship.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Behavior:&lt;/strong&gt; Selecting a value in a dimension filters the fact table, and filtering the fact table simultaneously filters the connected dimension table (as well as any other downstream dimension tables).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Model View Indicator:&lt;/strong&gt; A double arrowhead pointing toward both tables (&lt;code&gt;1 &amp;lt;──&amp;gt; *&lt;/code&gt; or &lt;code&gt;* &amp;lt;──&amp;gt; *&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Step-by-Step Example: Filtering &lt;code&gt;Fact_Sales&lt;/code&gt; from &lt;code&gt;Dim_Product&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Using our dataset model (&lt;code&gt;Dim_Category&lt;/code&gt; ──&amp;gt; &lt;code&gt;Dim_Product&lt;/code&gt; ──&amp;gt; &lt;code&gt;Fact_Sales&lt;/code&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Action:&lt;/strong&gt; A user clicks on the &lt;strong&gt;"Electronics"&lt;/strong&gt; category in a report slicer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;First Propagation:&lt;/strong&gt; The filter context targets &lt;code&gt;Dim_Category&lt;/code&gt; where &lt;code&gt;CategoryName = "Electronics"&lt;/code&gt;. It passes down the &lt;code&gt;1 ──&amp;gt; *&lt;/code&gt; relationship on &lt;code&gt;CategoryKey&lt;/code&gt; to &lt;code&gt;Dim_Product&lt;/code&gt;, instantly filtering &lt;code&gt;Dim_Product&lt;/code&gt; to only include products belonging to Electronics (e.g., &lt;code&gt;ProductKey = 1&lt;/code&gt; for "Laptop Pro" and &lt;code&gt;ProductKey = 2&lt;/code&gt; for "Smartphone X").&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Second Propagation:&lt;/strong&gt; The filter context continues down the next &lt;code&gt;1 ──&amp;gt; *&lt;/code&gt; relationship on &lt;code&gt;ProductKey&lt;/code&gt; to &lt;code&gt;Fact_Sales&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Final Result:&lt;/strong&gt; Power BI filters &lt;code&gt;Fact_Sales&lt;/code&gt; to keep only rows where &lt;code&gt;ProductKey&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;2&lt;/code&gt;, dynamically recalculating metrics like &lt;code&gt;SUM(Revenue)&lt;/code&gt; or &lt;code&gt;SUM(Quantity)&lt;/code&gt; strictly for Electronics transactions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Bidirectional Filtering Should Be Used Carefully
&lt;/h3&gt;

&lt;p&gt;While bidirectional filtering allows tables to filter each other, enabling it permanently in relationship properties introduces major data modeling risks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ambiguous Filter Paths:&lt;/strong&gt; When multiple bidirectional relationships exist in a model, Power BI creates closed loops (circular paths). The engine cannot determine which path to take to apply a filter, leading to unpredictable measure results or DAX calculation errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unnecessary Model Complexity &amp;amp; Slow Performance:&lt;/strong&gt; Bidirectional paths force Power BI's VertiPaq engine to evaluate dynamic cross-filtering on large tables at report runtime, increasing memory usage and slowing down report visuals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unexpected Slicer Masking:&lt;/strong&gt; Filtering a transaction table can unexpectedly mask or hide valid items in dimension drop-downs, confusing users who expect to see all available lookup options.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Best Practice:&lt;/strong&gt; Keep physical relationship filter directions set to &lt;strong&gt;Single&lt;/strong&gt;. If bidirectional filtering is required for a specific business metric, activate it dynamically inside individual DAX measures using &lt;code&gt;CROSSFILTER()&lt;/code&gt; rather than changing the physical model properties.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Joins in Power Query.
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;join&lt;/strong&gt; is a relational operation that combines columns from two tables based on matching values in one or more common key columns.&lt;/p&gt;

&lt;p&gt;In Power Query, tables are combined using &lt;strong&gt;Merge Queries&lt;/strong&gt; (&lt;code&gt;Home&lt;/code&gt; $\rightarrow$ &lt;code&gt;Merge Queries&lt;/code&gt;). You select a primary table (Left Table), a secondary table (Right Table), and highlight the matching key columns in both preview panels. Power Query evaluates the matching key values and appends a nested &lt;code&gt;Table&lt;/code&gt; column containing the matched rows from the right table, which can then be expanded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source Sample Tables from Our Model
&lt;/h3&gt;

&lt;p&gt;To illustrate each join type clearly, we use two sample tables from our dataset:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Left Table: &lt;code&gt;Dim_Customer&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains records for &lt;code&gt;CustomerKey&lt;/code&gt; 1 through 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Right Table: &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains records for &lt;code&gt;CustomerKey&lt;/code&gt; 1 through 4, plus an unassigned security record with &lt;code&gt;CustomerKey&lt;/code&gt; 99.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  1. Left Outer Join (All from First, Matching from Second)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Evaluates every row in the left table and retrieves matching rows from the right table. If no match exists in the right table, &lt;code&gt;null&lt;/code&gt; values are filled in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; All rows from the &lt;strong&gt;Left Table&lt;/strong&gt; + matched rows from the &lt;strong&gt;Right Table&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Merging master customer details (&lt;code&gt;Dim_Customer&lt;/code&gt;) with optional security audit records (&lt;code&gt;Dim_CustomerSecurity&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  2. Right Outer Join (All from Second, Matching from First)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Evaluates every row in the right table and retrieves matching rows from the left table. Unmatched left attributes are filled with &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; All rows from the &lt;strong&gt;Right Table&lt;/strong&gt; + matched rows from the &lt;strong&gt;Left Table&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Ensuring all compliance records in &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; are evaluated against customer master records.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3. Full Outer Join (All Rows from Both Tables)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Combines all records from both tables regardless of matches. Missing pairs on either side are filled with &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; &lt;strong&gt;All rows from both tables&lt;/strong&gt; (complete union of matched and unmatched keys).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Reconciling two lists to see all active customers and all security profiles.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  4. Inner Join (Only Matching Rows)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Keeps only rows where the key value exists in &lt;strong&gt;both&lt;/strong&gt; the left and right tables. Unmatched records from both sides are dropped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; &lt;strong&gt;Strictly matching pairs&lt;/strong&gt; present in both tables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Returning only customers who have completed security verification.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  5. Left Anti Join (Rows Only in First Table)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Filters the left table to isolate rows that have &lt;strong&gt;no matching key&lt;/strong&gt; in the right table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; &lt;strong&gt;Left-only unmatched rows&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Identifying customers in &lt;code&gt;Dim_Customer&lt;/code&gt; who are missing a security profile in &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  6. Right Anti Join (Rows Only in Second Table)
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt; Filters the right table to isolate rows that have &lt;strong&gt;no matching key&lt;/strong&gt; in the left table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Records Retained:&lt;/strong&gt; &lt;strong&gt;Right-only unmatched rows&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practical Example:&lt;/strong&gt; Finding orphan security profiles in &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; that are not assigned to any customer in &lt;code&gt;Dim_Customer&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Power Query Joins vs Power BI Relationships
&lt;/h3&gt;

&lt;p&gt;Merging tables in Power Query and establishing model relationships in Power BI serve fundamentally different functions within your analytical data pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Structural Differences
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Physical Data Combination in Power Query Merge:&lt;/strong&gt; Yes, a Power Query Merge physically combines attributes into a single query structure during data transformation. When expanding columns from &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; into &lt;code&gt;Dim_Customer&lt;/code&gt;, Power Query reshapes the underlying M-code query and loads a single combined table into memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separation via Model Relationships:&lt;/strong&gt; No, creating a relationship between tables does &lt;strong&gt;not&lt;/strong&gt; combine them physically. Both tables remain distinct physical storage objects in the VertiPaq engine. The relationship acts as a logical bridge allowing filter context to pass dynamically between them at visual rendering time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Workflow Stage Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Power Query Merge:&lt;/strong&gt; Occurs at the &lt;strong&gt;ETL (Extract, Transform, Load) stage&lt;/strong&gt; before data is compressed and loaded into the tabular data model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Relationships:&lt;/strong&gt; Defined in the &lt;strong&gt;Model View stage&lt;/strong&gt; after data has been loaded into the VertiPaq engine, governing DAX calculation context and reporting visuals.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Core Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature / Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Power Query Merge Queries&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Power BI Model Relationships&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denormalize, consolidate, or clean datasets&lt;/td&gt;
&lt;td&gt;Build Star/Snowflake schemas for dynamic analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permanently increases table width (more columns/rows)&lt;/td&gt;
&lt;td&gt;Keeps tables lean and distinct in VertiPaq memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Power Query Formula Engine (M)&lt;/td&gt;
&lt;td&gt;VertiPaq Analytics Engine (DAX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Interactivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static (fixed during data refresh)&lt;/td&gt;
&lt;td&gt;Dynamic (responds instantly to slicers and visual filters)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to Choose a Merge vs. a Relationship
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. When to Use a Merge (Power Query)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consolidating 1-to-1 Extensions:&lt;/strong&gt; Merging extended attribute lookup tables (e.g., merging &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; into &lt;code&gt;Dim_Customer&lt;/code&gt; to avoid holding unnecessary table entities in your model).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flattening Snowflake Dimensions:&lt;/strong&gt; Collapsing multi-level dimension hierarchies (e.g., merging &lt;code&gt;Dim_Category&lt;/code&gt; directly into &lt;code&gt;Dim_Product&lt;/code&gt; to create a clean, single-level Product dimension table).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pre-filtering Fact Rows:&lt;/strong&gt; Joining reference keys to eliminate unwanted rows before loading data into the model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. When to Use a Relationship (Model View)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connecting Fact and Dimension Tables:&lt;/strong&gt; Linking &lt;code&gt;Fact_Sales&lt;/code&gt; to &lt;code&gt;Dim_Customer&lt;/code&gt; or &lt;code&gt;Dim_Product&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preserving Multi-Fact Models:&lt;/strong&gt; Connecting shared dimensions (e.g., &lt;code&gt;Dim_Category&lt;/code&gt; or &lt;code&gt;Dim_Date&lt;/code&gt;) to multiple fact tables like &lt;code&gt;Fact_Sales&lt;/code&gt; and &lt;code&gt;Fact_Targets&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Impact of Excessive Merging on Data Model Performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Severe Storage Bloat:&lt;/strong&gt; Merging dimension attributes directly into large fact tables (e.g., merging &lt;code&gt;Dim_Customer&lt;/code&gt; attributes into 1,000,000 rows of &lt;code&gt;Fact_Sales&lt;/code&gt;) forces repetitive string text (such as customer names and email addresses) into every fact row. This ruins VertiPaq run-length encoding (RLE) dictionary compression and balloons your file size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Granularity Errors and Measure Corruption:&lt;/strong&gt; Merging a table with a higher row count (1-to-Many) into a fact table duplicates transaction metrics, causing measures like &lt;code&gt;SUM(SalesAmount)&lt;/code&gt; or &lt;code&gt;COUNTROWS()&lt;/code&gt; to return inaccurate, double-counted figures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Keeping Fact and Dimension Tables Separate is Preferable
&lt;/h3&gt;

&lt;p&gt;Keeping fact tables (containing numerical events/metrics) separated from dimension tables (containing descriptive attributes) inside a &lt;strong&gt;Star Schema&lt;/strong&gt; provides critical modeling advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimal Compression:&lt;/strong&gt; High-cardinality text fields are stored once in lightweight dimension lookup tables. Fact tables retain only narrow integer keys (&lt;code&gt;CustomerKey&lt;/code&gt;, &lt;code&gt;ProductKey&lt;/code&gt;), allowing the VertiPaq engine to compress millions of rows efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible Analytics Across Shared Dimensions:&lt;/strong&gt; A single standalone dimension table (e.g., &lt;code&gt;Dim_Category&lt;/code&gt;) can simultaneously filter multiple fact tables (&lt;code&gt;Fact_Sales&lt;/code&gt; and &lt;code&gt;Fact_Targets&lt;/code&gt;), enabling side-by-side performance tracking against target KPIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clean, Readable DAX:&lt;/strong&gt; Keeping entities separate simplifies DAX measures, avoiding complex filtering logic required when working with oversized single-flat-table models.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Recommended Power BI Model
&lt;/h3&gt;

&lt;p&gt;Merging tables in Power Query and establishing model relationships in Power BI serve fundamentally different functions within your analytical data pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview: Merge vs. Relationship
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Structural Differences
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Physical Data Combination in Power Query Merge:&lt;/strong&gt; Yes, a Power Query Merge physically combines attributes into a single query structure during data transformation. When expanding columns from &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; into &lt;code&gt;Dim_Customer&lt;/code&gt;, Power Query reshapes the underlying M-code query and loads a single combined table into memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separation via Model Relationships:&lt;/strong&gt; No, creating a relationship between tables does &lt;strong&gt;not&lt;/strong&gt; combine them physically. Both tables remain distinct physical storage objects in the VertiPaq engine. The relationship acts as a logical bridge allowing filter context to pass dynamically between them at visual rendering time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Workflow Stage Execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Power Query Merge:&lt;/strong&gt; Occurs at the &lt;strong&gt;ETL (Extract, Transform, Load) stage&lt;/strong&gt; before data is compressed and loaded into the tabular data model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Relationships:&lt;/strong&gt; Defined in the &lt;strong&gt;Model View stage&lt;/strong&gt; after data has been loaded into the VertiPaq engine, governing DAX calculation context and reporting visuals.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Core Comparison Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Aspect&lt;/th&gt;
&lt;th&gt;Power Query Merge Queries&lt;/th&gt;
&lt;th&gt;Power BI Model Relationships&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denormalize, consolidate, or clean datasets&lt;/td&gt;
&lt;td&gt;Build Star/Snowflake schemas for dynamic analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permanently increases table width (more columns/rows)&lt;/td&gt;
&lt;td&gt;Keeps tables lean and distinct in VertiPaq memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Power Query Formula Engine (M)&lt;/td&gt;
&lt;td&gt;VertiPaq Analytics Engine (DAX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Interactivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static (fixed during data refresh)&lt;/td&gt;
&lt;td&gt;Dynamic (responds instantly to slicers and visual filters)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to Choose a Merge vs. a Relationship
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. When to Use a Merge (Power Query)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consolidating 1-to-1 Extensions:&lt;/strong&gt; Merging extended attribute lookup tables (e.g., merging &lt;code&gt;Dim_CustomerSecurity&lt;/code&gt; into &lt;code&gt;Dim_Customer&lt;/code&gt; to avoid holding unnecessary table entities in your model).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flattening Snowflake Dimensions:&lt;/strong&gt; Collapsing multi-level dimension hierarchies (e.g., merging &lt;code&gt;Dim_Category&lt;/code&gt; directly into &lt;code&gt;Dim_Product&lt;/code&gt; to create a clean, single-level Product dimension table).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pre-filtering Fact Rows:&lt;/strong&gt; Joining reference keys to eliminate unwanted rows before loading data into the model.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. When to Use a Relationship (Model View)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connecting Fact and Dimension Tables:&lt;/strong&gt; Linking &lt;code&gt;Fact_Sales&lt;/code&gt; to &lt;code&gt;Dim_Customer&lt;/code&gt; or &lt;code&gt;Dim_Product&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preserving Multi-Fact Models:&lt;/strong&gt; Connecting shared dimensions (e.g., &lt;code&gt;Dim_Category&lt;/code&gt; or &lt;code&gt;Dim_Date&lt;/code&gt;) to multiple fact tables like &lt;code&gt;Fact_Sales&lt;/code&gt; and &lt;code&gt;Fact_Targets&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Impact of Excessive Merging on Data Model Performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Severe Storage Bloat:&lt;/strong&gt; Merging dimension attributes directly into large fact tables (e.g., merging &lt;code&gt;Dim_Customer&lt;/code&gt; attributes into 1,000,000 rows of &lt;code&gt;Fact_Sales&lt;/code&gt;) forces repetitive string text (such as customer names and email addresses) into every fact row. This ruins VertiPaq run-length encoding (RLE) dictionary compression and balloons your file size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Granularity Errors and Measure Corruption:&lt;/strong&gt; Merging a table with a higher row count (1-to-Many) into a fact table duplicates transaction metrics, causing measures like &lt;code&gt;SUM(SalesAmount)&lt;/code&gt; or &lt;code&gt;COUNTROWS()&lt;/code&gt; to return inaccurate, double-counted figures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Keeping Fact and Dimension Tables Separate is Preferable
&lt;/h3&gt;

&lt;p&gt;Keeping fact tables (containing numerical events/metrics) separated from dimension tables (containing descriptive attributes) inside a &lt;strong&gt;Star Schema&lt;/strong&gt; provides critical modeling advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimal Compression:&lt;/strong&gt; High-cardinality text fields are stored once in lightweight dimension lookup tables. Fact tables retain only narrow integer keys (&lt;code&gt;CustomerKey&lt;/code&gt;, &lt;code&gt;ProductKey&lt;/code&gt;), allowing the VertiPaq engine to compress millions of rows efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible Analytics Across Shared Dimensions:&lt;/strong&gt; A single standalone dimension table (e.g., &lt;code&gt;Dim_Category&lt;/code&gt;) can simultaneously filter multiple fact tables (&lt;code&gt;Fact_Sales&lt;/code&gt; and &lt;code&gt;Fact_Targets&lt;/code&gt;), enabling side-by-side performance tracking against target KPIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clean, Readable DAX:&lt;/strong&gt; Keeping entities separate simplifies DAX measures, avoiding complex filtering logic required when working with oversized single-flat-table models.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>analytics</category>
      <category>data</category>
      <category>database</category>
    </item>
    <item>
      <title>Getting Started with Excel for Data Analytics: From Basics to Data Cleaning.</title>
      <dc:creator>Njuguna Gichia</dc:creator>
      <pubDate>Wed, 02 Sep 2026 20:26:38 +0000</pubDate>
      <link>https://dev.to/9ichia/getting-started-with-excel-for-data-analytics-from-basics-to-data-cleaning-438k</link>
      <guid>https://dev.to/9ichia/getting-started-with-excel-for-data-analytics-from-basics-to-data-cleaning-438k</guid>
      <description>&lt;p&gt;Microsoft Excel is a spreadsheet software that allows users to organize, analyze, calculate and visualize data. Despite the rise of Python, R and SQL, Excel has remained a foundational data analysis tool across industries because of it's low learning curve, powerful features like pivot tables, formulas, functions and quick charts and because no coding experience is needed to navigate or see results.&lt;/p&gt;

&lt;p&gt;In this article, we will explore navigating the grid, essential formulas, data filtering and cleaning messy data using Excel's built-in features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding The Excel Interface
&lt;/h2&gt;

&lt;p&gt;The excel interface consists of these core elements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Ribbon &amp;amp; Tabs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The toolbar across the top that contains the primary commands organized into tabs (&lt;strong&gt;Home, Insert, Draw, Page Layout&lt;/strong&gt; etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Quick Access Toolbar

&lt;ul&gt;
&lt;li&gt;The icons on the top left corner to Save, Undo and Redo.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formula Bar&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;The area above the grid where the content or the formula of the selected cell appears.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name Box&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Displays the address of the selected Cell (e.g. &lt;code&gt;B3&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Grid&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;This is the visual network of intersecting horizontal lines and vertical lines that creates the workspace. The Grid is made up of 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rows&lt;/strong&gt; - Horizontal lines of cells labelled vertically with numbers (e.g. Row 1)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Columns&lt;/strong&gt; - Vertical line of cells labelled horizontally with letters (e.g. Column A)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cells&lt;/strong&gt; - A single box where a row and a column intersect (e.g. &lt;code&gt;B4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range&lt;/strong&gt; - A group of selected cells (e.g., &lt;code&gt;A1:C3&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Worksheet Tabs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The tabs at the bottom that show the name of the spreadsheet and also allow you to navigate between the sheets in a workbook.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Essential Navigation &amp;amp; Keyboard Shortcuts
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Keyboard Navigation:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Arrow keys&lt;/code&gt; – Move one cell at a time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Arrow key&lt;/code&gt; – Jump to the end of the data region&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Tab&lt;/code&gt; – Move one cell right&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Enter&lt;/code&gt; – Move one cell down&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + S&lt;/code&gt; – Save the file&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Mouse Navigation:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Click to select a single cell&lt;/li&gt;
&lt;li&gt;Click &amp;amp; Drag to select multiple cells&lt;/li&gt;
&lt;li&gt;Scroll to move vertically or horizontally across the sheet&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Efficiency Combinations (Keyboard &amp;amp; Mouse):
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Right Arrow&lt;/code&gt; to jump to the end of row&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Down Arrow&lt;/code&gt; to jump to the bottom&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Shift + Arrow Keys&lt;/code&gt; to select ranges efficiently&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Data Environment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data Types.
&lt;/h3&gt;

&lt;p&gt;Excel supports 4 primary data types. i.e.,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Text&lt;/strong&gt; - Any non-numeric or character based information e.g. names, addresses, descriptions).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numbers&lt;/strong&gt; - Any values that can participate in mathematical calculations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dates and Time&lt;/strong&gt; - Values used to track time-based events and calculations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boolean&lt;/strong&gt; - Logical values, either (&lt;code&gt;TRUE&lt;/code&gt; or &lt;code&gt;FALSE&lt;/code&gt;) usually generated from logical formulas.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Entering Data
&lt;/h3&gt;

&lt;p&gt;We can enter data anywhere in the grid by: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking on a cell&lt;/li&gt;
&lt;li&gt;Typing in our value (e.g. &lt;code&gt;Hello&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Enter&lt;/code&gt; to move down or &lt;code&gt;Tab&lt;/code&gt; to move right&lt;/li&gt;
&lt;li&gt;Double click on the cell or selecting it and pressing &lt;code&gt;F2&lt;/code&gt; to edit existing content.
### Formatting Basics.
Formatting changes how the data looks without changing the values stored in the cells, making the data visually clear and more professional.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Text Formatting.
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Selecting the Cell&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Home &amp;gt; **Font Group&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clicking &lt;strong&gt;Bold&lt;/strong&gt; (&lt;code&gt;Ctrl+B&lt;/code&gt;), &lt;strong&gt;&lt;em&gt;Italic&lt;/em&gt;&lt;/strong&gt; (&lt;code&gt;Ctrl + I&lt;/code&gt;), Underline (&lt;code&gt;Ctrl + U&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Change Font, Font Size, and Font Colour.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h4&gt;
  
  
  Number Formatting
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Select cells with numeric data. &lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Home &amp;gt; Number section&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose the appropriate format:

&lt;ul&gt;
&lt;li&gt;Number (with decimals)&lt;/li&gt;
&lt;li&gt;Currency (KES, $, etc.)&lt;/li&gt;
&lt;li&gt;Percentage&lt;/li&gt;
&lt;li&gt;Date formats&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Alternatively, we can access the Format Cells Dialogue by (&lt;code&gt;Ctrl+1&lt;/code&gt;)&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Data Cleaning and Sorting.
&lt;/h3&gt;

&lt;p&gt;Data Cleaning is the process of formatting, or removing incorrect, duplicate or incomplete data within a dataset. Sometimes, especially when working with large datasets, data can get misplaced, mistyped or mislabeled. Cleaning ensures that the dataset is accurate and consistent. &lt;/p&gt;

&lt;p&gt;Data cleaning prevents making decisions based on flawed data, catches and fixes errors early and provides clean inputs for formulas and charts.&lt;/p&gt;

&lt;p&gt;For Practice, I used a spreadsheet containing HR data, applying a combination of built-in excel tools and formulas to clean it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Best Practice:&lt;/strong&gt; Always preserve your original raw data. Make all required cleaning changes on a duplicate worksheet or separate file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  How to Duplicate Your Data
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click anywhere in the spreadsheet and press &lt;strong&gt;Ctrl + A&lt;/strong&gt; to select all cells.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Press &lt;strong&gt;Ctrl + C&lt;/strong&gt; to copy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;+&lt;/strong&gt; button at the bottom of the window to create a new sheet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click cell &lt;strong&gt;A1&lt;/strong&gt; in the new sheet and press &lt;strong&gt;Ctrl + V&lt;/strong&gt; to paste.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Double-click the sheet tab and rename it to &lt;code&gt;Cleaned_HR_Data&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives us a canvas to work in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Cleaning Workflow
&lt;/h2&gt;

&lt;p&gt;We did the cleaning in distinct stages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Analyzing the Data&lt;/strong&gt;:
&lt;/h3&gt;

&lt;p&gt;Here we checked the data for visible errors and inconsistencies. In this dataset, we found mixed data types in numeric fields (text mixed with numbers), missing/blank values, duplicate entries, currency formatting errors, typos and misspellings etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Removing duplicates:&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The second step was to delete identical rows or entries that were recorded more than once. I achieved this by:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Selecting the entire spreadsheet using &lt;code&gt;Ctrl+A&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;navigating to the Data Tab and Clicking Remove Duplicates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirming the target columns and clicking OK&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h4&gt;
  
  
  &lt;strong&gt;Fixing structural errors&lt;/strong&gt;:
&lt;/h4&gt;

&lt;p&gt;This step involved correcting typos, standardizing inconsistent capitalization (e.g., changing "Nairobi", "nairobi", and "NAIROBI" into one uniform format), merging mislabeled categories and trimming extra spaces in the text columns. Certain categories like employer ID also had inconsistent formatting. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standardizing Codes:&lt;/strong&gt; &lt;br&gt;
Employer ID inconsistent formatting was remedied by selecting the column, then applying an auto filter using &lt;code&gt;Shift+Ctrl+L&lt;/code&gt; and checking the Drop down to see the range of values I had. Then I selected the Column, and &lt;code&gt;Ctrl+H&lt;/code&gt; to &lt;strong&gt;find and replace&lt;/strong&gt; and replaced all &lt;code&gt;EMP-&lt;/code&gt; with a Blank to fix the formatting.&lt;/p&gt;

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

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trimming Spaces and fixing Capitalization:&lt;/strong&gt; 
&lt;strong&gt;TRIM()&lt;/strong&gt;&amp;nbsp;removes extra spaces between words. 
Name Entries were fixed by using &lt;code&gt;=PROPER(TRIM(TEXT))&lt;/code&gt; on the first and last name columns for both text length and proper case and then replacing the names in both columns using Paste (Values)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Handling missing values:&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Deciding whether to filter out empty cells, fill them with zeroes, or input an average value. &lt;br&gt;
Empty/missing values in the remaining text and numerical Columns were replaced by &lt;code&gt;unknown&lt;/code&gt; converting them into unknown values using the &lt;code&gt;Ctrl+H&lt;/code&gt; find and replace feature. &lt;/p&gt;

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

&lt;h4&gt;
  
  
  &lt;strong&gt;Standardizing formats:&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Ensuring all dates followed a consistent format (e.g., &lt;code&gt;YYYY-MM-DD&lt;/code&gt;) and numbers are converted from plain text into actual numeric formats.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Salary &amp;amp; Bonuses:&lt;/strong&gt; Highlighted the numeric columns and pressed &lt;strong&gt;Ctrl + 1&lt;/strong&gt; to set the format to &lt;strong&gt;Currency (Ksh)&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fye1utosqh4qwuga72cqx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fye1utosqh4qwuga72cqx.png" alt="Alt text" width="542" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dates:&lt;/strong&gt; Selected the date column and set the formatting to &lt;code&gt;DD/MM/YYYY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;To verify that we had successfully cleaned and validated our data, we applied auto filters &lt;code&gt;Ctrl + Shift + L&lt;/code&gt; to the entire Spreadsheet and sorted each column to confirm that our categories were uniform, and our numbers were formatted correctly and whether we had any lingering errors. We then had a 'clean' dataset that was ready for analysis. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>datascience</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Understanding the Git Workflow: Working Directory, Staging, Commit and Push.</title>
      <dc:creator>Njuguna Gichia</dc:creator>
      <pubDate>Sat, 22 Aug 2026 20:20:07 +0000</pubDate>
      <link>https://dev.to/9ichia/understanding-the-git-workflow-working-directory-staging-commit-and-push-226i</link>
      <guid>https://dev.to/9ichia/understanding-the-git-workflow-working-directory-staging-commit-and-push-226i</guid>
      <description>&lt;p&gt;Two weeks ago, I joined a Data Science bootcamp and part of the learning outcomes for this week was independently navigating the Git Workflow to successfully upload a test project as part of the data portfolio to a GitHub repository. &lt;/p&gt;

&lt;p&gt;Git is a version control system that runs locally on a personal computer and allows us to keep track of changes made to our projects. Git creates saved checkpoints knowns as commits that act as snapshots of the state of the projects. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; is an online service where we can store Git repositories. While Git is installed and works locally on our personal computers, GitHub stores our repositories online and allows us to share and collaborate on projects &lt;/p&gt;

&lt;p&gt;My project was a custom energy consumption segmentation analysis using k means. Other than local python, this project required additional installation of &lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt; for data processing and manipulation, &lt;code&gt;scikit-learn&lt;/code&gt; for preprocessing, and &lt;code&gt;matplotlib&lt;/code&gt; and &lt;code&gt;seaborn&lt;/code&gt; for the data visualization. &lt;/p&gt;

&lt;p&gt;The Git workflow happened in 4 distinct stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Working Directory.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The working directory is the folder containing my project files where the terminal or Git is currently working from. These include: &lt;br&gt;
    - &lt;strong&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/strong&gt; file which is a description of the entire project written in raw markdown. It contains information about the project such as:&lt;br&gt;
        - What the project is about&lt;br&gt;
        - What dataset was used&lt;br&gt;
        - The tools used &lt;br&gt;
        - The analysis was performed&lt;br&gt;
        - Contents of each folder&lt;br&gt;
        - Important findings&lt;br&gt;
        - How another person can understand the project&lt;br&gt;
    - &lt;strong&gt;&lt;code&gt;Data&lt;/code&gt;&lt;/strong&gt; Folder which contains an excel spreadsheet named &lt;code&gt;energy_consumption_data.csv&lt;/code&gt; that contained the consumer data.&lt;br&gt;
    - &lt;strong&gt;&lt;code&gt;Scripts&lt;/code&gt;&lt;/strong&gt; Folder which contains a K means python script named &lt;code&gt;segmentation.py&lt;/code&gt;. Python script files typically end with the &lt;code&gt;.py&lt;/code&gt; extension.&lt;/p&gt;

&lt;p&gt;To create a working directory using Git Bash, I opened Gitbash on Desktop by clicking on the windows icon on the taskbar, typing in &lt;code&gt;bash&lt;/code&gt; and clicking on Gitbash to open a new window. Once the window was open, I ran &lt;strong&gt;&lt;code&gt;cd Desktop&lt;/code&gt;&lt;/strong&gt;, to navigate to my Desktop where I created a new Folder Titled &lt;code&gt;energy_consumption_analysis&lt;/code&gt; by running the command &lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir energy_customer_data_analysis&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;The command &lt;code&gt;cd&lt;/code&gt; means change directory and it allows you to navigate in and out of directories. &lt;code&gt;cd&lt;/code&gt; allows you to enter a folder while &lt;strong&gt;&lt;code&gt;cd ..&lt;/code&gt;&lt;/strong&gt; allows you to move backward by exiting a directory or a folder into the parent directory. Inside &lt;code&gt;energy_customer_data_analysis&lt;/code&gt;, I created two folders &lt;code&gt;Data&lt;/code&gt; and &lt;code&gt;Scripts&lt;/code&gt; simultaneously by running the &lt;strong&gt;&lt;code&gt;mkdir&lt;/code&gt;&lt;/strong&gt; command which allows you to create 2 folder simultaneously by listing two folder names separated by a space: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir Data Scripts&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;command created two folders named &lt;code&gt;Data&lt;/code&gt; and &lt;code&gt;Scripts&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I also created the &lt;code&gt;README.md&lt;/code&gt; file by running &lt;strong&gt;&lt;code&gt;touch README.md&lt;/code&gt;&lt;/strong&gt; in bash.&lt;/p&gt;

&lt;p&gt;to make sure that I had created all the files and folders, I ran the &lt;strong&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/strong&gt; command in bash which allowed me to see all the files and folders created in the working directory.&lt;/p&gt;

&lt;p&gt;I copied the CSV file from my downloads into the &lt;code&gt;Data&lt;/code&gt; folder then used bash to navigate into the &lt;code&gt;Scripts&lt;/code&gt; folder by running &lt;strong&gt;&lt;code&gt;cd Scripts&lt;/code&gt;&lt;/strong&gt; since I was still inside my project folder. once inside the Scripts folder, I ran &lt;strong&gt;&lt;code&gt;touch segmentation.py&lt;/code&gt;&lt;/strong&gt; to create the python script file and then ran &lt;strong&gt;&lt;code&gt;code .&lt;/code&gt;&lt;/strong&gt; in bash to then open &lt;code&gt;segmentation.py&lt;/code&gt; in VS Code.  &lt;/p&gt;

&lt;p&gt;After writing the python code in VS Code, I went back to bash and ran &lt;code&gt;cd ..&lt;/code&gt; to exit the scripts folder and ran &lt;code&gt;nano README.md&lt;/code&gt; to open the README file in Bash's text editor &lt;code&gt;nano&lt;/code&gt; where i wrote the description for the project in Markdown. Once this was complete, &lt;code&gt;Ctrl + O&lt;/code&gt;, then &lt;code&gt;Enter&lt;/code&gt; to save then &lt;code&gt;Ctrl + X&lt;/code&gt; to go back to bash. To see what's inside the README file, use &lt;code&gt;cat README.md&lt;/code&gt; in bash to display text inside the &lt;code&gt;README.md&lt;/code&gt; file and &lt;code&gt;ls&lt;/code&gt; to inspect the project folder. Once satisfied with everything, we then move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Staging Area.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The staging area is the preparation area for the next commit. Here, we Initialize Git, check changes and stage files. Before running any git commands, we check our location by running &lt;strong&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/strong&gt; . &lt;code&gt;Pwd&lt;/code&gt; means print working directory and allows us to see the exact location we are working from. &lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize git
&lt;/h3&gt;

&lt;p&gt;Once we ascertain that we are in the correct project folder, we can initialize git by running the command &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;which instructs Git to begin managing the project. When run successfully, we should see : `Initialized empty Git repository in (the working directory path)&lt;/p&gt;

&lt;p&gt;We also run &lt;strong&gt;&lt;code&gt;ls -la&lt;/code&gt;&lt;/strong&gt; which allows us to see a &lt;code&gt;.git&lt;/code&gt; folder which acts as Git's internal memory for the project. &lt;/p&gt;

&lt;h3&gt;
  
  
  Check Project Status
&lt;/h3&gt;

&lt;p&gt;We check project status by running &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git status&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;This allows git to show us exactly what is happening in our project and because this is a completely new project and repository that git has not started tracking, our project status is untracked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Staging
&lt;/h3&gt;

&lt;p&gt;We can add individual files in our directory by running &lt;code&gt;git add &amp;lt;filename&amp;gt;&lt;/code&gt; to select the individual file or we can run &lt;strong&gt;&lt;code&gt;git add .&lt;/code&gt;&lt;/strong&gt; to add everything in the current directory. This moves the selected files from the working directory into the staging area. &lt;/p&gt;

&lt;p&gt;We can check the status of our project by running &lt;code&gt;git status&lt;/code&gt; and if the staging is successful, our selected files and folders should be listed in green. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Commit.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We create the first commit by running &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git commit -m "Add Energy Consumption Segmentation Analysis Project"&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git commit&lt;/code&gt; creates a saved Git checkpoint while &lt;code&gt;-m&lt;/code&gt; means the message we want to attach to the commit which in this case is &lt;code&gt;"Add Energy Consumption Segmentation Analysis Project"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The message explains what was saved. A good commit message creates an understandable history of the project. &lt;/p&gt;

&lt;h3&gt;
  
  
  Commit History
&lt;/h3&gt;

&lt;p&gt;We can view the commit history by running &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;which shows us the commit history or by running:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git log --oneline&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;for a much shorter commit history. &lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Github Repository.
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;We can create a new repository in GitHub through our browser.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Github.com&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Sign in.&lt;/li&gt;
&lt;li&gt;Once Signed in Click the &lt;code&gt;+&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select: &lt;strong&gt;New repository&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter &lt;strong&gt;Repository Name(e.g.,&lt;a href="https://github.com/njugunagichia/Energy-Consumer-Data-Segmentation-Analysis" rel="noopener noreferrer"&gt; &lt;code&gt;energy_consumer_data_segmentation_analysis&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;You can also Add an optional description such as:(&lt;code&gt;energy_consumer_data_analysis_using_Python&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Choose between a &lt;strong&gt;Public&lt;/strong&gt; or &lt;strong&gt;Private&lt;/strong&gt; Repository (keep in mind that a public repository can generally be viewed by anyone and should not be used for sensitive or confidential data. In our case, the data is synthetic and non-specific so a public repository is fine.) &lt;/li&gt;
&lt;li&gt;Leave options such as: &lt;strong&gt;Add README&lt;/strong&gt;, &lt;strong&gt;Add .gitignore&lt;/strong&gt;, and &lt;strong&gt;Add license unchecked.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Create New Repository.&lt;/strong&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After creating the repository, navigate to the connection options, Select &lt;strong&gt;SSH&lt;/strong&gt; if you have it already configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Push.&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Pushing means sending committed changes on our projects to GitHub.&lt;/p&gt;

&lt;p&gt;After copying the SSH string, return to Git Bash. To check whether we are still in the project, run &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If still in the folder, run: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git remote add origin git@github.com:USERNAME/project-name.git&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;USERNAME&lt;/code&gt;&lt;/strong&gt;: is your GitHub Username&lt;br&gt;
&lt;strong&gt;&lt;code&gt;Project-name.git&lt;/code&gt;&lt;/strong&gt;: is the name of the repository you've just created.&lt;/p&gt;

&lt;p&gt;type &lt;strong&gt;&lt;code&gt;git remote add origin&lt;/code&gt;&lt;/strong&gt; and then press &lt;code&gt;Shift + Insert&lt;/code&gt; on your keyboard to paste the SSH string  you've just copied from GitHub to complete the command and then run it. &lt;/p&gt;

&lt;h3&gt;
  
  
  Checking the Remote.
&lt;/h3&gt;

&lt;p&gt;A remote is a connection between our local repository and another repository which is in our case, the one on GitHub. &lt;/p&gt;

&lt;p&gt;We check the remote by running: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git remote -v&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;origin git@github.com:USERNAME/project-name.git (fetch)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;means that Git can retrieve information from the remote repository&lt;/p&gt;

&lt;p&gt;&lt;code&gt;origin git@github.com:USERNAME/project-name.git (push)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;means that git can send commits to the remote Repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pushing the Code
&lt;/h3&gt;

&lt;p&gt;Pushing means sending the committed changes in my project to the Remote Git Repository.&lt;/p&gt;

&lt;p&gt;we do this by running: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git push -u origin main&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Bash will prompt you to enter the configured passphrase. if you have one, type it in and then &lt;strong&gt;Enter&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying the Push
&lt;/h3&gt;

&lt;p&gt;Return to the repository page on your browser and refresh it. &lt;br&gt;
GitHub should render the contents in your &lt;code&gt;README.md&lt;/code&gt; file visually.&lt;br&gt;
You should also be in a position to see your files and the contents in those files (i.e the CSV file in the &lt;code&gt;Data&lt;/code&gt; folder and the &lt;code&gt;.py&lt;/code&gt; file in the &lt;code&gt;Scripts&lt;/code&gt; folder).&lt;/p&gt;

&lt;p&gt;If we can see this, then our project has successfully been pushed to GitHub.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>python</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
