<?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: Boaz Wangai</title>
    <description>The latest articles on DEV Community by Boaz Wangai (@boaz_wangai_7bffb392ebc93).</description>
    <link>https://dev.to/boaz_wangai_7bffb392ebc93</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%2F4070922%2F1018c74f-7b32-44cb-9475-5afd46b9b440.png</url>
      <title>DEV Community: Boaz Wangai</title>
      <link>https://dev.to/boaz_wangai_7bffb392ebc93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/boaz_wangai_7bffb392ebc93"/>
    <language>en</language>
    <item>
      <title>Data Modelling, Relationships &amp; Joins</title>
      <dc:creator>Boaz Wangai</dc:creator>
      <pubDate>Thu, 17 Sep 2026 04:52:42 +0000</pubDate>
      <link>https://dev.to/boaz_wangai_7bffb392ebc93/data-modelling-relationships-joins-46cn</link>
      <guid>https://dev.to/boaz_wangai_7bffb392ebc93/data-modelling-relationships-joins-46cn</guid>
      <description>&lt;h1&gt;
  
  
  Data Modelling in Power BI
&lt;/h1&gt;

&lt;p&gt;Extracting reliable insights from a dataset requires a structured framework designed to guarantee accuracy, optimize performance, and streamline reporting. Developing this framework comprises the core process of data modeling.&lt;/p&gt;

&lt;p&gt;A structured data model serves as the foundation for comprehensive analysis, driving success in crucial areas such as:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Scalability and Maintainability
&lt;/h4&gt;

&lt;p&gt;A scalable data model facilitates the seamless integration of new data without requiring a structural rebuild. Because a well-designed model is inherently modular, it easily adapts when reporting requirements expand. When new metrics or data sources are introduced, new data can easily be added in new tables and relationships established with existing tables.&lt;/p&gt;

&lt;p&gt;By structuring data into separate, logical tables, new data can be added in new tables and relationships established seamlessly. These updates automatically flow through the rest of the model as defined by the relationships. Eg. The addition of a new customer will only require the addition of all the customer details in the table set aside for that while the customer ID will be used in the other tables to represent the entire information.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Performance
&lt;/h4&gt;

&lt;p&gt;Proper data modeling minimizes table width, eliminates redundant text columns from high-cardinality fact tables, leading to faster visual rendering and quicker dashboard interactions&lt;/p&gt;

&lt;h4&gt;
  
  
  3. DAX Calculations
&lt;/h4&gt;

&lt;p&gt;DAX relies heavily on active relationship paths and filter propagation. A suitable data model ensures a predictable filter context, making it far easier to write formulas because users can mentally visualize how filters flow through the data.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Analytics
&lt;/h4&gt;

&lt;p&gt;Choosing the right data model, with properly configured relationships, granularity and cardinality, drastically reduces logic errors to ensure all derived metrics yield accurate insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Modelling Approches in Power BI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Flat Tables
&lt;/h3&gt;

&lt;p&gt;Flat table combines all attributes and transactional data into a single, unnormalized table. This structure introduces heavy data redundancy, increases model size, and makes writing clean, efficient DAX calculations significantly harder.&lt;/p&gt;

&lt;p&gt;To keep the data model organized, clean, and efficient, datasets are separated into distinct fact and dimension tables. The logic behind how and why this data is split relies on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entity-Relationship: Descriptive attributes belonging to a specific subject (e.g., combining Customer_Name and Customer_City) are grouped into a dedicated dimension table. This ensures the data model is logically organized and meaningful.&lt;/li&gt;
&lt;li&gt;Data Granularity: Granularity defines exactly what a single row represents. This dictates the lowest level of detail the fact table will store before connecting out to the dimensions.&lt;/li&gt;
&lt;li&gt;Column Cardinality: Cardinality serves as the primary performance metric for the split. Columns with low cardinality (highly repetitive data) are moved into dimension tables to eliminate redundancy, while high-cardinality unique event identifiers remain in the fact table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fact tables are the tables in a schema that store quantitative data, while dimension tables store the descriptive data that gives context to the data in the fact tables. The combination and relationship between these fact and dimension tables make up the core architecture of a schema.&lt;/p&gt;

