<?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: Esther Njihia</title>
    <description>The latest articles on DEV Community by Esther Njihia (@esther_njihia).</description>
    <link>https://dev.to/esther_njihia</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%2F815150%2F8788c6d0-cb4d-4e01-a9ee-826c4c7a532a.png</url>
      <title>DEV Community: Esther Njihia</title>
      <link>https://dev.to/esther_njihia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/esther_njihia"/>
    <language>en</language>
    <item>
      <title>Power BI Data Modeling: Relationships, Star Schema, Fact and Dimension Tables, and Power Query Joins</title>
      <dc:creator>Esther Njihia</dc:creator>
      <pubDate>Sun, 13 Sep 2026 13:31:21 +0000</pubDate>
      <link>https://dev.to/esther_njihia/power-bi-data-modeling-relationships-star-schema-fact-and-dimension-tables-and-power-query-joins-1iff</link>
      <guid>https://dev.to/esther_njihia/power-bi-data-modeling-relationships-star-schema-fact-and-dimension-tables-and-power-query-joins-1iff</guid>
      <description>&lt;h2&gt;
  
  
  The problem before the visual
&lt;/h2&gt;

&lt;p&gt;A Power BI dashboard can look impressive, but the model underneath can be complete chaos. The visual is what the user sees, but its accuracy and flexibility depend on how the data is structured before reporting begins.&lt;/p&gt;

&lt;p&gt;Consider a Premier League dataset, the business process being analysed is the Premier League match intended to answer questions such as: Which teams perform better at home? Which teams struggle away? Do match statistics change during different periods of the season? Are there patterns in fouls, cards, or referee decisions?&lt;/p&gt;

&lt;p&gt;All of these questions can be answered with visuals, but the more important question comes first:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How should the data be structured so that the same model can support different reports and analytical questions?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where data modelling begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is data modelling?
&lt;/h2&gt;

&lt;p&gt;Data modelling is the process of organising data into logical structures and defining how those structures relate to each other.&lt;/p&gt;

&lt;p&gt;Source data is not always arranged in the best way for analysis. It is usually structured according to how it was collected, exported, or stored. Data modelling looks beyond that original structure and identifies the events in the data, the information that describes those events, and the relationships required to analyse them correctly.&lt;/p&gt;

&lt;p&gt;A good model therefore does more than separate columns into different tables. It creates a structure that Power BI can use efficiently for reporting, filtering and calculations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does data modelling matter?
&lt;/h3&gt;

&lt;p&gt;As more information is added to a report, a poorly structured model becomes harder to maintain. Descriptive values can be repeated unnecessarily, tables can become increasingly wide, and calculations may begin carrying logic that should have been handled by the model itself.&lt;/p&gt;

&lt;p&gt;Performance is another consideration. Power BI needs to load, compress, scan, and filter the model before producing results. Unnecessary duplication and complicated relationship paths can make that work harder.&lt;/p&gt;

&lt;p&gt;The model also affects DAX. When relationships and table responsibilities are clear, calculations can use the existing model structure. When the structure is poor, DAX often has to compensate for missing or confusing modelling logic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Golden rule:&lt;/strong&gt; A bad data model can lead to bad and complicated DAX.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maintainability is equally important. A model should make sense when another analyst opens it later. The purpose of each table, the central business event, and the main relationship paths should be understandable without having to reverse-engineer the entire report.&lt;/p&gt;

&lt;p&gt;A useful modelling question is therefore:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can this model serve multiple reports?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Parting shot
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without deliberate modelling&lt;/th&gt;
&lt;th&gt;With a deliberate data model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Information can be repeatedly stored&lt;/td&gt;
&lt;td&gt;Repetition can be reduced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporting logic becomes harder to follow&lt;/td&gt;
&lt;td&gt;Tables have clearer responsibilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAX may compensate for structural problems&lt;/td&gt;
&lt;td&gt;DAX can use the model structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Growth can increase complexity quickly&lt;/td&gt;
&lt;td&gt;The model is easier to extend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Harder to maintain&lt;/td&gt;
&lt;td&gt;Easier to understand and maintain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Often designed around one report&lt;/td&gt;
&lt;td&gt;Can support multiple reporting questions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Fact tables, dimension tables and grain
&lt;/h2&gt;

&lt;p&gt;In dimensional modelling, tables normally serve different purposes. Two of the most important roles are the &lt;strong&gt;fact table&lt;/strong&gt; and the &lt;strong&gt;dimension table&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;fact table&lt;/strong&gt; stores the business events, transactions, or activities being analysed. It normally contains measurements together with keys that connect those events to descriptive information.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;dimension table&lt;/strong&gt; stores the descriptive information that gives context to those events. Dimensions are commonly used for filtering, grouping, and labelling data in reports.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension table&lt;/th&gt;
&lt;th&gt;Fact table&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Describes the event&lt;/td&gt;
&lt;td&gt;Records the event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains descriptive attributes&lt;/td&gt;
&lt;td&gt;Contains events and measurements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commonly used for filtering and grouping&lt;/td&gt;
&lt;td&gt;Commonly used for calculations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usually contains unique members&lt;/td&gt;
&lt;td&gt;Foreign keys can repeat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gives context to the numbers&lt;/td&gt;
&lt;td&gt;Contains the numbers being analysed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before deciding what belongs in the fact table, the &lt;strong&gt;grain&lt;/strong&gt; must be established.&lt;/p&gt;

&lt;p&gt;Grain describes the level of detail represented by one row of the fact table. This matters because every measure is interpreted at that level. A table where one row represents an individual event behaves very differently from one where each row represents a monthly summary.&lt;/p&gt;

&lt;p&gt;The simplest way to identify grain is to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What does one row represent?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That answer should be clear enough to state in one sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to organise data: Flat, Star and Snowflake
&lt;/h2&gt;

&lt;p&gt;Once the roles of the tables are understood, the next question is how those tables should be arranged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flat schema
&lt;/h3&gt;

&lt;p&gt;A flat schema keeps descriptive information and measurements together in one large table.&lt;/p&gt;

&lt;p&gt;Its main advantage is simplicity. There are fewer tables to manage, no relationships are required, and the structure can be convenient for a small or temporary analysis.&lt;/p&gt;

&lt;p&gt;The problem appears as the data grows. Descriptive values are repeated across many rows, redundancy increases, and the table can become increasingly difficult to maintain. Calculations may also become more complicated because there is no clear separation between descriptive attributes and measurable events.&lt;/p&gt;

