Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Microsoft is giving away 50,000 FREE Microsoft Certification exam vouchers. Get Fabric certified for FREE! Learn more

Reply
frithjof_v
Super User
Super User

Default joins work differently in KQL vs. SQL

When doing a default join in KQL (without specifying join kind), it seems that the left or right position of the tables affect the number of rows returned by the query.

 

This is not the case with the default joins in SQL.

 

KQL version

1. Below is one of the tables involved in the join:

frithjof_v_0-1731762570397.png

 

2. Below is the other table involved in the join:

frithjof_v_1-1731762602606.png

 

3. When the first table above is on the left side of the join, and the second table above is on the right side: 

frithjof_v_2-1731762796744.png

In this case (3.), why does it only return three rows? In SQL, such a default join would return all the matching rows (similar to 4. below)

 

4. When the tables switch sides (the second table above is now on the left side, and the first table above is now on the right side), it returns all the rows as is familiar from SQL: 

frithjof_v_3-1731762878998.png

 

What is the reason for the behaviour in step 3?

This means that the default join type in KQL give different results (behave differently) than the default join type in SQL (see below).

 

 

SQL version

 

1. 

frithjof_v_4-1731763359484.png

 

2. 

frithjof_v_5-1731763388458.png

 

3. Here we get all the matching rows, instead of just 3 rows (we only got 3 rows with the similar query in KQL)

frithjof_v_6-1731763425203.png

4.

frithjof_v_7-1731763456736.png

 

 

2 ACCEPTED SOLUTIONS
frithjof_v
Super User
Super User

It works similar like SQL's default join type (which is inner join) if I explicitly add kind=inner to the join in KQL.

 

 

let varStockMarketLast10 = 
StockMarketRaw
| project ['time'], symbol, bidPrice, bidSize, askPrice, askSize, volume
| order by ['time']
| take 10;

varStockMarketLast10;

let varDimStockMarket = 
StockMarketRaw
| distinct symbol, sector, securityType;

varDimStockMarket;

varStockMarketLast10
| join kind=inner varDimStockMarket on symbol;

varDimStockMarket
| join kind=inner varStockMarketLast10 on symbol;

 

 

So the default join kind in KQL is not an inner join, whereas in SQL, the default join kind is inner join.

 

The default join kind in KQL is:

  • innerunique (default)

Inner join with left side deduplication
Schema: All columns from both tables, including the matching keys
Rows: All deduplicated rows from the left table that match rows from the right table

 

https://learn.microsoft.com/en-us/training/modules/multi-table-queries-with-kusto-query-language/2-m...

 

To be honest, I find the results provided by KQL's default join kind confusing. What is the practical use case for those results? (Ref. step 3 in the original post).

 

What is the benefit of KQL's default join kind (innerunique)?

 

I think I will always need to specify join kind in KQL. (Or, be careful about which table is on the right side or left side of the join, when using the default join.)

 

In SQL, by contrast, it can many times make sense to use the default join kind (inner join), and the left/right position won't affect the number of rows returned.
Still, it is always a good practice to specify join kind explicitly also in SQL, for readability.

View solution in original post

Hi  @frithjof_v

 

I agree with your insights.

Quote from Microsoft:

 

By default, the innerunique join flavor is used if the kind parameter isn't specified.

 

This default implementation is useful in log/trace analysis scenarios, where you aim to correlate two events based on a shared correlation ID.

 

It allows you to retrieve all instances of the phenomenon while disregarding duplicate trace records that contribute to the correlation.


More information for your reference:

innerunique join - Kusto | Microsoft Learn

 

Best regards,

Joyce

If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

View solution in original post

3 REPLIES 3
frithjof_v
Super User
Super User

It works similar like SQL's default join type (which is inner join) if I explicitly add kind=inner to the join in KQL.

 

 

let varStockMarketLast10 = 
StockMarketRaw
| project ['time'], symbol, bidPrice, bidSize, askPrice, askSize, volume
| order by ['time']
| take 10;

varStockMarketLast10;

let varDimStockMarket = 
StockMarketRaw
| distinct symbol, sector, securityType;

varDimStockMarket;

varStockMarketLast10
| join kind=inner varDimStockMarket on symbol;

varDimStockMarket
| join kind=inner varStockMarketLast10 on symbol;

 

 

So the default join kind in KQL is not an inner join, whereas in SQL, the default join kind is inner join.

 

The default join kind in KQL is:

  • innerunique (default)

Inner join with left side deduplication
Schema: All columns from both tables, including the matching keys
Rows: All deduplicated rows from the left table that match rows from the right table

 