&lt;h4&gt;
  
  
  Characteristics of tables in schemas
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Fact Tables&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They hold quantitative numerical data which can be computed using DAX fnctions.&lt;/li&gt;
&lt;li&gt;They have the foreign keys which are used as the connectors to the dimension tables.&lt;/li&gt;
&lt;li&gt;Defines the grain of the data, which is the level of detail of the dataset. Eg. Each row holds a transaction of a single product to a specific client.&lt;/li&gt;
&lt;li&gt;They are normally updated frequntly because they hold the data for recurring events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Dimension Tables&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They hold descriptive data which give context to the dataset in the fact tables.&lt;/li&gt;
&lt;li&gt;They hold the primary keys which are the unique identifiers used to connect to the foreign keys in the fact tables.&lt;/li&gt;
&lt;li&gt;The descriptive data rarely changes so these tables are not updated frequently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The arrangement and relationship pattern between the fact table, dimension tables, and sub-dimension tables determine the type of schema:&lt;/p&gt;

&lt;p&gt;A. Star Schema&lt;/p&gt;

&lt;p&gt;In a star schema, all dimension tables are connected directly to a central fact 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkc2slbiwcqwlvu0i2t5u.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%2Fkc2slbiwcqwlvu0i2t5u.png" alt="Star Schema" width="755" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the illustration, the customers, store and product dimension tables are connected to the central sales table, which is a fact table.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Advantages of a star schema&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuitive and Easy to Navigate: Because there are no complex sub-tables, the model is easy for users to visualize.&lt;/li&gt;
&lt;li&gt;Simple formulas are required for computations.&lt;/li&gt;
&lt;li&gt;DAX performs better because it has less relationships to navigate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disadvantages of a star schema&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storing all data in a single flat table leads to disorganization, as it creates overwhelming visual clutter, mixes different levels of data granularity, and increases the risk of data entry inconsistencies.&lt;/li&gt;
&lt;li&gt;Data Redundancy: Text values that would otherwise appear only once in a normalized snowflake schema are repeated across multiple rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In conclusion, the star schema is suited for use cases where the requirements are fast computation and easy interpretation and visualization.&lt;/p&gt;

&lt;p&gt;B. Snowflake Schema&lt;/p&gt;

&lt;p&gt;In a snowflake schema, the dimension tables are connected to a central fact table and the dimension tables themselves have subdimensional tables connecting to them.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Advantages of a snowflake schema&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows storage of higly comprehensive detail with no repetition. For example, data for different mugs and umbrellas can be normalized further to have a separate table hold the category details, rather than repeating the details for each type of mug and each type of umbrella on the same table.&lt;/li&gt;
&lt;li&gt;Data model maintainability is improved because updates can be made directly within their respective tables, rather than editing thousands of duplicate rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Disadvantages of a snowflake schema&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error propagation: If for instance an error was made in one sub dimension table, the error will affect the data to a larger extent as it will be passed on to the related tables and troubleshooting would be complex for such a case.&lt;/li&gt;
&lt;li&gt;Complexity: the visual layout is more complex and therefore more difficult to visualize, and more complex formulas are required for computations.&lt;/li&gt;
&lt;li&gt;When performing computations, DAX is slower because it has to navigate through complex relationships between the tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In conclusion:&lt;br&gt;
The snowflake schema is ideal for managing massively complex and rigid data hierarchies, as it normalizes the data to completely avoid the repetition of entries.&lt;/p&gt;

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

&lt;p&gt;Relationships refer to logical connections between tables that share a column with common data. They serve as bridges to enable Power BI to understand how the tables in a model relate to one another.&lt;/p&gt;