&lt;p&gt;A flat schema is therefore not automatically wrong. It can be appropriate when the analysis is small and unlikely to grow. The problem is using the same structure for a model that needs to scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Star schema
&lt;/h3&gt;

&lt;p&gt;A star schema separates the business event from the descriptive information surrounding it. The fact table sits in the centre while dimension tables connect directly to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 DimDate
                    |
                    |
DimTable ------- FactTable ------- DimTable
                    |
                    |
                 DimTable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The central fact table stores events and measurements while dimensions provide the context required to analyse them.&lt;/p&gt;

&lt;p&gt;One of the main advantages of a star schema is that the filter paths are relatively easy to understand. A selection from a dimension can travel through the relationship to the fact table, where the relevant measures are calculated.&lt;/p&gt;

&lt;p&gt;This structure can also make DAX easier to follow because the model already separates descriptive attributes from business events.&lt;/p&gt;

&lt;p&gt;The trade-off is that real-world data does not always form a perfect star. A dimension may need to play more than one role, or the analytical problem may contain relationships that require additional handling. A star schema provides a clear structure, but it still has to reflect the actual problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snowflake schema
&lt;/h3&gt;

&lt;p&gt;A snowflake schema begins with the same fact-and-dimension structure but normalises one or more dimensions into additional related tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 DimDate
                    |
                    |
DimTable ------- FactTable ------- DimTable
   |
   |
SubDimension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can reduce repetition inside large dimensions by moving repeated descriptive information into separate tables.&lt;/p&gt;

&lt;p&gt;The trade-off is additional complexity. More tables create more relationships, and filters may need to travel through several tables before reaching the fact table. This can make the model harder to read, maintain, and troubleshoot.&lt;/p&gt;

&lt;p&gt;Snowflaking therefore needs a reason. Reduced redundancy should provide enough value to justify the additional relationship complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we choose a schema?
&lt;/h2&gt;

&lt;p&gt;Knowing that flat, star, and snowflake schemas exist is not enough. The model still needs to be selected according to the analytical problem.&lt;/p&gt;

&lt;p&gt;Star schema may be widely recommended, but that alone is not a sufficient reason to use it.&lt;/p&gt;

&lt;p&gt;Instead, the model can be evaluated against questions such as:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consideration&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grain&lt;/td&gt;
&lt;td&gt;What does one row represent?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;What questions must the model answer?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facts&lt;/td&gt;
&lt;td&gt;What events or numbers are being measured?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dimensions&lt;/td&gt;
&lt;td&gt;What describes those events?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relationships&lt;/td&gt;
&lt;td&gt;How complicated will the connections become?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAX&lt;/td&gt;
&lt;td&gt;Will calculations remain understandable?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Is there unnecessary duplication or relationship complexity?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintainability&lt;/td&gt;
&lt;td&gt;Can another person understand the model easily?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Can more data be added later?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporting&lt;/td&gt;
&lt;td&gt;Can users filter and analyse the data easily?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These questions make the schema choice a design decision rather than a rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying the concepts to the Premier League data
&lt;/h2&gt;

&lt;p&gt;The Premier League dataset used in this project contains match information such as date, home and away teams, referee, goals, results, shots, shots on target, fouls, corners and cards.&lt;/p&gt;

&lt;p&gt;Before creating tables, the grain needs to be established.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One row represents one Premier League match.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes the match the central business event.&lt;/p&gt;

&lt;h3&gt;
  
  
  FactMatches
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;FactMatches&lt;/code&gt; records the match itself together with measurable statistics such as goals, shots, shots on target, fouls, corners, cards, and results.&lt;/p&gt;

&lt;h3&gt;
  
  
  DimTeam
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DimTeam&lt;/code&gt; contains descriptive information about the teams. This allows teams to be used consistently for filtering and grouping while the match events remain in the fact table.&lt;/p&gt;

&lt;h3&gt;
  
  
  DimDate
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DimDate&lt;/code&gt; describes when matches occurred. It can contain attributes such as day, month, quarter, and year, which can then be reused across different reports.&lt;/p&gt;

&lt;h3&gt;
  
  
  DimReferee
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DimReferee&lt;/code&gt; describes the referees associated with the matches. This provides a clear location for referee information and allows matches to be analysed or filtered by referee.&lt;/p&gt;

&lt;p&gt;The resulting structure begins to resemble a star schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 DimDate
                    |
                    |
DimTeam -------- FactMatches -------- DimReferee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this stage, however, separate tables alone do not create a complete model. Power BI still needs a reliable way to determine how records from one table correspond to records in another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relationships in Power BI
&lt;/h2&gt;

&lt;p&gt;A relationship defines how records in one table correspond to records in another.&lt;/p&gt;

&lt;p&gt;Relationships are necessary when information has been distributed across several tables. They allow those tables to remain separate while still interacting during filtering and analysis.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;DimTeam&lt;/code&gt; contains descriptive information about a team while &lt;code&gt;FactMatches&lt;/code&gt; records the matches involving that team. The relationship tells Power BI how those records correspond.&lt;/p&gt;

&lt;p&gt;Relationships therefore affect much more than the appearance of Model View. They influence filter propagation, calculation context, and how data from different tables can be analysed together.&lt;/p&gt;

&lt;p&gt;Before relationships can be created correctly, the tables need suitable keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keys
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;primary key&lt;/strong&gt; uniquely identifies a record in a table.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;foreign key&lt;/strong&gt; exists in another table and refers back to that record. The foreign key can repeat because the same entity may participate in many events.&lt;/p&gt;

&lt;p&gt;The important point is that a relationship should not be created simply because two columns have similar names. The values must support the intended relationship.&lt;/p&gt;

&lt;p&gt;For a normal one-to-many relationship:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the key on the one side should be unique;&lt;/li&gt;
&lt;li&gt;the corresponding foreign key can repeat on the many side;&lt;/li&gt;
&lt;li&gt;the values on the many side should have valid matches on the one side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What makes a column suitable for a relationship?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer depends on the meaning of the column, its uniqueness, and whether the values correctly match across the tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying keys to the football model
&lt;/h2&gt;

