• If you would like to post, please check out the MrExcel Message Board FAQ and register here. If you forgot your password, you can reset your password.
  • Excel articles and downloadable files provided in the articles have not been reviewed by MrExcel Publishing. Please apply the provided methods / codes and open the files at your own risk. If you have any questions regarding an article, please use the Article Discussion section.
smozgur

Parent & Child Relationship Between Sources in Power Query

Excel Version
  1. 365
As long as there are matching fields in the source tables, we can join the associated queries (one-to-one, one-to-many, etc.) by using the Merge feature.

We are going to use the following sample source tables to demonstrate parent & child merging. These tables have a parent-child relationship between each other.
Products TableOrders Table
Product IDNamePriceOrder IDDateCustomer ID
1Product A102007/1/23100
2Product B92017/4/23100
3Product C132027/5/23101
4Product D202037/9/23102
2047/11/23100
Customer Table2057/11/23103
Customer IDName2067/13/23102
100Customer A
101Customer B
102Customer COrderItems Table
103Customer DOrderItem IDOrder IDProduct IDQuantity
30120011
30220023
30320132
30420111
30520121
30620242
30720313
30820332
30920341
31020421
31120432
31220542
31320621
31420632
31520643

We can see the foreign IDs in the Orders (Customer ID) and the OrderItems (Order ID, Product ID) tables which establish the relationship between tables. This is how the data is sourced in a normalized manner. By simply merging the necessary tables on these key columns, we can easily generate the required reports.

Load all tables as queries into Power Query. M codes for the base queries are provided below. Make sure using the given query names since these queries will be referred to in the other queries. Note: Binary data source used for the corresponding data tables/sources in the codes below to make it easy to test the samples below.

Products
Power Query:
let
    // Sample data used as a binary source to make it easier to follow the code
    // The following line could be used instead to load data from a worksheet table
    // Source = Excel.CurrentWorkbook(){[Name="Products"]}[Content],
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMlTSUQooyk8pTS5RcASyDQ2UYnWilYyQhJ2AbEuwqDGSqDNIsTFY2ARJ2AXINgKaEQsA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Product ID" = _t, Name = _t, Price = _t]), 
    ChangeType = Table.TransformColumnTypes(Source,{{"Product ID", Int64.Type}, {"Name", type text}, {"Price", type number}})
in
    ChangeType
Customers:
Power Query:
let
    // Source = Excel.CurrentWorkbook(){[Name="Customers"]}[Content],
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjQwUNJRci4tLsnPTS1ScFSK1QEJGiILOkEFjZAFnaGCxsiCLkqxsQA=", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Customer ID" = _t, Name = _t]), 
    ChangeType = Table.TransformColumnTypes(Source,{{"Customer ID", Int64.Type}, {"Name", type text}})
in
    ChangeType
Orders:
Power Query:
let
    // Source = Excel.CurrentWorkbook(){[Name="Orders"]}[Content],
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjIwUNJRMtc31DcyMDIGMg2BArE6IAlDsIQJpoQRWMIUIWEIlTAGS1giJIygEiYQO7BYYoohYwyVMYPIGKOYFgsA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"Order ID" = _t, Date = _t, #"Customer ID" = _t]),
    ChangeType = Table.TransformColumnTypes(Source,{{"Order ID", Int64.Type}, {"Date", type date}, {"Customer ID", Int64.Type}})
in
    ChangeType