&lt;h4&gt;
  
  
  Characteristics and properties of relationships
&lt;/h4&gt;

&lt;p&gt;1: Relationships are formed by linking the primary key of in the dimension table to the foreign key in the fact table. For example, sales transaction  have customers IDs to represent the customer involved. This can be used to link to the table which holds the data about customers.&lt;/p&gt;

&lt;p&gt;2: Cardinality of relationships: This refers to the numerical interaction between primary and foreign keys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One-to-many: A single instance of a primary key connects to multiple instances of a foreign key. The use case for this is when the primary key in the dimension table corresponds to multiple instances of the foreign key in the fact table.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;A unique primary key in one table links to a non-repeating foreign key in another table. This occurs when two tables share the same primary key structure or when an aggregated fact table contains exactly one record per foreign key.&lt;/li&gt;
&lt;li&gt;Many-to-Many: Multiple instances of a foreign key connect to multiple instances of another foreign key, where both foreign keys represent the same underlying entity or attribute across both tables.The use case for this is when bridging two tables that both contain non-unique, repeating values, such as relating a fact table directly to another fact table at a shared grain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3: Crossfilter direction&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single: Filters flow in one direction. Filtering the dimension table propagates to the fact table, but the inverse is not true. Eg, for two related tables, product table and sales table, if the product table is filtered to just display a single product, the sales table will also filter to show one product.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Both (Bi-directional): Filters flow in both directions simultaneously. Filtering the dimension table propagates to the fact table, and the inverse is also true. Although necessary for certain complex data models, bi-directional filtering can slow down performance and introduce ambiguity into your DAX calculations.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;4: Active and inactive relationships&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Power BI enforces a strict limit of one active relationship path between any two tables at a time. This constraint prevents ambiguous, conflicting filter arguments that would otherwise produce unpredictable calculation results in DAX. Any additional relationships established between those same tables are set to inactive but can be invoked using DAX.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Joins in Power Query
&lt;/h2&gt;

&lt;p&gt;Joins in Power Query combine two separate tables horizontally into a single table based on matching columns.&lt;br&gt;
The types of join is based on the rows that are retained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Left outer join keeps all rows from the left table and only matching rows from the right table.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsptyas0hrq7st4bipb9z.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%2Fsptyas0hrq7st4bipb9z.png" alt="Result" width="798" height="166"&gt;&lt;/a&gt;&lt;br&gt;
In this example, the join returns all the sales with the details of the corresponding clients who made the sales. The sale C5 returns nulls because it has no corresponding customer on the customers table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right outer join Keeps every row from the right/second table and matching rows from the first table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F38lug0sjsqas47sjvng5.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%2F38lug0sjsqas47sjvng5.png" alt="Right Outer Join" width="800" height="146"&gt;&lt;/a&gt;&lt;br&gt;
In the example (same left and right tables as above), the join returns all the customers with their corresponding sales. Customer 4 has nulls because they have no corresponding purchases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full outer join keeps all the rows from both tables, regardless of matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbabcob3a5kgwk7xvpet9.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%2Fbabcob3a5kgwk7xvpet9.png" alt="Full Outer" width="800" height="189"&gt;&lt;/a&gt;&lt;br&gt;
This returns all the records from both tables and fills out nulls where clients have no corresponding sales and sales have no corresponding clients.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inner join keeps only rows that have exact key matches in both tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fihemelmpee0ihcl8qxde.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%2Fihemelmpee0ihcl8qxde.png" alt="Inner Join" width="799" height="166"&gt;&lt;/a&gt;&lt;br&gt;
The inner join returns all records which have their customer IDs in both tables. The sales that have no corresponding clients are removed and the clients with no corresponding clients are also removed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right anti join keeps only rows from the right table that have no match in the left table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkjofxvjyhjbldlj9v1cy.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%2Fkjofxvjyhjbldlj9v1cy.png" alt="Right Anti" width="800" height="127"&gt;&lt;/a&gt;&lt;br&gt;
This returns the client Diana because they have no corresponding entry on the sales table.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Left anti join keeps only rows from the left table that have no match in the right table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0pzt4hggpi287bk8oaun.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%2F0pzt4hggpi287bk8oaun.png" alt="Left Anti" width="800" height="122"&gt;&lt;/a&gt;&lt;br&gt;
This returns the sale 104 because it has no corresponding customer (The customer ID C5 is not assigned to any client)&lt;/p&gt;

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