&lt;p&gt;The Premier League source contains team and referee names but does not provide ready-made model IDs such as &lt;code&gt;TeamID&lt;/code&gt; or &lt;code&gt;RefereeID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This does not prevent dimensional modelling. Once the entities have been identified, model keys can be created to provide stable identifiers.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;DimTeam&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;th&gt;TeamName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;T001&lt;/td&gt;
&lt;td&gt;Bournemouth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;T002&lt;/td&gt;
&lt;td&gt;Newcastle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;T003&lt;/td&gt;
&lt;td&gt;Fulham&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;DimReferee&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RefereeID&lt;/th&gt;
&lt;th&gt;RefereeName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;R001&lt;/td&gt;
&lt;td&gt;A Taylor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R002&lt;/td&gt;
&lt;td&gt;C Pawson&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;R003&lt;/td&gt;
&lt;td&gt;S Barrott&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;DimDate&lt;/code&gt; can use a &lt;code&gt;DateKey&lt;/code&gt;, while &lt;code&gt;FactMatches&lt;/code&gt; stores the corresponding foreign keys.&lt;/p&gt;

&lt;p&gt;The key exists because the model needs a reliable way to identify and relate records.&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%2F3kiwqll27j2vve8i6d90.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%2F3kiwqll27j2vve8i6d90.png" alt="DimReferee Table" width="337" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cardinality
&lt;/h2&gt;

&lt;p&gt;Cardinality describes how many records on one side of a relationship can correspond to records on the other side.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-to-many
&lt;/h3&gt;

&lt;p&gt;A one-to-many relationship means one record on the one side can correspond to several records on the many side.&lt;/p&gt;

&lt;p&gt;This is common in dimensional modelling because a dimension contains one record for an entity while that same entity may appear in many fact-table events.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-to-one
&lt;/h3&gt;

&lt;p&gt;A one-to-one relationship means one record in one table corresponds to exactly one record in another table.&lt;/p&gt;

&lt;p&gt;The relationship key is unique on both sides.&lt;/p&gt;

&lt;p&gt;This can be valid, but it should have a clear purpose. If both tables have the same grain and are always used together, separating them may introduce complexity without providing much analytical value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Many-to-many
&lt;/h3&gt;

&lt;p&gt;A many-to-many relationship allows values to repeat on both sides.&lt;/p&gt;

&lt;p&gt;This can represent a legitimate analytical problem, but it creates more complicated filter behaviour. It should not be used simply because duplicate values appear unexpectedly.&lt;/p&gt;

&lt;p&gt;If a table that was expected to provide unique values contains duplicates, the grain and table design should be checked before choosing many-to-many.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cardinality in the Premier League model
&lt;/h2&gt;

&lt;p&gt;The relationship between &lt;code&gt;DimDate&lt;/code&gt; and &lt;code&gt;FactMatches&lt;/code&gt; is one-to-many because one date can contain several matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimDate[DateKey]  1 -------- * FactMatches[DateKey]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same principle applies to referees. One referee can officiate several matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimReferee[RefereeID]  1 -------- * FactMatches[RefereeID]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current model does not naturally require a one-to-one or many-to-many relationship. Understanding those relationship types includes knowing when they are unnecessary.&lt;/p&gt;

&lt;p&gt;The team dimension introduces a different challenge. One team can participate in many matches, but each match contains two separate team roles:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Both refer to the same &lt;code&gt;DimTeam[TeamID]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     DimTeam
                    /       \
                   /         \
                  v           v
           HomeTeamID     AwayTeamID
                   \         /
                    \       /
                    FactMatches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cardinality remains one-to-many, but the same dimension is now being used in two different roles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Active and inactive relationships
&lt;/h2&gt;

&lt;p&gt;Power BI can contain more than one possible relationship between tables, but only one relationship can normally act as the default filtering path between the same two tables.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;active relationship&lt;/strong&gt; is used automatically during filtering and calculations.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;inactive relationship&lt;/strong&gt; still exists in the model but is not used automatically. It can be activated when a specific calculation requires that alternative relationship.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Active relationship&lt;/th&gt;
&lt;th&gt;Inactive relationship&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Used automatically&lt;/td&gt;
&lt;td&gt;Not automatically used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default analytical path&lt;/td&gt;
&lt;td&gt;Alternative analytical path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Solid line in Model View&lt;/td&gt;
&lt;td&gt;Dashed line in Model View&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This becomes important when the same dimension needs to describe the fact table in more than one role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Active and inactive relationships in the football model
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;DimTeam&lt;/code&gt; contains one list of teams, while &lt;code&gt;FactMatches&lt;/code&gt; contains both &lt;code&gt;HomeTeamID&lt;/code&gt; and &lt;code&gt;AwayTeamID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both are valid references to the same dimension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                     DimTeam
                    /       \
             active           inactive
                  /             \
                 v               v
           HomeTeamID       AwayTeamID
                   \         /
                    FactMatches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a &lt;strong&gt;role-playing dimension&lt;/strong&gt;. The entity is still a team, but its role changes depending on whether it appears as the home team or away team.&lt;/p&gt;

&lt;p&gt;One relationship can provide the normal active path, while the second remains available for calculations that need the alternative team role.&lt;/p&gt;

&lt;p&gt;The football model therefore provides a practical reason for active and inactive relationships rather than treating them as two settings to memorise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referential integrity
&lt;/h2&gt;

&lt;p&gt;Referential integrity means that every foreign-key value should have a valid corresponding record in the related table.&lt;/p&gt;

&lt;p&gt;If a fact table contains a foreign key that cannot be found in the dimension, the event still exists, but the model cannot retrieve the descriptive information expected from that dimension.&lt;/p&gt;

&lt;p&gt;Relationships therefore depend on the quality of the keys used to create them.&lt;/p&gt;

&lt;p&gt;A relationship does not correct an invalid reference. It exposes the consequences of that invalid reference during analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referential integrity in the Premier League model
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HomeTeamID
AwayTeamID
RefereeID
DateKey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in &lt;code&gt;FactMatches&lt;/code&gt; should have a corresponding value in its related dimension.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;FactMatches&lt;/code&gt; contains a team key that does not exist in &lt;code&gt;DimTeam&lt;/code&gt;, that match cannot be fully described through the team dimension.&lt;/p&gt;

&lt;p&gt;The same principle applies to referees and dates.&lt;/p&gt;

&lt;p&gt;This creates an important connection between data quality and data modelling:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Relationships do not repair bad keys. They rely on valid keys.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anti joins provide one way to test these relationships before relying on them in the final model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter direction
&lt;/h2&gt;

&lt;p&gt;A relationship also determines how filters can propagate between tables.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;single-direction filtering&lt;/strong&gt;, a filter travels in one direction. In a typical dimensional model, this is normally from the dimension to the fact table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dimension
    |
    v