OrderItems:
Power Query:
let
    // Source = Excel.CurrentWorkbook(){[Name="OrderItems"]}[Content],
    Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("Xc/BDYAgDIXhXThzoLSgzkLYfw1pi8/qAUI+kp8yRuJCKadaytrJ1syqFaon3sqmZKI3rgKNhQatQbupioTCYcq78Lx2QuNrF1TeLtmkPklQgoYC+d/adwbyv/VfQaCx0KDenvMG", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [#"OrderItem ID" = _t, #"Order ID" = _t, #"Product ID" = _t, Quantity = _t]),
    ChangeType = Table.TransformColumnTypes(Source,{{"OrderItem ID", Int64.Type}, {"Order ID", Int64.Type}, {"Product ID", Int64.Type}, {"Quantity", type number}})
in
    ChangeType

There is nothing fancy so far, simply loaded each table as separate queries into the Power Query environment, and made sure these are saved as Connection Only queries.

The first report we need is the ReportOrders which simply lists individual orders showing the customer and total order value for each order. In order to build this report, first we need a helper query that will get the Customer Name from the Customers table (child query) by using the Customer ID field in the Orders table (parent query).

Select the Orders query and click on Combine->Merge Queries->Merge Queries as New.

1690375584457.png

Merge queries


In the Merge dialog that pops up, the first query is selected as default since you selected the Query before executing the merge command. Select the Customers query in the second query drop-down control. Once the sample data is loaded, click on the Customer ID column headers in both tables to define the key between the two queries. Note: There will be times that you'll need multiple key matching. In this case, you can select multiple columns by holding the Ctrl key down and making sure the key numbers that appear in the header are matching the second table key numbers (there is no key number displayed if there is only one key column).

1690375597345.png

Select key columns


Left Outer join is the join type to be used for this sample project. Just click on OK and you get the result table with a new column consisting of cells with the Customer table value embedded. Preview the data in these table values by simply clicking on the white space in the cell. Note: Clicking on the Table keyword will extract that table value (Drill-Down) as a new step, that's why you need to click on the white space.

1690375618998.png

Merge result


It is time to extract the Customers' table values. Click on the little "two-ways" button on the right side in the Customers header. Select the Customer Name only to extract the name data.

1690375629253.png

Extract merged table value


Since this is a one-to-one relationship (One customer for each Order), there will be only one row extracted for each order record. Rename the query as OrdersWithCustomer and load it as a connection-only query as well.
Note: I often work with M code and personally don't prefer to work with step names (identifiers) including spaces. Therefore, I also changed the step name to something more meaningful and without space.

1690375642821.png

Orders with customer name

The M code for the OrdersWithCustomer query:
Power Query:
let
    Source = Table.NestedJoin(Orders, {"Customer ID"}, Customers, {"Customer ID"}, "Customers", JoinKind.LeftOuter),
    ExpandCustomers = Table.ExpandTableColumn(Source, "Customers", {"Name"})
in
    ExpandCustomers

Secondly, we need another helper query that will get the order item totals that are calculated as the multiplication of the Quantity and Product Price values. It means we will join the OrderItems query (parent with Quantity field) with the Products query (child with Price field). Select the OrderItems query and follow the same steps except selecting the Products query as the second query and the key column is "Product ID" this time. Extract the Product Name and Price columns, rename the new query as OrderItemsWithProduct, and load it as a connection-only query. With this new query, you have the Product Name and Product Price from the Products table for each order item.

1690375653458.png

Orders with product name

The M code for the OrderItemsWithProduct query:
Power Query:
let
    Source = Table.NestedJoin(OrderItems, {"Product ID"}, Products, {"Product ID"}, "Products", JoinKind.LeftOuter),
    ExpandProducts = Table.ExpandTableColumn(Source, "Products", {"Name", "Price"}, {"Products.Name", "Products.Price"})
in
    ExpandProducts

Finally, we can create the ReportOrder query. Merge the OrdersWithCustomer and the OrderItemsWithProduct queries on the Order ID field as the key.

1690375663814.png

Merge queries

Expand the newly added OrderItemsWithProduct column. Only select the Quantity and Product.Price columns to extract. You will notice that Quantity is coming from the OrderItems query and the Product.Price is coming from the joined Product table.

It is good so far, but we have duplicate orders in the table. That's because an order might have more than one order item. A new column is necessary that will be the result of the total amount for that order item, which means Price x Quantity. Select the query and add a new column by using the Custom Column button in the Add Column tab of the ribbon. The formula is the multiplication of the Quantity and the Price columns. Name the column as OrderLineTotal (I would also change the step name to make it more descriptive and space free).

1690375700218.png

Add the order line total column


Now there is a total column that could be aggregated as the grouped sum on Order ID. Select the Order ID, Date, and Name columns (leave the Customer ID column since it is not necessary in this report) and click on the Group By button in the Home tab of the ribbon. Note: Use the Ctrl key to select multiple columns in the table.

1690375716059.png

Group by Order ID


Change the new column name to "OrderTotal", select Sum as the operation, and finally select the OrderLineTotal as the column to be aggregated.

1690375736642.png

Order Line Total


Click OK and you get the desired result.

1690375760329.png

Orders Report


The following is the M code for the ReportOrders query.
Power Query:
let
    Source = Table.NestedJoin(OrdersWithCustomer, {"Order ID"}, OrderItemsWithProduct, {"Order ID"}, "OrderItemsWithProduct", JoinKind.LeftOuter),
    ExpandOrderItemsWithProduct = Table.ExpandTableColumn(Source, "OrderItemsWithProduct", {"Quantity", "Products.Price"}, {"OrderItemsWithProduct.Quantity", "OrderItemsWithProduct.Products.Price"}),
    OrderLineTotal = Table.AddColumn(ExpandOrderItemsWithProduct, "OrderLineTotal", each [OrderItemsWithProduct.Quantity] * [OrderItemsWithProduct.Products.Price]),
    OrderTotal = Table.Group(OrderLineTotal, {"Order ID", "Date", "Name"}, {{"OrderTotal", each List.Sum([OrderLineTotal]), type number}})
in
    OrderTotal

Additionally, we can create reports for customer total sales and product total sales by using the same method and grouping rows on the parent query. The M codes for those reports are provided below.
ReportCustomers (Please note that the previous report query is used in this query. Remember Orders vs Customer is one-to-one relationship.)
Power Query:
let
    Source = ReportOrders,
    CustomerTotals = Table.Group(Source, {"Name"}, {{"OrderTotal", each List.Sum([OrderTotal]), type number}})
in
    CustomerTotals

ReportProducts
Power Query:
let
    Source = OrderItemsWithProduct,
    OrderLineTotal = Table.AddColumn(Source, "OrderLineTotal", each [Quantity] * [Products.Price]),
    ProductTotals = Table.Group(OrderLineTotal, {"Products.Name"}, {{"OrderQuantity", each List.Sum([Quantity]), type nullable number}, {"OrderTotal", each List.Sum([OrderLineTotal]), type number}})
in
    ProductTotals

Finally, we can see the dependencies by using the Query Dependencies button in the View tab of the ribbon.

1690375773446.png

Query Dependencies
Author
smozgur
Views
934
First release
Last update
Rating
0.00 star(s) 0 ratings

More Excel articles from smozgur

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top