https://learn.microsoft.com/en-us/training/modules/multi-table-queries-with-kusto-query-language/2-m...

 

To be honest, I find the results provided by KQL's default join kind confusing. What is the practical use case for those results? (Ref. step 3 in the original post).

 

What is the benefit of KQL's default join kind (innerunique)?

 

I think I will always need to specify join kind in KQL. (Or, be careful about which table is on the right side or left side of the join, when using the default join.)

 

In SQL, by contrast, it can many times make sense to use the default join kind (inner join), and the left/right position won't affect the number of rows returned.
Still, it is always a good practice to specify join kind explicitly also in SQL, for readability.

After further reading, it seems that the reason for this different behaviour is performance optimization in KQL's default join type (inneruniqe).

 

Bottom line: it's important to know that the default join type in KQL is different than the default join type in SQL.

 

When using the default join in KQL, it seems to make sense to have the Dimension table on the left side, and the Fact table on the right side.

 

This returns all the matching rows (assuming a 1:many relationship between dim:fact).

 

It seems to generally be a good practice in KQL to have the Dim table on the left side of the join, and the fact table on the right side of the join.

 

Pseudo-code:

dimTable
| join factTable on keyColumn;

 

I am new to KQL, and curious what others think.

So - what do you think?
Does this align with your understanding?

Hi  @frithjof_v

 

I agree with your insights.

Quote from Microsoft:

 

By default, the innerunique join flavor is used if the kind parameter isn't specified.

 

This default implementation is useful in log/trace analysis scenarios, where you aim to correlate two events based on a shared correlation ID.

 

It allows you to retrieve all instances of the phenomenon while disregarding duplicate trace records that contribute to the correlation.


More information for your reference:

innerunique join - Kusto | Microsoft Learn

 

Best regards,

Joyce

If this post helps, then please consider Accept it as the solution to help the other members find it more quickly.

Helpful resources

Announcements
MarchFBCvideo - carousel

Fabric Monthly Update - March 2025

Check out the March 2025 Fabric update to learn about new features.

Notebook Gallery Carousel1

NEW! Community Notebooks Gallery

Explore and share Fabric Notebooks to boost Power BI insights in the new community notebooks gallery.

April2025 Carousel

Fabric Community Update - April 2025

Find out what's new and trending in the Fabric community.