Fact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a predictable analytical path. A descriptive value is selected in the dimension, and the corresponding fact records are filtered.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;bidirectional filtering&lt;/strong&gt;, filters can travel in both directions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table A &amp;lt;----&amp;gt; Table B
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bidirectional filtering can be useful, but it should be introduced carefully. As the number of relationships increases, allowing filters to travel both ways can create multiple filter paths, ambiguity, and model behaviour that becomes harder to troubleshoot.&lt;/p&gt;

&lt;p&gt;The decision should therefore be based on analytical requirements rather than simply using bidirectional filtering because it makes a particular visual work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter direction in the Premier League model
&lt;/h2&gt;

&lt;p&gt;The predictable default for this model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dimension
    |
    v
FactMatches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selecting a referee in &lt;code&gt;DimReferee&lt;/code&gt; should filter the matching records in &lt;code&gt;FactMatches&lt;/code&gt;. Selecting a date should do the same through &lt;code&gt;DimDate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The active team relationship follows the same principle.&lt;/p&gt;

&lt;p&gt;Single-direction filtering from the dimensions to &lt;code&gt;FactMatches&lt;/code&gt; therefore provides the clearest default design. Bidirectional filtering would only be introduced where a specific reporting requirement justifies the additional complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  A join is not a relationship
&lt;/h2&gt;

&lt;p&gt;Relationships and joins both involve matching data, but they perform different jobs.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Power Query Merge&lt;/strong&gt; combines matching data while the queries are being transformed.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Power BI relationship&lt;/strong&gt; keeps the tables separate and defines how they interact after they have been loaded into the semantic model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Power Query Merge&lt;/th&gt;
&lt;th&gt;Power BI relationship&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data preparation&lt;/td&gt;
&lt;td&gt;Data modelling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combines matching information&lt;/td&gt;
&lt;td&gt;Keeps tables separate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can add columns to a query&lt;/td&gt;
&lt;td&gt;Defines analytical interaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changes the resulting query&lt;/td&gt;
&lt;td&gt;Does not physically combine tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Happens during transformation&lt;/td&gt;
&lt;td&gt;Operates in the semantic model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Useful for shaping and lookup operations&lt;/td&gt;
&lt;td&gt;Useful for filtering and calculations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This distinction matters because excessive merging can undo the separation created by dimensional modelling. If every dimension is merged back into the fact table, the model can gradually return to a flat structure.&lt;/p&gt;

&lt;p&gt;A merge should therefore solve a transformation problem, while a relationship should solve a modelling problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Join types
&lt;/h2&gt;

&lt;p&gt;Power Query provides six main join types. The easiest way to understand them is to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which records does the join retain?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Left Outer Join
&lt;/h3&gt;

&lt;p&gt;A Left Outer Join keeps every row from the first table together with matching information from the second table. If no matching row exists on the right, the left-side record remains.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right Outer Join
&lt;/h3&gt;

&lt;p&gt;A Right Outer Join keeps every row from the second table together with matching information from the first table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Outer Join
&lt;/h3&gt;

&lt;p&gt;A Full Outer Join keeps all rows from both tables. Matching records can be combined, while unmatched records from either side remain visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inner Join
&lt;/h3&gt;

&lt;p&gt;An Inner Join keeps only records that have a match in both tables. Unmatched records from either side are removed from the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Left Anti Join
&lt;/h3&gt;

&lt;p&gt;A Left Anti Join keeps rows from the first table that do not have a matching row in the second table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right Anti Join
&lt;/h3&gt;

&lt;p&gt;A Right Anti Join keeps rows from the second table that do not have a matching row in the first table.&lt;/p&gt;

&lt;p&gt;The anti joins are particularly useful for validation because they answer a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which records failed to match?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Six joins using the football data
&lt;/h2&gt;

&lt;p&gt;The join behaviour becomes easier to see using a small sample of the football model.&lt;/p&gt;

&lt;h3&gt;
  
  
  FactMatches sample
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MatchID&lt;/th&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;M001&lt;/td&gt;
&lt;td&gt;T009&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M002&lt;/td&gt;
&lt;td&gt;T011&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M003&lt;/td&gt;
&lt;td&gt;T020&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M004&lt;/td&gt;
&lt;td&gt;T021&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  DimTeam sample
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;th&gt;TeamName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;T009&lt;/td&gt;
&lt;td&gt;Arsenal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;T011&lt;/td&gt;
&lt;td&gt;Chelsea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;T020&lt;/td&gt;
&lt;td&gt;Liverpool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;T099&lt;/td&gt;
&lt;td&gt;Example FC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;T009&lt;/code&gt;, &lt;code&gt;T011,&lt;/code&gt;, and &lt;code&gt;T020&lt;/code&gt; exist in both tables. &lt;code&gt;T021&lt;/code&gt; exists only in the fact sample, while &lt;code&gt;T099&lt;/code&gt; exists only in the dimension sample.&lt;/p&gt;

&lt;h3&gt;
  
  
  Left Outer
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;FactMatches&lt;/code&gt; is the left table, all four match records remain. The record containing &lt;code&gt;T021&lt;/code&gt; is still preserved even though no matching team exists in &lt;code&gt;DimTeam&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This type of join is useful when adding descriptive or key information to a fact query without wanting an unsuccessful lookup to remove the original event.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right Outer
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;DimTeam&lt;/code&gt; is the right table, every team record remains, including &lt;code&gt;T099&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The join therefore preserves the population from the right-side table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Outer
&lt;/h3&gt;

&lt;p&gt;A Full Outer Join retains all records from both tables.&lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;T021&lt;/code&gt; and &lt;code&gt;T099&lt;/code&gt; remain visible, making this join useful when comparing two datasets and identifying mismatches on either side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inner
&lt;/h3&gt;

&lt;p&gt;An Inner Join retains only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;T009
T011
T020
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the values that exist in both tables.&lt;/p&gt;

&lt;p&gt;The important risk is that &lt;code&gt;M004&lt;/code&gt; disappears from the result because its team key does not match. If the objective is to preserve all match events, that behaviour matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Left Anti
&lt;/h3&gt;

&lt;p&gt;A Left Anti Join returns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MatchID&lt;/th&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;M004&lt;/td&gt;
&lt;td&gt;T021&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which match record contains a team that cannot be found in DimTeam?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes Left Anti particularly useful for testing referential integrity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right Anti
&lt;/h3&gt;

&lt;p&gt;A Right Anti Join returns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;th&gt;TeamName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;T099&lt;/td&gt;
&lt;td&gt;Example FC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Which team exists in the dimension but does not appear in this match sample?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Parting shot
&lt;/h3&gt;

