<?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: Cynthia Sophie Mwangi</title>
    <description>The latest articles on DEV Community by Cynthia Sophie Mwangi (@sophiemwangi).</description>
    <link>https://dev.to/sophiemwangi</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%2F3812673%2Fba38f3c7-aa8b-4792-927f-b1e3ea605ddd.png</url>
      <title>DEV Community: Cynthia Sophie Mwangi</title>
      <link>https://dev.to/sophiemwangi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sophiemwangi"/>
    <language>en</language>
    <item>
      <title>Recursive CTEs, explained from first principles</title>
      <dc:creator>Cynthia Sophie Mwangi</dc:creator>
      <pubDate>Fri, 04 Sep 2026 13:17:17 +0000</pubDate>
      <link>https://dev.to/sophiemwangi/recursive-ctes-explained-from-first-principles-295c</link>
      <guid>https://dev.to/sophiemwangi/recursive-ctes-explained-from-first-principles-295c</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxh6obdp9znqaj1cix5sl.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%2Fxh6obdp9znqaj1cix5sl.png" alt=" " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some data isn't flat. An employee has a manager, who has a manager, who has a manager, all the way up to the boss at the top. A comment can be a reply to a reply to a reply. A folder can sit inside a folder inside a folder. This kind of data, where things are connected in layers or "levels," is called hierarchical data. Think of a family tree, or the folders on your computer. Each item points up (or down) to another item of the same kind.&lt;/p&gt;

&lt;p&gt;The tricky part: you never know ahead of time how many layers deep the chain goes. One employee might be 2 levels from the top, another might be 6. A recursive CTE is the tool SQL gives you to handle exactly this. A query that keeps digging one layer deeper, automatically, until it runs out of layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, what's a CTE?
&lt;/h2&gt;

&lt;p&gt;CTE stands for &lt;strong&gt;Common Table Expression&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Don't let the name intimidate you, all it means is: "give this block of query a name, so I can use it again further down, later in my script." You write it with the keyword &lt;strong&gt;WITH&lt;/strong&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;recent_orders&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2026-01-01'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;recent_orders&lt;/span&gt; &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, recent_orders isn't a real table sitting in your database. It's a temporary result, given a name, that only exists for this one query. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;why not just write a normal query?&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;A CTE is really just a nicer way of writing something you could squeeze into one messy query anyway; it just keeps things readable by breaking the logic into named, ordered steps instead of one big nested blob.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A recursive CTE takes this one step further: the named result (from the CTE) is allowed to refer to itself. That's the entire trick behind it and it's the only way a query can "&lt;strong&gt;loop&lt;/strong&gt;" over hierarchical data of unknown depth.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Why a normal query can't do this&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Picture a simple company organization chart: the CEO, and everyone below them, layer by layer.&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%2Fcq5rlbme97dzx2enl68h.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%2Fcq5rlbme97dzx2enl68h.png" alt=" " width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a hierarchy: everyone connects upward to one person, layer by layer, and you don't know how deep it goes until you get there.&lt;/p&gt;

&lt;p&gt;A "normal" query can only join a table to itself a fixed number of times. So if you wanted everyone under the CEO with plain joins, you'd have to write something like: join employees to their manager, then join that result to the next manager up, then join that to the next one and stop wherever you guessed the chart ends. If someone gets hired 7 levels deep next year, your query silently misses them.&lt;/p&gt;

&lt;p&gt;A recursive CTE removes the guesswork. You tell it where to start and how to take one step outward and it keeps repeating that one step, on its own, until there's nobody left to find.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;The two parts of a recursive CTE&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every recursive CTE is built from two pieces, glued together with UNION ALL (which just means "stack these two result sets on top of each other"):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The anchor&lt;/strong&gt; -&amp;gt; this runs once, and only once. It's your starting point: "give me the person with no manager" (the CEO), or "give me the top-level category."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The recursive part&lt;/strong&gt; -&amp;gt; this is the piece that repeats. Each time it runs, it looks for "who connects to the people I just found," and it keeps doing that, again and again, until a round comes back completely empty.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fraepbxm1krvxylgxt15o.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%2Fraepbxm1krvxylgxt15o.png" alt=" " width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the org chart example written out as SQL. It maps directly onto the two pieces above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;RECURSIVE&lt;/span&gt; &lt;span class="n"&gt;employee_chain&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;-- THE ANCHOR: find the one person with no manager (the CEO)&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;

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

  &lt;span class="c1"&gt;-- THE RECURSIVE PART: find everyone whose manager we just found&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
  &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;employee_chain&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;