&lt;p&gt;For a typical business intelligence project, the recommendation is to use a Star Schema.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Balanced Normalization: In a typical business scenario, the number of data categories is manageable. There is rarely a need to over-normalize and break tables down into multiple, deep hierarchies. Keeping a single tier of dimension tables is highly efficient and reduces unnecessary complexity.&lt;/li&gt;
&lt;li&gt;Visual Clarity and DAX Simplicity: The star structure is easy to visualize. This visual clarity makes it straightforward to establish relationships, trace exactly how filters propagate from dimensions to facts, and write accurate DAX functions without getting lost in a web of tables.&lt;/li&gt;
&lt;li&gt;Maintainability and Scalability: As the business grows, appending new data is simple. Because the separation between descriptive attributes and transactional metrics is straighforward, it is obvious where new data belongs, making long-term maintenance effortless.&lt;/li&gt;
&lt;li&gt;Model Readability: Because typical business data is not overwhelmingly complex, the Star Schema ensures the model remains highly readable. It is sufficient to avoid the clutter of over-segmented tables.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>analytics</category>
      <category>architecture</category>
      <category>data</category>
      <category>database</category>
    </item>
    <item>
      <title>My First GitHub Project: From a Local Folder to GitHub Using Git and SSH</title>
      <dc:creator>Boaz Wangai</dc:creator>
      <pubDate>Mon, 24 Aug 2026 22:13:06 +0000</pubDate>
      <link>https://dev.to/boaz_wangai_7bffb392ebc93/my-first-github-project-from-a-local-folder-to-github-using-git-and-ssh-2po3</link>
      <guid>https://dev.to/boaz_wangai_7bffb392ebc93/my-first-github-project-from-a-local-folder-to-github-using-git-and-ssh-2po3</guid>
      <description>&lt;h2&gt;
  
  
  The Journey Begins
&lt;/h2&gt;

&lt;p&gt;Like every journey, we need a trail, a compass, a couple of checkpoints and milestones, and a general sense of direction, and we need to know how to weave all these elements to move from the start to the destination. My journey is much the same, from a folder on my desktop to a repository on my GitHub. Join me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;li&gt;Git Bash&lt;/li&gt;
&lt;li&gt;Windows PowerShell&lt;/li&gt;
&lt;li&gt;File explorer&lt;/li&gt;
&lt;li&gt;Excel&lt;/li&gt;
&lt;li&gt;VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;A general overview of the checkpoints on our journey&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a folder&lt;/li&gt;
&lt;li&gt;Create a GitHub account&lt;/li&gt;
&lt;li&gt;Create an SSH key&lt;/li&gt;
&lt;li&gt;Start the SSH agent and add your key&lt;/li&gt;
&lt;li&gt;Add the key to GitHub&lt;/li&gt;
&lt;li&gt;Create a repository on GitHub&lt;/li&gt;
&lt;li&gt;Link your folder&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Creating a folder
&lt;/h3&gt;

&lt;p&gt;To get to this checkpoint, I have 2 alternative paths that we can use. The general sense of direction arriving at a new folder created in a path of your choice.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using file explorer
&lt;/h4&gt;

&lt;p&gt;Navigate to the desired location via the file explorer and create a new folder.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Git Bash
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;On Git Bash, use the cd command and a path to navigate to the desired location.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;mkdir&lt;/code&gt; command to create a folder in that location.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h4&gt;
  
  
  Creating required files and subfolders