&lt;p&gt;Anti joins are more than additional options in the Merge Queries window. They can be useful data-quality tools because they make unmatched records visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Merge Queries fits into the football model
&lt;/h2&gt;

&lt;p&gt;The original match data stores team names, while &lt;code&gt;DimTeam&lt;/code&gt; contains the corresponding model key.&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;while the dimension contains:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TeamID&lt;/th&gt;
&lt;th&gt;TeamName&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;T009&lt;/td&gt;
&lt;td&gt;Arsenal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FactMatches&lt;/code&gt; requires the corresponding &lt;code&gt;HomeTeamID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A Power Query merge can therefore match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FactMatches[HomeTeam]
        ↓
DimTeam[TeamName]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and return the related &lt;code&gt;TeamID&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That value can then become:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The process can be repeated for &lt;code&gt;AwayTeamID&lt;/code&gt; and for the referee key.&lt;/p&gt;

&lt;p&gt;Once the tables are loaded into the model, a relationship can then be created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimTeam[TeamID]
      1
      |
      *
FactMatches[HomeTeamID]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two operations have different purposes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The merge builds or retrieves the foreign key. The relationship tells Power BI how to use that key during analysis.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The model that fits the Premier League data
&lt;/h2&gt;

&lt;p&gt;After considering the grain, table roles, schemas, keys, cardinality, and filtering requirements, a star-style structure provides the clearest fit for the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                         DimDate
                            |
                            |
                            v
DimTeam  -------------&amp;gt; FactMatches &amp;lt;------------- DimReferee
    \                       ^
     \______________________|
        second team role
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The normal design uses one-to-many relationships from the dimensions to &lt;code&gt;FactMatches&lt;/code&gt;, together with single-direction filtering from dimension to fact.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DimTeam&lt;/code&gt; requires additional handling because it performs both the home-team and away-team roles.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Why the model fits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reporting&lt;/td&gt;
&lt;td&gt;Team, referee, and date provide clear reporting dimensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAX&lt;/td&gt;
&lt;td&gt;Calculations can use understandable filter paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Descriptive information does not need to be repeated with every match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readability&lt;/td&gt;
&lt;td&gt;Facts and dimensions are easy to identify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;More seasons and match statistics can be added later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redundancy&lt;/td&gt;
&lt;td&gt;Repeated descriptive information is separated from match events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintainability&lt;/td&gt;
&lt;td&gt;Each table has a defined purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter propagation&lt;/td&gt;
&lt;td&gt;Dimensions normally filter the central fact table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model complexity&lt;/td&gt;
&lt;td&gt;Most relationships remain straightforward one-to-many relationships&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The star-style structure is therefore not selected simply because it is commonly recommended. It fits the grain of the dataset, the analytical questions being asked, and the relationship complexity required by the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The source structure is not the analytical model
&lt;/h3&gt;

&lt;p&gt;The way data arrives does not determine how it must be modelled. Source data is designed around collection and storage, while the Power BI model should be designed around analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Grain comes before measures
&lt;/h3&gt;

&lt;p&gt;A measure only has meaning when the level of detail underneath it is understood. In this model, establishing that one row represents one match provides the foundation for every match statistic that follows.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Keys need a purpose
&lt;/h3&gt;

&lt;p&gt;IDs should not be created simply because dimensional models use keys. The entities and grain should be understood first, after which the appropriate model keys can be created.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Relationships are part of the analytical logic
&lt;/h3&gt;

&lt;p&gt;Relationship lines in Model View are not decoration. Cardinality, active and inactive relationships, and filter direction all affect how Power BI evaluates a report.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Data quality becomes a modelling issue
&lt;/h3&gt;

&lt;p&gt;Once dimensions and facts depend on matching keys, missing or invalid values can affect the model itself. Referential integrity therefore connects data preparation directly to reporting reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Joins and relationships solve different problems
&lt;/h3&gt;

&lt;p&gt;A merge prepares and combines data during transformation. A relationship allows separate model tables to interact during analysis. Using one does not make the other unnecessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Schema selection is a design decision
&lt;/h3&gt;

&lt;p&gt;Flat, star, and snowflake schemas each have advantages and trade-offs. The correct structure depends on grain, analytical requirements, performance, maintainability, reporting needs, and relationship complexity.&lt;/p&gt;

</description>
      <category>data</category>
      <category>powerfuldevs</category>
      <category>analyst</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From MKDIR to GIT PUSH: What Actually Happens to a Project?</title>
      <dc:creator>Esther Njihia</dc:creator>
      <pubDate>Sun, 23 Aug 2026 07:02:43 +0000</pubDate>
      <link>https://dev.to/esther_njihia/from-mkdir-to-git-push-what-actually-happens-to-a-project-1ang</link>
      <guid>https://dev.to/esther_njihia/from-mkdir-to-git-push-what-actually-happens-to-a-project-1ang</guid>
      <description>&lt;p&gt;The first time you see a Git workflow, it can look like a list of commands you are supposed to memorize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add.
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three commands, and somehow the project ends up on GitHub.&lt;/p&gt;

&lt;p&gt;But what actually happens between creating a folder on your computer and seeing that project in a GitHub repository?&lt;/p&gt;

&lt;p&gt;The commands become much easier to understand when you stop treating them as a sequence to memorize and start seeing them as &lt;strong&gt;different stages in a workflow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we discussed a small Git Workflow, from creating a directory to your code being available on GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir
  ↓
git init
  ↓
git add
  ↓
git commit
  ↓
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not simply to learn what each command means, but to understand &lt;strong&gt;where the project is at each stage and what changes after every command.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this workflow matter?
&lt;/h2&gt;

&lt;p&gt;When creating a project on your computer.&lt;/p&gt;

&lt;p&gt;You create a folder, add some files, and start making changes.&lt;/p&gt;

&lt;p&gt;At some point, you want Git to keep track of those changes. Eventually, you may also want the project's history to be available on GitHub. Simply put, local files store your work, Git tracks how it changes over time, and GitHub backs up and shares those tracked changes with the world.&lt;/p&gt;

&lt;p&gt;It is tempting to think that Git takes the files on your computer and uploads them to GitHub.&lt;/p&gt;

&lt;p&gt;That is not what happens.&lt;/p&gt;

&lt;p&gt;There are several stages between the files you are editing locally and the repository hosted remotely.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory
       ↓
Staging Area
       ↓
Local Repository
       ↓
Remote Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding these four places is the key to understanding the Git workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Working Directory: where the project starts
&lt;/h2&gt;

&lt;p&gt;Everything starts with a directory on your computer.&lt;/p&gt;