&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employee_chain&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;&lt;strong&gt;a note on syntax&lt;/strong&gt;&lt;br&gt;
WITH RECURSIVE is what Postgres and SQLite expect. MySQL (version 8 and up) and SQL Server are happy with just WITH — no extra word needed. Small detail, but it trips people up.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;You don't need to think of this as clever. It's just "keep repeating one step", automated.&lt;/strong&gt;
&lt;/h4&gt;
&lt;h2&gt;
  
  
  Watching it run, step by step
&lt;/h2&gt;

&lt;p&gt;The easiest way to actually understand this is to stop thinking of it as one magic statement, and picture the database running it in rounds:&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%2F35vz69eahh6wcgm9xhup.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%2F35vz69eahh6wcgm9xhup.png" alt=" " width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database is just doing the "join, join, join" you'd otherwise write by hand, and it's smart enough to know when to stop.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;The one thing to be careful of:&lt;/strong&gt;&lt;br&gt;
If your data has a loop in it by mistake say, an employee who is (accidentally) listed as their own manager's manager, the recursive part will never come back empty, and the query will run forever. Most databases let you set a safety limit: SQL Server has OPTION (MAXRECURSION n), and elsewhere you can add your own cutoff, like WHERE ec.level &amp;lt; 20, inside the recursive part.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Is there such a thing as a "recursive subquery"?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You'll hear this phrase sometimes, but strictly speaking, no, and understanding why makes the whole topic click better. &lt;/p&gt;

&lt;p&gt;A subquery is a &lt;strong&gt;query with no name&lt;/strong&gt;, tucked inside another query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Jane'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recursion needs something to call back to by name - and an unnamed query has nothing to point at. A CTE gets a name the second you write it, and that name is exactly what the recursive part calls. No name, no recursion. That's really the entire difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What people usually actually mean&lt;/strong&gt;
&lt;/h3&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%2Fr43vs1w1dwe71tely80z.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%2Fr43vs1w1dwe71tely80z.png" alt=" " width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Oracle's older way: CONNECT BY&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before recursive CTEs existed in the SQL standard, Oracle already had its own way of walking a hierarchy: &lt;strong&gt;CONNECT BY&lt;/strong&gt;, paired with a built-in "column" called &lt;strong&gt;LEVEL&lt;/strong&gt;. It solves the exact same problem, it's just written as a subquery tacked onto a normal SELECT, instead of a self-referencing WITH block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;
&lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;CONNECT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;PRIOR&lt;/span&gt; &lt;span class="n"&gt;employee_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;manager_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flw4qdjq1pr6eu52x4bim.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%2Flw4qdjq1pr6eu52x4bim.png" alt=" " width="753" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CONNECT BY looks like one plain SELECT statement, which is probably why people call it a "recursive subquery" but it's doing the same job as a recursive CTE.&lt;/p&gt;

&lt;p&gt;One nice thing Oracle gives you for free here: LEVEL automatically tells you how deep you are in the hierarchy. In a recursive CTE, you have to build that counter yourself, like we did with level + 1 earlier.&lt;/p&gt;

&lt;p&gt;CONNECT BY only works in Oracle. Modern Oracle also supports the standard WITH RECURSIVE CTE and most new projects use that instead, since it's the version that works, unchanged, on Postgres, SQL Server, MySQL 8+, and SQLite too.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The one idea to hold onto&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A recursive CTE isn't really a special trick. It's a pattern with a name. &lt;/p&gt;

&lt;p&gt;Pick a starting point. Define one step that reaches one layer further out. Let the database repeat that step on its own, until nothing new is left to find.&lt;br&gt;
Once that clicks, every hierarchy: org charts, comment threads, category trees, folders inside folders, starts looking like the same problem, just wearing a different table name.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>cte</category>
      <category>dataengineering</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Data Modeling in Power BI: Joins, Relationships, and Schemas Explained</title>
      <dc:creator>Cynthia Sophie Mwangi</dc:creator>
      <pubDate>Sat, 09 May 2026 08:01:22 +0000</pubDate>
      <link>https://dev.to/sophiemwangi/data-modeling-in-power-bi-joins-relationships-and-schemas-explained-285c</link>
      <guid>https://dev.to/sophiemwangi/data-modeling-in-power-bi-joins-relationships-and-schemas-explained-285c</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Introduction&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Data Modeling, the beginning of making sense out of data. This is one of the most important steps to dealing with your data. This means that your most complex and detailed dashboards will not be appreciated without clean and well done data modeling.&lt;/p&gt;