&lt;/h4&gt;

&lt;p&gt;Inside your main folder, you can create files and folders that you will need for your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You may need to create a README file to document the description pf your project. For example what you're trying to achieve and the tools used in the project. Use the &lt;code&gt;touch&lt;/code&gt; command for this.
&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%2F581pxeeip3qmf14ejac0.png" alt="Creating a file" width="698" height="136"&gt;
&lt;/li&gt;
&lt;li&gt;To create a subfolder, use the &lt;code&gt;mkdir&lt;/code&gt; command while inside the main folder.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a GitHub Account
&lt;/h3&gt;

&lt;p&gt;Navigate to the &lt;a href="https://github.com/login" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; website. You have the option of signing up with email or you can continue with Google. &lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an SSH Key
&lt;/h3&gt;

&lt;p&gt;Your SSH key authenticates you to GitHub to ensure that you are the only one allowed to make pushes, clone, fetch, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open PowerShell and run as administrator. Check the version of Git installed or install if necessary.&lt;/li&gt;
&lt;li&gt;Open Git Bash and generate SSH key and SSH passphrase. Use the command &lt;code&gt;ssh-keygen -t ed25519 -C "your_email@example.com"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On running the command, set a location to store it or accept the default if it is your first key, and the set a passphrase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Adding the SSH Key to the SSH Client
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Run Windows PowerShell as administrator.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;Get-Service -Name ssh-agent | Set-Service -StartupType Manual&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then &lt;code&gt;Start-Service ssh-agent\&lt;/code&gt; to start the agent.&lt;/li&gt;
&lt;li&gt;Close the administrator PowerShell and open a normal PowerShell window.&lt;/li&gt;
&lt;li&gt;Run the command &lt;code&gt;ssh-add c:/Users/YOUR_USERNAME/.ssh/id_ed25519&lt;/code&gt;. Replace &lt;code&gt;YOUR_USERNAME&lt;/code&gt; with your actual Windows username.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To finalize adding your SSH key to your GitHub&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy the public key by running below command on Git Bash
&lt;code&gt;cat ~/.ssh/id_ed25519.pub | clip&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Or display it by running &lt;code&gt;cat ~/.ssh/id_ed25519.pub&lt;/code&gt; and then manually copy it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Adding the SSH Key to GitHub
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;&lt;em&gt;New SSH Key&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;Add SSH Key&lt;/em&gt;&lt;/strong&gt; on your GitHub profile.&lt;/li&gt;
&lt;li&gt;In the SSH Key form, give it a title, set the Key type to Authentication Key and paste the key copied above, and click &lt;strong&gt;&lt;em&gt;Add SSH Key&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a Repository on GitHub
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Log into GitHub and click on &lt;em&gt;**Add New Repository *&lt;/em&gt;*&lt;/li&gt;
&lt;li&gt;Fill in the name, description, and set it to either public or private&lt;/li&gt;
&lt;li&gt;Create repository&lt;/li&gt;
&lt;li&gt;Copy the SSH URL. This will be used to connect your local folder.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Linking Local Folder to GitHub
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Using Git Bash
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the project folder using cd command (main folder). &lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git init&lt;/code&gt; to initialize Git. This means that GitHub will be tracking the changes in the folder.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git remote add origin [SSH_URL]&lt;/code&gt; to connect the local folder to the Git repository. The SSH URL is the link to the repository created in GitHub.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git add .&lt;/code&gt; to stage. The dot indicates that staging should be done in the specific directory we're in.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git commit -m[commit message]&lt;/code&gt; to set your commit message.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git branch -M main&lt;/code&gt; to rename the current branch as main.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;git push -u origin main&lt;/code&gt; to push to GitHub.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;There are different methods to achieve the same thing for different hurdles, much like taking different routes between successive check points. The important thing is to consider what you want to achieve, the tools to use, and the commands to run.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