&lt;p&gt;For this walkthrough, create a small Git sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;git-workflow-demo
&lt;span class="nb"&gt;cd &lt;/span&gt;git-workflow-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project can contain two simple files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git-workflow-demo/
├── README.md
└── hello.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, these are simply files inside a directory.&lt;/p&gt;

&lt;p&gt;Git has not started tracking the project yet.&lt;/p&gt;

&lt;p&gt;This is the &lt;strong&gt;working directory&lt;/strong&gt;: the location where the project files exist and where changes are made.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;hello.py&lt;/code&gt; might contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, Git!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file is edited, the change happens directly in the working directory.&lt;/p&gt;

&lt;p&gt;But there is an important question:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;How does Git know which changes should become part of the project's history? *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That is where Git's repository and staging area come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;git init&lt;/code&gt;: turning the folder into a Git repository
&lt;/h2&gt;

&lt;p&gt;Before Git can track the project, initialize a repository:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This creates the Git metadata needed for the directory to become a Git repository.&lt;/p&gt;

&lt;p&gt;The project is still in the same location, but Git can now start managing its history.&lt;/p&gt;

&lt;p&gt;The transition looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Regular project folder
        ↓
     git init
        ↓
Git repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful command to run immediately after this is:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git status&lt;/code&gt; provides information about the current state of the repository.&lt;/p&gt;

&lt;p&gt;It helps answer questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What files have changed?&lt;/li&gt;
&lt;li&gt;What files are untracked?&lt;/li&gt;
&lt;li&gt;What changes are staged?&lt;/li&gt;
&lt;li&gt;What changes are ready to be committed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than guessing what Git knows about, &lt;code&gt;git status&lt;/code&gt; lets you inspect the repository's current state.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Staging Area: choosing what to record
&lt;/h2&gt;

&lt;p&gt;Suppose &lt;code&gt;README.md&lt;/code&gt; has been created or modified.&lt;/p&gt;

&lt;p&gt;You can stage it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the most important distinctions for anyone learning Git:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;git add&lt;/code&gt; does not upload the file to GitHub.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, it tells Git:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Include this change in the next commit.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The staging area therefore sits between the working directory and the local repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory
       │
       │ git add
       ▼
Staging Area
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the state with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This shows that the change has moved from an unstaged state to a staged state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why have a staging area?
&lt;/h3&gt;

&lt;p&gt;The staging area gives you control over what goes into a commit.&lt;/p&gt;

&lt;p&gt;Imagine three files have changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.py
README.md
config.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might decide that only the changes to &lt;code&gt;app.py&lt;/code&gt; and &lt;code&gt;README.md&lt;/code&gt; belong in the next commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add app.py README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now those selected changes are staged, while &lt;code&gt;config.py&lt;/code&gt; can remain unstaged.&lt;/p&gt;

&lt;p&gt;This is one reason Git is more than a simple file-uploading tool.&lt;/p&gt;

&lt;p&gt;It gives you a way to &lt;strong&gt;select the changes you want to record together&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;git commit&lt;/code&gt;: creating a point in project history
&lt;/h2&gt;

&lt;p&gt;Once the desired changes have been staged, create a commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add README"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A commit is a recorded point in the project's history.&lt;/p&gt;

&lt;p&gt;The workflow is now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory
       ↓
    git add
       ↓
Staging Area
       ↓
   git commit
       ↓
Local Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local repository now contains a record of the staged changes.&lt;/p&gt;

&lt;p&gt;But there is an important distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The commit has not been sent to GitHub yet.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where most confuse &lt;strong&gt;commit&lt;/strong&gt; with &lt;strong&gt;push&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of the two commands this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit
→ Records the staged changes locally

git push
→ Sends local commits to a remote repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A commit and a push are therefore two separate operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A small experiment: watch the state change
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to understand Git is to observe what happens after each command.&lt;/p&gt;

&lt;p&gt;Start with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;Stage the README:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the status again:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create the commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add README"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;/div&gt;



&lt;p&gt;The important part of this exercise is not simply running the commands.&lt;/p&gt;

&lt;p&gt;It is observing how the repository's state changes.&lt;/p&gt;

&lt;p&gt;You can think of the experiment as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before git add
        ↓
Working Directory

After git add
        ↓
Staging Area

After git commit
        ↓
Local Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That makes the workflow easier to understand than memorizing a sequence of commands without knowing what each one is doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. What changes after each command?
&lt;/h2&gt;

&lt;p&gt;Here is the entire workflow in one place:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What changes?&lt;/th&gt;
&lt;th&gt;Where are the changes?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates Git metadata&lt;/td&gt;
&lt;td&gt;Local project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git add&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selects changes for the next commit&lt;/td&gt;
&lt;td&gt;Staging area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Records staged changes&lt;/td&gt;
&lt;td&gt;Local repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sends local commits to the remote&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important idea is that &lt;strong&gt;each command has a different responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is why the commands should not be thought of as interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Where does GitHub come in?
&lt;/h2&gt;

&lt;p&gt;So far, everything has happened on the local computer.&lt;/p&gt;

&lt;p&gt;The working directory is local.&lt;/p&gt;

&lt;p&gt;The staging area is local.&lt;/p&gt;

&lt;p&gt;The Git repository containing the commit history is local.&lt;/p&gt;

&lt;p&gt;GitHub becomes involved when a &lt;strong&gt;remote repository&lt;/strong&gt; is connected.The local repository and the GitHub repository are separate repositories.&lt;/p&gt;

&lt;p&gt;The next step is to connect them.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Connecting the local repository to GitHub
&lt;/h2&gt;

&lt;p&gt;After creating an empty repository on GitHub, connect it to the local repository with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin git@github.com:username/git-workflow-demo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;origin&lt;/code&gt; is the name given to the remote repository.&lt;/p&gt;

&lt;p&gt;You can check the configured remote with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;At this point, Git knows where the remote repository is.&lt;/p&gt;

&lt;p&gt;But the local commits are still local.&lt;/p&gt;

&lt;p&gt;They have not been sent to GitHub yet. That requires &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Where SSH fits and what it does
&lt;/h2&gt;

&lt;p&gt;If the remote URL looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git@github.com:username/git-workflow-demo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the repository is being accessed through SSH.&lt;/p&gt;

&lt;p&gt;SSH provides an authenticated way for your computer to communicate with GitHub.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your computer
     │
     │ SSH authentication
     ▼
   GitHub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before Git can communicate with GitHub through SSH, an SSH key needs to be configured and associated with the GitHub account.&lt;/p&gt;

