{"id":104979,"date":"2025-01-20T03:43:00","date_gmt":"2025-01-20T03:43:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=104979"},"modified":"2025-04-07T20:12:42","modified_gmt":"2025-04-07T20:12:42","slug":"loading-models-from-source-data-with-dbt","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/business-intelligence\/general-analytics\/loading-models-from-source-data-with-dbt\/","title":{"rendered":"Loading Models from Source Data with dbt"},"content":{"rendered":"<p>The data transformation tool <a href=\"https:\/\/www.getdbt.com\/\">dbt<\/a> (data build tool) has become more and more popular over the past years. It focuses heavily on SQL, and it adds a bunch of interesting features into the mix, such as data lineage, automatic orchestration, reusable macros etc. In the first article in this series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/business-intelligence\/general-analytics\/a-gentle-introduction-to-dbt\/\">A gentle introduction to dbt<\/a>, it\u2019s explained how you can get dbt for free in the cloud version, how you can set up an account and create a connection to Microsoft Fabric. We also created our first models, where a model can eventually be persisted as a view or a table in your database. It\u2019s recommended to go through that article first if you haven\u2019t already, because we will build upon it.<\/p>\n<p>In this article, we will dive a bit deeper into how you can specify the source tables for your dbt project, and we will create a couple of models that will load some fact and dimension tables.<\/p>\n<h2>How to read your Source Data<\/h2>\n<p>In an ELT pipeline, dbt is responsible for the \u201cT\u201d, meaning it can transform data that is already in the data store of choice using SQL. This means that some other tool \u2013 this can be pipelines in Azure Data Factory or in Fabric \u2013 needs to be responsible for extracting source data and storing it in your database. Suppose this part of the process has already been completed and you have data available for you in staging tables. How can you process that data with dbt?<\/p>\n<h3>Get sample data in Fabric SQL DB<\/h3>\n<p>As a source, we\u2019ll use sample data loaded into a SQL DB in Microsoft Fabric (this <a href=\"https:\/\/blog.fabric.microsoft.com\/en-us\/blog\/announcing-sql-database-in-microsoft-fabric-public-preview?ft=All\">feature is in preview<\/a> since the Microsoft Ignite 2024 conference). In a Fabric-enabled workspace, you can find the SQL database item under the \u201cstore data\u201d category when you want to add a new item.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"555\" class=\"wp-image-104980\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-6.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Give the new database a name. This name will be displayed when you connect to the database with the SQL Analytics Endpoint (which reads the mirrored Parquet files in the delta lake storage).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"308\" height=\"183\" class=\"wp-image-104981\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-7.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Once the database is provisioned, you\u2019re presented with multiple options to load data into it. In our case, we want to load the sample <code>AdventureWorksLT<\/code> database.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1096\" height=\"341\" class=\"wp-image-104982\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-8.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>It might take a little while to load the data.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"280\" height=\"88\" class=\"wp-image-104983\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-9.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>When the database is ready, you can see several tables are loaded into the <code>SalesLT<\/code> schema:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"952\" height=\"763\" class=\"wp-image-104984\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-10.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>We will now try to read and transform this data in dbt.<\/p>\n<h3>Specifying the Sources in dbt<\/h3>\n<p>dbt needs to know where it can find the source tables. The declaration of the source tables is done in a YAML file. Let us first add a new folder to the dbt project and<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"455\" height=\"292\" class=\"wp-image-104985\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-11.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Name it \u201cAdventureWorksDW\u201d:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"483\" height=\"228\" class=\"wp-image-104986\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-12.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Using the same context menu, we can add a new YAML file to this folder:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"491\" height=\"239\" class=\"wp-image-104987\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-13.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>The file doesn\u2019t necessarily need to be called <code>sources.yml<\/code>, but inside the YAML file there needs to be a <code>sources:<\/code> key in the file. In the file, we place the following contents:<\/p>\n<pre class=\"lang:none theme:none\"><code>version: 2\nsources:\n\u00a0 - name: adventureworkslt\n\u00a0 \u00a0 database: AdventureWorks\n\u00a0 \u00a0 schema: SalesLT\n\u00a0 \u00a0 tables:\n\u00a0 \u00a0 \u00a0 - name: Product\n\u00a0 \u00a0 \u00a0 - name: ProductCategory\n\u00a0 \u00a0 \u00a0 - name: ProductDescription\n\u00a0 \u00a0 \u00a0 - name: ProductModel\n\u00a0 \u00a0 \u00a0 - name: ProductModelProductDescription<\/code><\/pre>\n<p>We gave our source a name \u2013 <code>adventureworkslt<\/code> \u2013 and we specify the name of the database, the schema and the names of the different tables. Because the warehouse can use three-part naming, we can access the tables in the SQL DB from the warehouse itself. This uses the SQL Analytics Endpoint of the SQL DB. For example:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"817\" height=\"371\" class=\"wp-image-104988\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-screen-description-aut.png\" alt=\"A screenshot of a computer screen\n\nDescription automatically generated\" \/><\/p>\n<p>In the folder we created earlier, we can now add a new file called <code>DimProduct.sql<\/code>. It\u2019s important to include the <code>.sql<\/code> extension, so dbt can recognize it as a model. Inside the file, we can add the following SQL, which includes a <a href=\"https:\/\/docs.getdbt.com\/docs\/build\/jinja-macros\">jinja<\/a> reference to one of the source tables we specified in the <code>sources.yml<\/code> file:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\"><code>SELECT *\nFROM {{ source('adventureworkslt', 'Product') }}<\/code><\/pre>\n<p>The reference consists of the source name and the name of the table. At compile time, dbt will translate this to the correct three-part name:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"759\" height=\"215\" class=\"wp-image-104989\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-14.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>It\u2019s also possible to do a preview of the data, if the SQL statement is valid:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1025\" height=\"630\" class=\"wp-image-104990\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-15.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Let\u2019s change our query with something more complex; we now read from several source tables in the same query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\"><code>WITH cte_categories AS\n(\n\u00a0 \u00a0 SELECT\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0ProductCategoryID\n\u00a0 \u00a0 \u00a0 \u00a0 ,ProductCategoryName \u00a0 \u00a0= [Name]\n\u00a0 \u00a0 FROM {{ source('adventureworkslt', 'ProductCategory') }}\n\u00a0 \u00a0 WHERE ParentProductCategoryID IS NULL\n)\n, \u00a0 cte_subcategories AS\n(\n\u00a0 \u00a0 SELECT\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0ProductSubCategoryID \u00a0 = sc.ProductCategoryID\n\u00a0 \u00a0 \u00a0 \u00a0 ,ProductSubCategoryName = sc.[Name]\n\u00a0 \u00a0 \u00a0 \u00a0 ,c.ProductCategoryName\n\u00a0 \u00a0 FROM {{ source('adventureworkslt', 'ProductCategory') }} sc\n\u00a0 \u00a0 LEFT JOIN cte_categories \u00a0 \u00a0 c ON sc.ParentProductCategoryID = c.ProductCategoryID\n\u00a0 \u00a0 WHERE ParentProductCategoryID IS NOT NULL\n)\n, \u00a0 cte_models AS\n(\n\u00a0 \u00a0 SELECT\n\u00a0 \u00a0 \u00a0 \u00a0 m.ProductModelID\n\u00a0 \u00a0 \u00a0 \u00a0,ProductModelName \u00a0 \u00a0= m.[Name]\n\u00a0 \u00a0 \u00a0 \u00a0,ProductModelDesc \u00a0 \u00a0= ISNULL(d.[Description],'Description missing...')\n\u00a0 \u00a0 FROM {{ source('adventureworkslt', 'ProductModel') }} \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0m\n\u00a0 \u00a0 LEFT JOIN {{ source('adventureworkslt', 'ProductModelProductDescription') }} md \n       ON m.ProductModelID = md.ProductModelID AND md.Culture = 'en'\n\u00a0 \u00a0 LEFT JOIN {{ source('adventureworkslt', 'ProductDescription') }} \u00a0 \u00a0 \u00a0d \u00a0\n       ON md.ProductDescriptionID = d.ProductDescriptionID\n)\nSELECT\n\u00a0 \u00a0 \u00a0p.ProductID\n\u00a0 \u00a0 ,ProductName \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0= p.[Name]\n\u00a0 \u00a0 ,p.ProductNumber\n\u00a0 \u00a0 ,ProductColor \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 = ISNULL(p.Color,'N\/A')\n\u00a0 \u00a0 ,p.StandardCost\n\u00a0 \u00a0 ,p.ListPrice\n\u00a0 \u00a0 ,ProductSize \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0= ISNULL(p.Size,'N\/A')\n\u00a0 \u00a0 ,ProductWeight \u00a0 \u00a0 \u00a0 \u00a0 \u00a0= p.[Weight]\n\u00a0 \u00a0 ,p.SellStartDate\n\u00a0 \u00a0 ,p.SellEndDate\n\u00a0 \u00a0 ,p.DiscontinuedDate\n\u00a0 \u00a0 ,ProductSubCategoryName = ISNULL(pc.ProductSubCategoryName,'N\/A')\n\u00a0 \u00a0 ,ProductCategoryName \u00a0 \u00a0= ISNULL(pc.ProductCategoryName,'N\/A')\n\u00a0 \u00a0 ,ProductModelName \u00a0 \u00a0 \u00a0 = ISNULL(m.ProductModelName,'N\/A')\n\u00a0 \u00a0 ,ProductModelDesc \u00a0 \u00a0 \u00a0 = ISNULL(m.ProductModelDesc,'N\/A')\nFROM {{ source('adventureworkslt', 'Product') }} p\nLEFT JOIN cte_subcategories pc ON p.ProductCategoryID = pc.ProductSubCategoryID\nLEFT JOIN cte_models \u00a0 \u00a0 \u00a0 \u00a0m \u00a0ON p.ProductModelID = m.ProductModelID;<\/code><\/pre>\n<p>When we save and compile the model, we get an updated lineage view, which shows the dependencies of the model on the source tables:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"473\" class=\"wp-image-104991\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-16.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>This only works if you use the source macro to reference the source tables, if you would hardcode the table names the lineage view will not show the dependencies. We can now create a similar model for the customers. First, we will need to append the names of the source tables to the <code>sources.yml<\/code> file:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"408\" height=\"454\" class=\"wp-image-104992\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-17.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Add a new file called <code>DimCustomer.sql<\/code> and add the following SQL statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\"><code>WITH cte_mainoffices AS\n(\n    SELECT\n         ca.CustomerID\n        ,a.City\n        ,a.StateProvince\n        ,a.CountryRegion\n        ,a.PostalCode\n    FROM {{ source('adventureworkslt', 'CustomerAddress') }} ca\n    JOIN {{ source('adventureworkslt', 'Address') }}       a \n          ON ca.AddressID = a.AddressID\n    WHERE ca.AddressType = 'Main Office'\n)\nSELECT\n     c.CustomerID\n    ,CustomerName           = CONCAT_WS(' ',c.Title, c.FirstName, c.MiddleName, \n                                                              c.LastName, c.Suffix)\n    ,CustomerCompanyName    = ISNULL(c.CompanyName,'N\/A')\n    ,CustomerSalesPerson    = ISNULL(c.SalesPerson,'N\/A')\n    ,CustomerEmail          = ISNULL(c.EmailAddress,'N\/A')\n    ,CustomerPhone          = ISNULL(c.Phone,'N\/A')\n    ,CustomerPostalCode     = ISNULL(m.PostalCode,'N\/A')\n    ,CustomerCity           = ISNULL(m.City,'N\/A')\n    ,CustomerStateProvince  = ISNULL(m.StateProvince,'N\/A')\n    ,CustomerCountryRegion  = ISNULL(m.CountryRegion,'N\/A')\nFROM {{ source('adventureworkslt', 'Customer') }}       c\nLEFT JOIN cte_mainoffices   m ON c.CustomerID = m.CustomerID;<\/code><\/pre>\n<p>Resulting in this lineage view:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"573\" height=\"334\" class=\"wp-image-104993\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-diagram-of-a-company-description-automatically.png\" alt=\"A diagram of a company\n\nDescription automatically generated\" \/><\/p>\n<p>For our final model, <code>FactSales.sql<\/code>, we can use the following SQL statement (don\u2019t forget to add the two extra source tables to the <code>sources.yml<\/code> file!).<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk \">WITH cte_details AS\n(\n\u00a0 \u00a0 SELECT\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0SalesOrderID\n\u00a0 \u00a0 \u00a0 \u00a0 ,SalesOrderDetailID\n\u00a0 \u00a0 \u00a0 \u00a0 ,OrderQty\n\u00a0 \u00a0 \u00a0 \u00a0 ,ProductID\n\u00a0 \u00a0 \u00a0 \u00a0 ,UnitPrice\n\u00a0 \u00a0 \u00a0 \u00a0 ,UnitPriceDiscount\n\u00a0 \u00a0 \u00a0 \u00a0 ,LineTotal = \n            ISNULL(OrderQty * (1.0 - ISNULL(UnitPriceDiscount,0.0)) * UnitPrice,0.0)\n\u00a0 \u00a0 FROM {{ source('adventureworkslt', 'SalesOrderDetail') }}\n), cte_sales AS\n(\n\u00a0 \u00a0 SELECT\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0soh.SalesOrderID\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.SalesOrderDetailID\n\u00a0 \u00a0 \u00a0 \u00a0 ,SK_OrderDate \u00a0 = CONVERT(DATE,soh.OrderDate)\n\u00a0 \u00a0 \u00a0 \u00a0 ,SK_DueDate \u00a0 \u00a0 = CONVERT(DATE,soh.DueDate)\n\u00a0 \u00a0 \u00a0 \u00a0 ,SK_ShipDate \u00a0 \u00a0= CONVERT(DATE,soh.ShipDate)\n\u00a0 \u00a0 \u00a0 \u00a0 ,soh.PurchaseOrderNumber\n\u00a0 \u00a0 \u00a0 \u00a0 ,soh.AccountNumber\n\u00a0 \u00a0 \u00a0 \u00a0 ,soh.CustomerID\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.ProductID\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.OrderQty\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.UnitPrice\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.UnitPriceDiscount\n\u00a0 \u00a0 \u00a0 \u00a0 ,sod.LineTotal\n\u00a0 \u00a0 \u00a0 \u00a0 \/* the next two costs are evenly divided over the detail lines *\/\n\u00a0 \u00a0 \u00a0 \u00a0 ,TaxAmt \u00a0 \u00a0 \u00a0 \u00a0 = soh.TaxAmt \/ (COUNT(1) OVER (PARTITION BY soh.SalesOrderID))\n\u00a0 \u00a0 \u00a0 \u00a0 ,Freight \u00a0 \u00a0 \u00a0 \u00a0= soh.Freight \/ (COUNT(1) OVER (PARTITION BY soh.SalesOrderID))\n\u00a0 \u00a0 FROM {{ source('adventureworkslt', 'SalesOrderHeader') }} \u00a0 soh\n\u00a0 \u00a0 JOIN cte_details \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0sod ON soh.SalesOrderID = sod.SalesOrderID\n)\nSELECT\n\u00a0 \u00a0 \u00a0SalesOrderID\n\u00a0 \u00a0 ,SalesOrderDetailID\n\u00a0 \u00a0 ,SK_OrderDate\n\u00a0 \u00a0 ,SK_DueDate\n\u00a0 \u00a0 ,SK_ShipDate\n\u00a0 \u00a0 ,PurchaseOrderNumber\n\u00a0 \u00a0 ,AccountNumber\n\u00a0 \u00a0 ,CustomerID\n\u00a0 \u00a0 ,ProductID\n\u00a0 \u00a0 ,OrderQty\n\u00a0 \u00a0 ,UnitPrice\n\u00a0 \u00a0 ,UnitPriceDiscount\n\u00a0 \u00a0 ,LineTotal\n\u00a0 \u00a0 ,TaxAmt\n\u00a0 \u00a0 ,Freight\n\u00a0 \u00a0 ,TotalAmount \u00a0= LineTotal + ISNULL(TaxAmt,0.0) + ISNULL(Freight,0.0)\nFROM cte_sales;<\/pre>\n<p>Which will result in this lineage view:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"637\" height=\"207\" class=\"wp-image-104994\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-diagram-of-a-diagram-description-automatically.png\" alt=\"A diagram of a diagram\n\nDescription automatically generated with medium confidence\" \/><\/p>\n<p>Our (simple) data warehouse is finished, for now. We can build all the models and deploy them to the Fabric warehouse with the following command:<\/p>\n<pre class=\"lang:none theme:none\"><code>dbt\u00a0run --models AdventureWorksDW.*<\/code><\/pre>\n<p>This <a href=\"https:\/\/docs.getdbt.com\/reference\/commands\/run\">command<\/a> will take all compiled models within the path specified and execute them against the database.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"571\" height=\"359\" class=\"wp-image-104995\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-18.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>However, the models are created as views, not as tables.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"170\" height=\"128\" class=\"wp-image-104996\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-19.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>This is the <a href=\"https:\/\/docs.getdbt.com\/docs\/build\/materializations\">default in dbt<\/a>, but we can override this by setting it in the model itself. If you want to do this for an entire folder, you can specify the behaviour in the <code>dbt_project.yml <\/code>file.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"213\" height=\"423\" class=\"wp-image-104997\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-20.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>Add the folder and the <code>+materialized<\/code> keyword and set it to <strong>Table<\/strong>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"555\" height=\"203\" class=\"wp-image-104998\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screen-shot-of-a-computer-program-description-a.png\" alt=\"A screen shot of a computer program\n\nDescription automatically generated\" \/><\/p>\n<p>When we run the command again, we can see load times have increased as data is loaded into tables:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"715\" height=\"435\" class=\"wp-image-104999\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-21.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<p>The views have been removed and tables have been created instead:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"207\" height=\"197\" class=\"wp-image-105000\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/12\/a-screenshot-of-a-computer-description-automatica-22.png\" alt=\"A screenshot of a computer\n\nDescription automatically generated\" \/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, we showed how you can load sample data into a Fabric SQL Database. Then we used this data to load a couple of models in dbt. We needed to specify those source tables in a YAML file, which allows metadata references inside the SQL statement, allowing dbt to derive dependencies and create a lineage view.<\/p>\n<p>Those models aren\u2019t \u201cbest practice\u201d yet when it comes to data warehouse modelling. In a following article, we will discover how we can use macros to add surrogate keys, reuse code and create a date dimension.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The data transformation tool dbt (data build tool) has become more and more popular over the past years. It focuses heavily on SQL, and it adds a bunch of interesting features into the mix, such as data lineage, automatic orchestration, reusable macros etc. In the first article in this series, A gentle introduction to dbt,&#8230;&hellip;<\/p>\n","protected":false},"author":110905,"featured_media":105002,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,159165],"tags":[159230],"coauthors":[159081],"class_list":["post-104979","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-general-analytics","tag-dbt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104979","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/110905"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=104979"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104979\/revisions"}],"predecessor-version":[{"id":106135,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104979\/revisions\/106135"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/105002"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=104979"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=104979"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=104979"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=104979"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}