"); $(".slidesjs-pagination" ).prependTo(".pagination_sec"); $(".slidesjs-pagination" ).append("
"); $(".slidesjs-play.slidesjs-navigation").appendTo(".playpause_sec"); $(".slidesjs-stop.slidesjs-navigation").appendTo(".playpause_sec"); $(".slidesjs-pagination" ).append(""); $(".slidesjs-pagination" ).append(""); } catch(e){ } /* End: This code is added by iTalent as part of iTrack COMPL-455 */ $(".slidesjs-previous.slidesjs-navigation").attr('tabindex', '0'); $(".slidesjs-next.slidesjs-navigation").attr('tabindex', '0'); /* start: This code is added by iTalent as part of iTrack 1859082 */ $('.slidesjs-play.slidesjs-navigation').attr('id','playtitle'); $('.slidesjs-stop.slidesjs-navigation').attr('id','stoptitle'); $('.slidesjs-play.slidesjs-navigation').attr('role','tab'); $('.slidesjs-stop.slidesjs-navigation').attr('role','tab'); $('.slidesjs-play.slidesjs-navigation').attr('aria-describedby','tip1'); $('.slidesjs-stop.slidesjs-navigation').attr('aria-describedby','tip2'); /* End: This code is added by iTalent as part of iTrack 1859082 */ }); $(document).ready(function() { if($("#slides .item").length < 2 ) { /* Fixing Single Slide click issue (commented following code)*/ // $(".item").css("left","0px"); $(".item.slidesjs-slide").attr('style', 'left:0px !important'); $(".slidesjs-stop.slidesjs-navigation").trigger('click'); $(".slidesjs-previous").css("display", "none"); $(".slidesjs-next").css("display", "none"); } var items_length = $(".item.slidesjs-slide").length; $(".slidesjs-pagination-item > button").attr("aria-setsize",items_length); $(".slidesjs-next, .slidesjs-pagination-item button").attr("tabindex","-1"); $(".slidesjs-pagination-item button").attr("role", "tab"); $(".slidesjs-previous").attr("tabindex","-1"); $(".slidesjs-next").attr("aria-hidden","true"); $(".slidesjs-previous").attr("aria-hidden","true"); $(".slidesjs-next").attr("aria-label","Next"); $(".slidesjs-previous").attr("aria-label","Previous"); //$(".slidesjs-stop.slidesjs-navigation").attr("role","button"); //$(".slidesjs-play.slidesjs-navigation").attr("role","button"); $(".slidesjs-pagination").attr("role","tablist").attr("aria-busy","true"); $("li.slidesjs-pagination-item").attr("role","list"); $(".item.slidesjs-slide").attr("tabindex","-1"); $(".item.slidesjs-slide").attr("aria-label","item"); /*$(".slidesjs-stop.slidesjs-navigation").on('click', function() { var itemNumber = parseInt($('.slidesjs-pagination-item > a.active').attr('data-slidesjs-item')); $($('.item.slidesjs-slide')[itemNumber]).find('.c-call-to-action').attr('tabindex', '0'); });*/ $(".slidesjs-stop.slidesjs-navigation, .slidesjs-pagination-item > button").on('click keydown', function() { $.each($('.item.slidesjs-slide'),function(i,el){ $(el).find('.c-call-to-action').attr('tabindex', '-1'); }); var itemNumber = parseInt($('.slidesjs-pagination-item > button.active').attr('data-slidesjs-item')); $($('.item.slidesjs-slide')[itemNumber]).find('.c-call-to-action').attr('tabindex', '0'); }); $(".slidesjs-play.slidesjs-navigation").on('click', function() { $.each($('.item.slidesjs-slide'),function(i,el){ $(el).find('.c-call-to-action').attr('tabindex', '-1'); }); }); $(".slidesjs-pagination-item button").keyup(function(e){ var keyCode = e.keyCode || e.which; if (keyCode == 9) { e.preventDefault(); $(".slidesjs-stop.slidesjs-navigation").trigger('click').blur(); $("button.active").focus(); } }); $(".slidesjs-play").on("click",function (event) { if (event.handleObj.type === "click") { $(".slidesjs-stop").focus(); } else if(event.handleObj.type === "keydown"){ if (event.which === 13 && $(event.target).hasClass("slidesjs-play")) { $(".slidesjs-stop").focus(); } } }); $(".slidesjs-stop").on("click",function (event) { if (event.handleObj.type === "click") { $(".slidesjs-play").focus(); } else if(event.handleObj.type === "keydown"){ if (event.which === 13 && $(event.target).hasClass("slidesjs-stop")) { $(".slidesjs-play").focus(); } } }); $(".slidesjs-pagination-item").keydown(function(e){ switch (e.which){ case 37: //left arrow key $(".slidesjs-previous.slidesjs-navigation").trigger('click'); e.preventDefault(); break; case 39: //right arrow key $(".slidesjs-next.slidesjs-navigation").trigger('click'); e.preventDefault(); break; default: return; } $(".slidesjs-pagination-item button.active").focus(); }); }); // Start This code is added by iTalent as part of iTrack 1859082 $(document).ready(function(){ $("#tip1").attr("aria-hidden","true").addClass("hidden"); $("#tip2").attr("aria-hidden","true").addClass("hidden"); $(".slidesjs-stop.slidesjs-navigation, .slidesjs-play.slidesjs-navigation").attr('title', ''); $("a#playtitle").focus(function(){ $("#tip1").attr("aria-hidden","false").removeClass("hidden"); }); $("a#playtitle").mouseover(function(){ $("#tip1").attr("aria-hidden","false").removeClass("hidden"); }); $("a#playtitle").blur(function(){ $("#tip1").attr("aria-hidden","true").addClass("hidden"); }); $("a#playtitle").mouseleave(function(){ $("#tip1").attr("aria-hidden","true").addClass("hidden"); }); $("a#play").keydown(function(ev){ if (ev.which ==27) { $("#tip1").attr("aria-hidden","true").addClass("hidden"); ev.preventDefault(); return false; } }); $("a#stoptitle").focus(function(){ $("#tip2").attr("aria-hidden","false").removeClass("hidden"); }); $("a#stoptitle").mouseover(function(){ $("#tip2").attr("aria-hidden","false").removeClass("hidden"); }); $("a#stoptitle").blur(function(){ $("#tip2").attr("aria-hidden","true").addClass("hidden"); }); $("a#stoptitle").mouseleave(function(){ $("#tip2").attr("aria-hidden","true").addClass("hidden"); }); $("a#stoptitle").keydown(function(ev){ if (ev.which ==27) { $("#tip2").attr("aria-hidden","true").addClass("hidden"); ev.preventDefault(); return false; } }); }); // End This code is added by iTalent as part of iTrack 1859082
Top Solution Authors