&lt;p&gt;The important point for the Git workflow is that &lt;strong&gt;SSH is about authentication and communication with the remote repository&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not another version of &lt;code&gt;git add&lt;/code&gt; or &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those commands manage the local Git workflow. SSH helps establish the connection to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Finally: &lt;code&gt;git push&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now the local commit can be sent to the remote repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the step where the local Git history is transferred to the configured remote repository.&lt;/p&gt;

&lt;p&gt;So what actually happened to the project?&lt;/p&gt;

&lt;p&gt;It did not simply jump from the computer to GitHub.&lt;/p&gt;

&lt;p&gt;It moved through a series of stages.&lt;/p&gt;

&lt;h1&gt;
  
  
  A common beginner misconception
&lt;/h1&gt;

&lt;p&gt;One of the easiest things to misunderstand when learning Git is that &lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt;, and &lt;code&gt;git push&lt;/code&gt; all somehow "move the code."&lt;/p&gt;

&lt;p&gt;They do—but &lt;strong&gt;not to the same place&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider this simplified model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add
→ Select changes for the next commit

git commit
→ Record those staged changes locally

git push
→ Send those local commits to the remote repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commands are connected, but each one operates at a different stage of the workflow.&lt;/p&gt;

&lt;p&gt;This distinction becomes particularly important when something does not behave as expected.&lt;/p&gt;

&lt;p&gt;If a file is not included in a commit, the first question should not be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why didn't GitHub update?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"At which stage did the change stop?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Was it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;never changed in the working directory?&lt;/li&gt;
&lt;li&gt;not staged?&lt;/li&gt;
&lt;li&gt;not committed?&lt;/li&gt;
&lt;li&gt;committed locally but not pushed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding the workflow makes those questions much easier to answer.&lt;/p&gt;

&lt;h1&gt;
  
  
  The bigger lesson: Git is a workflow, not a command list
&lt;/h1&gt;

&lt;p&gt;It is possible to memorize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and still have very little understanding of Git.&lt;/p&gt;

&lt;p&gt;A better mental model is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"I changed something."
        ↓
Working Directory

"I want this change included."
        ↓
Staging Area

"I want to record this version."
        ↓
Local Repository

"I want that recorded history on GitHub."
        ↓
Remote Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the purpose of each stage is clear, the commands become easier to remember because each command answers a specific need.&lt;/p&gt;

&lt;p&gt;That is more useful than memorizing a command sequence without understanding what happens between the commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 takeaways from the Git workflow
&lt;/h2&gt;

&lt;h4&gt;
  
  
  1. The working directory is where changes are made.
&lt;/h4&gt;

&lt;p&gt;This is where project files are created, edited, and deleted.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The staging area lets you choose what goes into the next commit.
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;git add&lt;/code&gt; prepares selected changes for recording.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. A commit records staged changes locally.
&lt;/h4&gt;

&lt;p&gt;A commit creates a point in the project's history.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. A push sends local commits to a remote repository.
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;git push&lt;/code&gt; is what moves committed history from the local repository to the configured remote.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Understanding the workflow is more useful than memorizing commands.
&lt;/h4&gt;

&lt;p&gt;When the purpose of each stage is clear, Git commands become easier to reason about.&lt;/p&gt;

&lt;h1&gt;
  
  
  What happens when more than one person changes the project?
&lt;/h1&gt;

&lt;p&gt;The basic workflow is only the beginning.&lt;/p&gt;

&lt;p&gt;Once a repository becomes collaborative, another set of questions appears:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens when two people work on the same project?&lt;/li&gt;
&lt;li&gt;What is a branch?&lt;/li&gt;
&lt;li&gt;Why create a branch instead of working directly on &lt;code&gt;main&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;How are changes reviewed before they become part of the main project?&lt;/li&gt;
&lt;li&gt;What happens when two people modify the same part of a file?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those questions lead to the next part of the Git workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;branches → pull requests → merging → merge conflicts → collaborative Git workflows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For now, the most useful mental model is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory
       ↓
   git add
       ↓
Staging Area
       ↓
 git commit
       ↓
Local Repository
       ↓
  git push
       ↓
Remote Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git is not just the tool used to put code on GitHub. It is a system for &lt;strong&gt;tracking, organizing, recording, and sharing changes to a project over time&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Analysis of 120 Years of Olympics Data</title>
      <dc:creator>Esther Njihia</dc:creator>
      <pubDate>Sun, 22 Jan 2023 06:12:18 +0000</pubDate>
      <link>https://dev.to/esther_njihia/analysis-of-120-years-of-olympics-data-13jl</link>
      <guid>https://dev.to/esther_njihia/analysis-of-120-years-of-olympics-data-13jl</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The Olympic Games are one of the most prestigious and widely-followed sporting events in the world. With a history dating back over a century, the Olympics have seen countless athletes from all corners of the globe compete for glory and honor. In this project, we will use Python, SQL and Tableau to analyze 120 years of Olympics data sourced from Kaggle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Collection
&lt;/h2&gt;

&lt;p&gt;The data for this analysis was sourced from Kaggle and includes information on athletes, teams, events, and medals. The data was collected for all Summer and Winter Olympics from 1896 to 2016.&lt;br&gt;
Data source: &lt;a href="https://www.kaggle.com/datasets/heesoo37/120-years-of-olympic-history-athletes-and-results" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Data Cleaning and Preparation
&lt;/h2&gt;

&lt;p&gt;Before we could begin our analysis, the data needed to be cleaned and prepared for use. This involved removing any duplicate or irrelevant data, correcting any errors or inconsistencies, and formatting the data in a way that would be compatible with our analysis tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  Data Cleaning with Python
&lt;/h2&gt;

&lt;p&gt;Load data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data/athlete_events.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calculate the number of missing values in the data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isnull&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We got some missing values with the age, height and weight variables. How about we fix that.&lt;br&gt;
Fill in the age missing values with its mean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Age&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Age&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;inplace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat the same for the height and weight variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL Analysis
&lt;/h2&gt;