&lt;p&gt;In this guide we will go through&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Data Modeling&lt;/li&gt;
&lt;li&gt;All SQL joins&lt;/li&gt;
&lt;li&gt;Power BI relationships and how they differ from joins&lt;/li&gt;
&lt;li&gt;Fact and Dimension tables&lt;/li&gt;
&lt;li&gt;Data Modeling Schemas&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;What is Data Modeling&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Data modeling refers to the process of structuring and organizing data so that it can be analyzed effectively. Think of this as cleaning a messy room and putting everything where it belongs: all books in the shelves, clothes on your hangers and shoes in the shoe-rack. &lt;/p&gt;

&lt;p&gt;In Power BI this means: defining tour tables, creating relationships and optimizing performance and accuracy.&lt;/p&gt;

&lt;p&gt;Think of it like designing a map: if roads (relationships) are wrong, you’ll never reach the right destination (insights).&lt;/p&gt;

&lt;h4&gt;
  
  
  SQL Joins
&lt;/h4&gt;

&lt;p&gt;When working with data, it’s rare to find everything you need in one table. In real-world databases, data is split into multiple tables to keep things organized and efficient.&lt;/p&gt;

&lt;p&gt;That’s where SQL joins come in.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;SQL join&lt;/strong&gt; is used to combine data from two or more tables based on a related column (usually a key).&lt;/p&gt;

&lt;p&gt;Imagine analyzing a business given these tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Products table that tells you what is sold&lt;/li&gt;
&lt;li&gt;A Customers table that tells you who is buying&lt;/li&gt;
&lt;li&gt;A Sales table that records transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the Sales table only contains IDs(product id and customer id), not full details (like customer name and product name/product price).&lt;/p&gt;

&lt;p&gt;So if you want to answer: “Who bought what?” You must &lt;strong&gt;join&lt;/strong&gt; the tables.&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;Types of SQL Joins&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;Let’s break them down. Shall we?&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;strong&gt;1. INNER JOIN (Only Matching Data)&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;This join is used when you want to find only records that exist in both tables, in other terms &lt;em&gt;matching rows&lt;/em&gt;&lt;/p&gt;

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

&lt;h5&gt;
  
  
  &lt;strong&gt;LEFT JOIN (Keep Everything from the Left Table)&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;Also known as the left outer join.&lt;br&gt;
This join is used when you want to return only records in your left table that also exist in your right table. &lt;em&gt;“Show me everything on the left, even if there’s no match in the right.”&lt;/em&gt;&lt;/p&gt;

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

&lt;h5&gt;
  
  
  &lt;strong&gt;RIGHT JOIN (Keep Everything from the Right Table)&lt;/strong&gt;
&lt;/h5&gt;

&lt;p&gt;Also known as the right outer join.&lt;br&gt;
This join is the polar opposite of the left join. It is used when you want to return everything in the right table that also exist in the left table.&lt;/p&gt;

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

&lt;p&gt;Joins are relating different tables using a related column.&lt;br&gt;
Say you have a table on sales - that has different products sold. One on products - with a list of the products available for sale and another on customers - detailing the unique attributes of your customers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faufg59e7b7mhx6crc6dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faufg59e7b7mhx6crc6dw.png" alt=" " width="598" height="262"&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.amazonaws.com%2Fuploads%2Farticles%2Fpjoi4hhyhslxekroq46b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjoi4hhyhslxekroq46b.png" alt=" " width="450" height="173"&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.amazonaws.com%2Fuploads%2Farticles%2Fyq39nhbn5sdb6xxudzbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyq39nhbn5sdb6xxudzbf.png" alt=" " width="681" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To relate the tables (get more information not available in our main table -sales) we use the primary key marked(PK) from our dimension tables (customers and products) and find the matching input in the foreign key columns in the fact table (sales)&lt;/p&gt;

&lt;p&gt;The process of ralting these table requi&lt;/p&gt;

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

</description>
      <category>database</category>
      <category>dataengineering</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