&lt;p&gt;Once the data was cleaned and prepared, we used SQL to extract and analyze the data. We were able to answer questions such as:&lt;br&gt;
1.What is the Gender distribution of athletes across different sports and event?&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;Sport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sex&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;Name&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;athlete_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Athlete&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;Sport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sex&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;athlete_count&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;p&gt;2.Which city and season have the most successful athletes?&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;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Season&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;Medal&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;Medal_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Athlete&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Medal&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Season&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;Medal_count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Which athletes are the most successful in terms of number of medals?&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="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;Medal&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;medal_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Athlete&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;Name&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;medal_count&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;p&gt;4.Trend analysis to identify any patterns in the number of medals over time&lt;br&gt;
By year&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="nb"&gt;Year&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;Medal&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;medal_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Athlete&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nb"&gt;Year&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="nb"&gt;Year&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5.Use statistical analysis to find factors associated with winning more medals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first create a sub query to find the total count of medals per city and season
&lt;/li&gt;
&lt;/ul&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;city_season_medals&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;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Season&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;Medal&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;medal_count&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Athlete&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;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Season&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;use statistical methods to find the relationship between city &amp;amp; season and the total number of medals. Fo this instance I used z-score. The higher the z-score the higher the correlation.
&lt;/li&gt;
&lt;/ul&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;City&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Season&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;medal_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;medal_count&lt;/span&gt; &lt;span class="o"&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;medal_count&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="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;STDEV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;medal_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;OVER&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;z_score&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;city_season_medals&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;z_score&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are some of the questions that were answered during the SQL analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization with Tableau
&lt;/h2&gt;

&lt;p&gt;To make the data more accessible and understandable, Tableau was used to create visualizations of our findings. Some of the visualizations we created include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A pie chart showing the gender distribution across all sports&lt;/li&gt;
&lt;li&gt;A bar chart showing the most successful athletes over time.&lt;/li&gt;
&lt;li&gt;line charts to show trends and patterns in the number of medals won over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;View the whole Olympics dashboard:&lt;a href="https://public.tableau.com/app/profile/esther7982/viz/OlympicAnalysis_16743097934740/Olympicsdashboard" rel="noopener noreferrer"&gt;Olympics Dashboard&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Through this analysis, I was able to gain a deeper understanding of the history of the Olympics and the performances of countries and athletes over the past 120 years. By using SQL and Tableau, I was able to extract valuable insights from the data and present them in a clear and accessible way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code and Data
&lt;/h2&gt;

&lt;p&gt;The code and data used for this project is available at &lt;a href="https://github.com/EstherNjihia/120-Years-of-Olympics" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using the above techniques, we can also use the data to predict future results, and also to identify patterns and trends that can be used to improve performance and achieve success in future Olympic games.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Data engineering 102: Introduction to python for data engineering.</title>
      <dc:creator>Esther Njihia</dc:creator>
      <pubDate>Sun, 04 Sep 2022 20:39:09 +0000</pubDate>
      <link>https://dev.to/esther_njihia/data-engineering-102-introduction-to-python-for-data-engineering-52p1</link>
      <guid>https://dev.to/esther_njihia/data-engineering-102-introduction-to-python-for-data-engineering-52p1</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The most interesting thing about python language is sort of all rounded. Due to its readability which is easy and the fact that it has very many applications including data engineering. In this post, we will discuss the most important concepts in the python language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Syntax
&lt;/h3&gt;

&lt;p&gt;Python, builds the code structure using whitespace and indentation. &lt;/p&gt;

&lt;h4&gt;
  
  
  Comments
&lt;/h4&gt;

&lt;p&gt;The comments are just as crucial as the code itself, because they explain why a piece of code was produced&lt;br&gt;
The comments are ignored by the Python interpreter when it runs the code.&lt;br&gt;
A single line comment in Python starts with the hash symbol (#), which is followed by the comment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Identifiers
&lt;/h4&gt;

&lt;p&gt;Python uses identifiers, or names, to designate variables, functions, modules, classes, and other types of objects.&lt;br&gt;
An identifier's name must begin with a letter or an underscore. After that, only underscores or alphanumeric characters are allowed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keywords
&lt;/h4&gt;

&lt;p&gt;Keywords are words that have special meaning in python. Python has a special module keyword that outlines all the keywords that are used in the Python Language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control statements
&lt;/h3&gt;

&lt;h4&gt;
  
  
  If statements.
&lt;/h4&gt;

&lt;p&gt;If statements are conditional statements. Below is the summary of the different if statements:&lt;br&gt;
When you want to run a code block based on a condition, use the if statement.&lt;br&gt;
If you want to execute a different code block if the condition is not True, use the if...else statement.&lt;br&gt;
When you wish to check multiple conditions and execute the associated code block after the condition that evaluates to True, use the if...elif...else statement.&lt;/p&gt;

&lt;h4&gt;
  
  
  for loop
&lt;/h4&gt;

&lt;p&gt;A code block can be performed a predetermined number of times by using the for loop statement.&lt;br&gt;
To alter the loop, use the range(start, stop, step).&lt;/p&gt;

&lt;h4&gt;
  
  
  while loop.
&lt;/h4&gt;

&lt;p&gt;If a condition is True, use the Python while loop statement to run a code block.&lt;/p&gt;

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

&lt;p&gt;A named, reusable chunk of code called a function in Python executes a command or returns a value.&lt;br&gt;
Create a new function by using the def keyword. The function definition and body make up a function.&lt;br&gt;
A function may have 0 parameters or more. You must pass a function the same amount of arguments if it has one or more parameters.&lt;br&gt;
A function can do a task or give back a value. If you want to return a value from a function, use the return statement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lists
&lt;/h3&gt;

&lt;p&gt;An orderly group of elements is referred to as a list.&lt;br&gt;
If you want to retrieve a list element by its index, use the square bracket notation []. The index of the first element is zero.&lt;br&gt;
To reach a list element from the list's end, use a negative index. The final component has an index of -1.&lt;br&gt;
To change an entry from a list, use list[index] = new value.&lt;br&gt;
To add a new element to the end of a list, use the append() method.&lt;br&gt;
To insert a new element at a certain location in a list, use insert().&lt;br&gt;
To remove an entry from a list and return it, use the pop() function.&lt;br&gt;
To eliminate an element from a list, use remove().&lt;br&gt;
Tuples are immutable lists.&lt;br&gt;
Use tuples when you want to define a list that cannot change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dictionary
&lt;/h3&gt;

&lt;p&gt;A Python dictionary is a group of key-value pairs, each of which has a corresponding value.&lt;br&gt;
In order to access a value by its key, use square brackets or the get() method.&lt;br&gt;
To remove a key-value pair by the key from a dictionary, use the del statement.&lt;br&gt;
To iterate through the keys, values, and key-value pairs in a dictionary, use a for loop.&lt;/p&gt;

&lt;p&gt;This post includes a summarization of some most important concepts as a beginner in Python.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>python</category>
    </item>
  </channel>
</rss>
