{"id":417,"date":"2018-06-23T20:18:01","date_gmt":"2018-06-23T20:18:01","guid":{"rendered":"http:\/\/dbtut.com\/?p=417"},"modified":"2018-11-08T11:19:56","modified_gmt":"2018-11-08T11:19:56","slug":"join-types-in-sql-server-execution-plan","status":"publish","type":"post","link":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/","title":{"rendered":"JOIN Types In SQL Server Execution Plan"},"content":{"rendered":"<p>&nbsp;<\/p>\n<p>In this article, we will examine the join types in the execution plan.<\/p>\n<p>Before you read this article, you can get more from this article if you read the articles named &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/18\/what-is-execution-plan-on-sql-server\/\" target=\"_blank\" rel=\"noopener\">What is Execution Plan On SQL Server<\/a>&#8221; and &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/20\/join-types-in-sql-server\/\" target=\"_blank\" rel=\"noopener\">Join Types in SQL Server<\/a>&#8220;.<\/p>\n<p>&nbsp;<\/p>\n<p>SQL Server converts the JOIN expressions written in TSQL to the following join types in the background.<\/p>\n<p>While performing this conversion, it will execute the query in the following join types, which will work better.<\/p>\n<p>&nbsp;<\/p>\n<ul>\n<li>NESTED LOOPS JOIN<\/li>\n<li>MERGE JOIN<\/li>\n<li>HASH JOIN<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>First we will explain the concepts above. We will then use the queries in the topic &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/20\/join-types-in-sql-server\/\" target=\"_blank\" rel=\"noopener\">Join Types in SQL Server<\/a>&#8221; to create examples.<\/p>\n<p>And we&#8217;ll add indexes to some columns to make the query work better.<\/p>\n<p>After adding an index, we will see how the join process (nested loop, merge, hash) in the background changes.<\/p>\n<p>&nbsp;<\/p>\n<h3>NESTED LOOPS JOIN<\/h3>\n<p>Marks one of the tables as inner (inner) and the other as outer, and for each row of the table marked as outer, reads each row in the table marked inner.<\/p>\n<p>If one of the tables is small and the other is large and the large table has an index in the join column, then this join type will work very well. The following picture will be a good example for you to understand the logic of Nested Loop Join.<\/p>\n<p>As you can see in the picture below, for each row of the table marked as outer on the left, all rows of the table marked as inner on the right side are checked.<\/p>\n<p>If there is an index on the join column of the table marked as outer, it will work very well.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\" width=\"539\" height=\"352\" \/><\/p>\n<h3><\/h3>\n<h3>MERGE JOIN<\/h3>\n<p>If the two tables to join are not small and the two tables are ordered according to the join columns (if there is an index in the join columns), merge join will be the most efficient option.<\/p>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s go through a sample,<\/p>\n<p>Consider a join as follows.<\/p>\n<pre class=\"lang:default decode:true\">Select * from table1 INNER JOIN table2 ON table1.a = table2.b\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>In column a in the first table there is an index in ordered structure,<\/p>\n<p>In column b in the secondary table there is an index in ordered structure too.<\/p>\n<p>As a result of such a join, merge join will work very well because both of the columns to be joined are in ordered structure.<\/p>\n<p>Merge Join compares two columns in a join with these two columns, and if it is equal, it returns the result.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/589-1.png\" width=\"678\" height=\"228\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h3>HASH JOIN<\/h3>\n<p>The type of join we do not like. We do not want to see in the execution plan.<\/p>\n<p>If large, unordered, and non-indexed tables enter the join process, SQL Server will have to use the HASH JOIN method to join these two tables.<\/p>\n<p>A hash table is created in the memory by taking the smallest of the two tables.<\/p>\n<p>Then the large table is scanned and the hash value of the large table is compared to the hash value of the hash table in the memory, and equal values \u200b\u200bare added to the result list.<\/p>\n<p>Below you can find the image showing the hash join.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/848.png\" width=\"668\" height=\"450\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h4>Which of these join methods is more efficient?<\/h4>\n<h4>Which join type should we try to convert to which join type?<\/h4>\n<p>&nbsp;<\/p>\n<p>When we told Hash join, we said we do not like this join type.<\/p>\n<p>Let&#8217;s illustrate how we can convert a hash join to other join types. I will proceed using queries from my previous article, &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/20\/join-types-in-sql-server\/\" target=\"_blank\" rel=\"noopener\">Join Types in SQL Server<\/a>&#8220;.<\/p>\n<p>&nbsp;<\/p>\n<p>For example, let&#8217;s open a new query page and paste the\u00a0 INNER JOIN query.<\/p>\n<pre>select s.CityName,my.FoodName from City s\r\nINNER JOIN FamousFood my ON s.ID=my.CityID<\/pre>\n<p>&nbsp;<\/p>\n<p>Next, let&#8217;s look at how to create the estimated execution plan without running the query by clicking on the Display Estimated Execution plan as shown in the picture below.<\/p>\n<p>Since there are no indexes in both tables, Table Scan has performed on both tables, and Hash Join has performed afterwards.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/266.png\" width=\"482\" height=\"209\" \/><\/p>\n<p>Let&#8217;s run the same script again by adding an index to the join column. For example, add the index to the CityID column in the FamousFood table using the following script.<\/p>\n<pre class=\"lang:default decode:true\">USE [Test]\r\nGO\r\nCREATE NONCLUSTERED INDEX [IX_CityID] ON [dbo].[FamousFood]\r\n(\r\n[CityID] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, \r\nONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\r\nGO<\/pre>\n<p>&nbsp;<\/p>\n<p lang=\"en-US\">After adding our index, let&#8217;s look at the estimated execution plan just as we did before.<\/p>\n<p>We always read the execution plan starting from the right side. When we look at the following execution plan;<\/p>\n<p>First, in the FamousFood table we have index seek from the index named IX_CityID (It goes to the index and finds records that it searched for without searching all of the index)<\/p>\n<p>Later, it made a table scan (It goes to the index and finds records that it searched for with scanning all of the index) from the City table and concatenated the two results with Nested Loop method,<\/p>\n<p>Since there is no Clustered Index in the FamousFood table,Sql server uses row id from index named IX_CityID to do RID Lookup and brings the other columns from the table.<\/p>\n<p>Finally, the results from the previous join and RID Lookup are combined with the Nested Loops join type to produce the result.<\/p>\n<p>&nbsp;<\/p>\n<h4><span lang=\"en-US\">What is RID Lookup ?,<\/span><\/h4>\n<h4><span lang=\"tr\">What is <\/span><span lang=\"en-US\">Clustered Index<\/span><span lang=\"tr\">?<\/span><span lang=\"en-US\">,<\/span><\/h4>\n<h4><span lang=\"en-US\">What are the differences between Clustered and NonClustered Indexes? <\/span><\/h4>\n<p>&nbsp;<\/p>\n<p><span lang=\"en-US\">The answers to your questions can be found in the following articles.<\/span><\/p>\n<p>&#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/12\/index-concept-and-performance-effect-on-sql-server\/\" target=\"_blank\" rel=\"noopener\">Index Concept and Performance Effect on SQL Server<\/a>&#8220;,<\/p>\n<p>&#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/13\/statistic-concept-and-performance-effect-on-sql-server\/\" target=\"_blank\" rel=\"noopener\">Statistics Concept and Performance Effect on SQL Server<\/a>&#8220;,(Scan, seek operations have been examined in detail in this article.)<\/p>\n<p>&#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/10\/difference-between-clustered-index-and-non-clustered-index\/\" target=\"_blank\" rel=\"noopener\">Difference Between Clustered Index and Non Clustered Index<\/a>&#8221;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/311-1.png\" width=\"528\" height=\"221\" \/><\/p>\n<p>&nbsp;<\/p>\n<p lang=\"en-US\">Let&#8217;s put a Clustered Index on the FamousFood table with the following script and then look at the execution plan again.<\/p>\n<pre class=\"lang:default decode:true\">USE [Test]\r\nGO\r\nCREATE UNIQUE CLUSTERED INDEX [IX_Clustered] ON [dbo].[FamousFood]\r\n(\r\n[ID] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, \r\nDROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\r\nGO<\/pre>\n<p>&nbsp;<\/p>\n<p>Looking back at the execution plan, Key Lookup came in instead of RID Lookup as follows.<\/p>\n<p>You can find the RID Lookup and Key Lookup difference in the article &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/10\/difference-between-clustered-index-and-non-clustered-index\/\" target=\"_blank\" rel=\"noopener\">Difference Between Clustered Index and Non Clustered Index<\/a>&#8220;.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/209.png\" width=\"614\" height=\"211\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Let&#8217;s keep improving our query. To remove the TableScan for the Cities table, let&#8217;s put an index into the ID column that joins the Cities table.<\/p>\n<p>I would prefer to place the Clustered Index in this example because the ID column is the determinative column for the Cities table.<\/p>\n<pre class=\"lang:default decode:true\">USE [Test]\r\nGO\r\nCREATE UNIQUE CLUSTERED INDEX [IX_Clustered] ON [dbo].[City]\r\n(\r\n[ID] ASC\r\n)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, \r\nDROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\r\nGO<\/pre>\n<p>&nbsp;<\/p>\n<p lang=\"en-US\">let&#8217;s look at the execution plan again.<\/p>\n<p><span lang=\"en-US\">As you can see below, the table scan for the Cit<\/span><span lang=\"tr\">y<\/span><span lang=\"en-US\"> tab<\/span><span lang=\"tr\">le<\/span><span lang=\"en-US\"> returned to the Clustered Index seek.<\/span><\/p>\n<p><span lang=\"en-US\">SQL Server was performing Index Seek + Key Lookup in a previous execution plan. <\/span><\/p>\n<p><span lang=\"en-US\">As you remember the previous execution plan, the sql server first performs index seek, then join, <\/span><span lang=\"tr\">then <\/span><span lang=\"en-US\">key lookup, and then join again.<\/span><\/p>\n<p lang=\"en-US\">Instead, it decided that the cost of the Clustered Index Scan + single join process was less.<\/p>\n<p lang=\"en-US\">Of course, the number of columns in the table, the size of the data can change the sql server&#8217;s decision.<\/p>\n<p lang=\"en-US\"><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/998.png\" width=\"529\" height=\"197\" \/><\/p>\n<p>As you see in our query, the CityID column of the FamousFood table is joined and the FoodName column of the FamousFood table is also selected in the select section.<\/p>\n<p>We can use the following script to add the FoodName column to the included field of the IX_CityID index that we created, so that we can use the IX_CityID index instead of the Clustered Index Scan.<\/p>\n<pre class=\"lang:default decode:true\">USE [Test]\r\nGO\r\nSET ANSI_PADDING ON\r\nGO\r\nCREATE NONCLUSTERED INDEX [IX_CityID] ON [dbo].[FamousFood]\r\n(\r\n[CityID] ASC\r\n)\r\nINCLUDE ([FoodName]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, \r\nDROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\nGO\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>After running the script, I see that the clustered index scan process is still on the execution plan.<\/p>\n<p>Let&#8217;s add a few columns to the FamousFood table and fill these columns with meaningless values.<\/p>\n<pre class=\"lang:default decode:true\">Use Test\r\nGO\r\nBEGIN TRANSACTION\r\nSET QUOTED_IDENTIFIER ON\r\nSET ARITHABORT ON\r\nSET NUMERIC_ROUNDABORT OFF\r\nSET CONCAT_NULL_YIELDS_NULL ON\r\nSET ANSI_NULLS ON\r\nSET ANSI_PADDING ON\r\nSET ANSI_WARNINGS ON\r\nCOMMIT\r\nBEGIN TRANSACTION\r\nGO\r\nALTER TABLE dbo.FamousFood ADD\r\ntest nchar(10) NULL,\r\ntest2 nchar(10) NULL,\r\ntest3 nchar(10) NULL,\r\ntest4 nchar(10) NULL,\r\ntest5 nchar(10) NULL\r\nGO\r\nALTER TABLE dbo.FamousFood SET (LOCK_ESCALATION = TABLE)\r\nGO\r\nCOMMIT\r\nUSE [Test]\r\nGO\r\nUPDATE [dbo].[FamousFood]\r\n\u00a0\u00a0 SET [test] = '0123456789'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ,[test2] = '0123456789'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ,[test3] = '0123456789'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ,[test4] = '0123456789'\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 ,[test5] = '0123456789'\r\nGO\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Then we will insert the table to enlarge the table.<\/p>\n<p>Let&#8217;s run the following script to set the value of the ID column to auto-increment before doing the Insert operation.<\/p>\n<pre class=\"lang:default decode:true\">BEGIN TRANSACTION\r\nSET QUOTED_IDENTIFIER ON\r\nSET ARITHABORT ON\r\nSET NUMERIC_ROUNDABORT OFF\r\nSET CONCAT_NULL_YIELDS_NULL ON\r\nSET ANSI_NULLS ON\r\nSET ANSI_PADDING ON\r\nSET ANSI_WARNINGS ON\r\nCOMMIT\r\nBEGIN TRANSACTION\r\nGO\r\nCREATE TABLE dbo.Tmp_FamousFood\r\n(\r\nID int NOT NULL IDENTITY (1, 1),\r\nCityID int NULL,\r\nFoodName varchar(100) NULL,\r\ntest nchar(10) NULL,\r\ntest2 nchar(10) NULL,\r\ntest3 nchar(10) NULL,\r\ntest4 nchar(10) NULL,\r\ntest5 nchar(10) NULL\r\n)\u00a0 ON [PRIMARY]\r\nGO\r\nALTER TABLE dbo.Tmp_FamousFood SET (LOCK_ESCALATION = TABLE)\r\nGO\r\nSET IDENTITY_INSERT dbo.Tmp_FamousFood ON\r\nGO\r\nIF EXISTS(SELECT * FROM dbo.FamousFood )\r\nEXEC('INSERT INTO dbo.Tmp_FamousFood (ID, CityID, FoodName, test, test2, test3, test4, test5)\r\nSELECT ID, CityID, FoodName, test, test2, test3, test4, test5 FROM dbo.FamousFood WITH (HOLDLOCK TABLOCKX)')\r\nGO\r\nSET IDENTITY_INSERT dbo.Tmp_FamousFood OFF\r\nGO\r\nDROP TABLE dbo.FamousFood\r\nGO\r\nEXECUTE sp_rename N'dbo.Tmp_FamousFood ', N'FamousFood ', 'OBJECT'\r\nGO\r\nCREATE UNIQUE CLUSTERED INDEX IX_Clustered ON dbo.FamousFood\r\n(\r\nID\r\n) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\nGO\r\nCREATE NONCLUSTERED INDEX IX_CityID ON dbo.FamousFood\r\n(\r\nCityID\r\n) INCLUDE (FoodName)\r\n\u00a0WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]\r\nGO\r\nCOMMIT\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Then we will I add\u00a0 records to our table using the following script. You can run the script for 1 minute.<\/p>\n<pre class=\"lang:default decode:true\">USE [Test]\r\nGO\r\nwhile(1=1)\r\nBEGIN\r\nINSERT INTO [dbo].[FamousFood]\r\n([CityID]\r\n,[FoodName]\r\n,[test]\r\n,[test2]\r\n,[test3]\r\n,[test4]\r\n,[test5])\r\nVALUES\r\n(1\r\n,'Test'\r\n,'0123456789'\r\n,'0123456789'\r\n,'0123456789'\r\n,'0123456789'\r\n,'0123456789')\r\nEND<\/pre>\n<p>&nbsp;<\/p>\n<p>Once we stop the script, let&#8217;s look at the execution plan again.<\/p>\n<p>As you can see, I only increased the number of columns and the number of records in the FamousFood table (setting the ID to automatically increase does not affect the execution plan)<\/p>\n<p>SQL Server did not choose to use the nonclustered index we created in the FamousFood table when the number of rows (data density) in the table was low.<\/p>\n<p>Since the table is small, it has been estimated that it would be less costly to bring the data over the clustered index.<\/p>\n<p>But once the data in the table grows, it chose to use the index to cover our query (an index that contains the columns of the select and where expressions).<\/p>\n<p>Another point that attracts our attention is Merge Join.<\/p>\n<p>It did not use merge join before.<\/p>\n<p>Because it was using the clustered index instead of the nonclustered index in the FamousFoods table when doing the join process.<\/p>\n<p>When we look at the execution plan below;<\/p>\n<p>ClusteredIndexScan was made using the index named IX_Clustered of the City table. IX_Clustered index has an ID column.<\/p>\n<p>IndexScan has been made using the index names IX_CityID of the FamousFood Table. IX_CityID index has an CityID column.<\/p>\n<p>While the table was small, the ID column of the City table was joined with the ID column of the FamousFood table.<\/p>\n<p>When the table grows, it performs merge join on the ordered columns (with index).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/270.png\" width=\"518\" height=\"192\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>As you can see, there can be hash join, nested loop or merge join on the same query, based on index and number of records in the table.<\/p>\n<p>Why does SQL Server perform Index Scan and Clustered Index Scan?<\/p>\n<p>Can we convert these operations to Clustered Index Seek and Index Seek?<\/p>\n<p>Because the result set of the join process is getting almost all the rows.<\/p>\n<p>By filtering the query as follows, the execution plan will return to index seek and clustered index seek.<\/p>\n<pre class=\"lang:default decode:true\">select s.CityName,my.FoodName from City s\r\nINNER JOIN FamousFood my ON\u00a0 s.ID=my.CityID\r\nwhere my.CityID=42<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/406.png\" width=\"479\" height=\"173\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>In the article &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/24\/spool-concept-in-execution-planeager-spool-lazy-spool\/\" target=\"_blank\" rel=\"noopener\">Spool Concept in Execution Plan(Eager Spool, Lazy Spool)<\/a>&#8221; we will continue to enter the details of the execution plan.<\/p>\n<p>You can reach our articles by searching for words that contain the subject you are interested.<\/p>\n<p>&nbsp;<\/p>\n<h4>What are the differences between Index Scan and Index Seek?<\/h4>\n<h4>What are the differences between RID Lookup and KeyLookup?<\/h4>\n<p>&nbsp;<\/p>\n<p>The answers to these questions can be found in the article titled &#8220;<a href=\"http:\/\/dbtut.com\/index.php\/2018\/06\/13\/statistic-concept-and-performance-effect-on-sql-server\/\" target=\"_blank\" rel=\"noopener\">Statistics Concept and Performance Effect on SQL Server<\/a>&#8220;.<\/p>\n\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_417\" class=\"pvc_stats all  \" data-element-id=\"417\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/dbtut.com\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; In this article, we will examine the join types in the execution plan. Before you read this article, you can get more from this article if you read the articles named &#8220;What is Execution Plan On SQL Server&#8221; and &#8220;Join Types in SQL Server&#8220;. &nbsp; SQL Server converts the JOIN expressions written in TSQL &hellip;<\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_417\" class=\"pvc_stats all  \" data-element-id=\"417\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/dbtut.com\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[3],"tags":[173,290,289,462,463,464,342,334,338,171,187,456,459,460,461,341,455,175,343,337,336,174,457,458,180,121],"class_list":["post-417","post","type-post","status-publish","format-standard","","category-mssql","tag-clustered-index","tag-clustered-index-scan","tag-clustered-index-seek","tag-convert-hash-join","tag-convert-merge-join","tag-convert-nested-loop","tag-eager-spool","tag-execution-plan","tag-hash-join","tag-index","tag-index-seek","tag-inner","tag-inner-relation","tag-join-methods","tag-join-methods-on-sql-server","tag-join-types","tag-join-types-in-execution-plan","tag-key-lookup","tag-lazy-spool","tag-merge-join","tag-nested-loop","tag-nonclustered-index","tag-outer","tag-outer-relation","tag-rid-lookup","tag-statistics"],"aioseo_notices":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.9 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>JOIN Types In SQL Server Execution Plan - Database Tutorials<\/title>\n<meta name=\"description\" content=\"JOIN Types In SQL Server Execution Plan\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"JOIN Types In SQL Server Execution Plan - Database Tutorials\" \/>\n<meta property=\"og:description\" content=\"JOIN Types In SQL Server Execution Plan\" \/>\n<meta property=\"og:url\" content=\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\" \/>\n<meta property=\"og:site_name\" content=\"Database Tutorials\" \/>\n<meta property=\"article:published_time\" content=\"2018-06-23T20:18:01+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-11-08T11:19:56+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\" \/>\n<meta name=\"author\" content=\"dbtut\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"dbtut\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\"},\"author\":{\"name\":\"dbtut\",\"@id\":\"https:\/\/dbtut.com\/#\/schema\/person\/fc047c39e1e53dce28fc4253529ea408\"},\"headline\":\"JOIN Types In SQL Server Execution Plan\",\"datePublished\":\"2018-06-23T20:18:01+00:00\",\"dateModified\":\"2018-11-08T11:19:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\"},\"wordCount\":1694,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/dbtut.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\",\"keywords\":[\"clustered index\",\"Clustered Index Scan\",\"Clustered Index Seek\",\"convert hash join\",\"convert merge join\",\"convert nested loop\",\"Eager Spool\",\"Execution Plan\",\"hash join\",\"Index\",\"index seek\",\"inner\",\"inner relation\",\"join methods\",\"join methods on sql server\",\"join types\",\"join types in execution plan\",\"key lookup\",\"Lazy Spool\",\"merge join\",\"Nested loop\",\"nonclustered index\",\"outer\",\"outer relation\",\"RID Lookup\",\"statistics\"],\"articleSection\":[\"MSSQL\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\",\"url\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\",\"name\":\"JOIN Types In SQL Server Execution Plan - Database Tutorials\",\"isPartOf\":{\"@id\":\"https:\/\/dbtut.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage\"},\"thumbnailUrl\":\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\",\"datePublished\":\"2018-06-23T20:18:01+00:00\",\"dateModified\":\"2018-11-08T11:19:56+00:00\",\"description\":\"JOIN Types In SQL Server Execution Plan\",\"breadcrumb\":{\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage\",\"url\":\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\",\"contentUrl\":\"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/dbtut.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"JOIN Types In SQL Server Execution Plan\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/dbtut.com\/#website\",\"url\":\"https:\/\/dbtut.com\/\",\"name\":\"Database Tutorials\",\"description\":\"MSSQL, Oracle, PostgreSQL, MySQL, MariaDB, DB2, Sybase, Teradata, Big Data, NOSQL, MongoDB, Couchbase, Cassandra, Windows, Linux\",\"publisher\":{\"@id\":\"https:\/\/dbtut.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/dbtut.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/dbtut.com\/#organization\",\"name\":\"dbtut\",\"url\":\"https:\/\/dbtut.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dbtut.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/dbtut.com\/wp-content\/uploads\/2021\/02\/dbtutlogo.jpg\",\"contentUrl\":\"https:\/\/dbtut.com\/wp-content\/uploads\/2021\/02\/dbtutlogo.jpg\",\"width\":223,\"height\":36,\"caption\":\"dbtut\"},\"image\":{\"@id\":\"https:\/\/dbtut.com\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/dbtut.com\/#\/schema\/person\/fc047c39e1e53dce28fc4253529ea408\",\"name\":\"dbtut\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/dbtut.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c322c32021bf651d9e103b183963c479a9c9791ead0715f4348203496c39aa54?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c322c32021bf651d9e103b183963c479a9c9791ead0715f4348203496c39aa54?s=96&d=mm&r=g\",\"caption\":\"dbtut\"},\"description\":\"We are a team with over 10 years of database management and BI experience. Our Expertises: Oracle, SQL Server, PostgreSQL, MySQL, MongoDB, Elasticsearch, Kibana, Grafana.\",\"sameAs\":[\"http:\/\/NurullahCAKIR\"],\"url\":\"https:\/\/dbtut.com\/index.php\/author\/dbtut\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"JOIN Types In SQL Server Execution Plan - Database Tutorials","description":"JOIN Types In SQL Server Execution Plan","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/","og_locale":"en_US","og_type":"article","og_title":"JOIN Types In SQL Server Execution Plan - Database Tutorials","og_description":"JOIN Types In SQL Server Execution Plan","og_url":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/","og_site_name":"Database Tutorials","article_published_time":"2018-06-23T20:18:01+00:00","article_modified_time":"2018-11-08T11:19:56+00:00","og_image":[{"url":"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png","type":"","width":"","height":""}],"author":"dbtut","twitter_card":"summary_large_image","twitter_misc":{"Written by":"dbtut","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#article","isPartOf":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/"},"author":{"name":"dbtut","@id":"https:\/\/dbtut.com\/#\/schema\/person\/fc047c39e1e53dce28fc4253529ea408"},"headline":"JOIN Types In SQL Server Execution Plan","datePublished":"2018-06-23T20:18:01+00:00","dateModified":"2018-11-08T11:19:56+00:00","mainEntityOfPage":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/"},"wordCount":1694,"commentCount":0,"publisher":{"@id":"https:\/\/dbtut.com\/#organization"},"image":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage"},"thumbnailUrl":"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png","keywords":["clustered index","Clustered Index Scan","Clustered Index Seek","convert hash join","convert merge join","convert nested loop","Eager Spool","Execution Plan","hash join","Index","index seek","inner","inner relation","join methods","join methods on sql server","join types","join types in execution plan","key lookup","Lazy Spool","merge join","Nested loop","nonclustered index","outer","outer relation","RID Lookup","statistics"],"articleSection":["MSSQL"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/","url":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/","name":"JOIN Types In SQL Server Execution Plan - Database Tutorials","isPartOf":{"@id":"https:\/\/dbtut.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage"},"image":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage"},"thumbnailUrl":"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png","datePublished":"2018-06-23T20:18:01+00:00","dateModified":"2018-11-08T11:19:56+00:00","description":"JOIN Types In SQL Server Execution Plan","breadcrumb":{"@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#primaryimage","url":"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png","contentUrl":"http:\/\/dbtut.com\/wp-content\/uploads\/2018\/06\/401-1.png"},{"@type":"BreadcrumbList","@id":"https:\/\/dbtut.com\/index.php\/2018\/06\/23\/join-types-in-sql-server-execution-plan\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/dbtut.com\/"},{"@type":"ListItem","position":2,"name":"JOIN Types In SQL Server Execution Plan"}]},{"@type":"WebSite","@id":"https:\/\/dbtut.com\/#website","url":"https:\/\/dbtut.com\/","name":"Database Tutorials","description":"MSSQL, Oracle, PostgreSQL, MySQL, MariaDB, DB2, Sybase, Teradata, Big Data, NOSQL, MongoDB, Couchbase, Cassandra, Windows, Linux","publisher":{"@id":"https:\/\/dbtut.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/dbtut.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/dbtut.com\/#organization","name":"dbtut","url":"https:\/\/dbtut.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dbtut.com\/#\/schema\/logo\/image\/","url":"https:\/\/dbtut.com\/wp-content\/uploads\/2021\/02\/dbtutlogo.jpg","contentUrl":"https:\/\/dbtut.com\/wp-content\/uploads\/2021\/02\/dbtutlogo.jpg","width":223,"height":36,"caption":"dbtut"},"image":{"@id":"https:\/\/dbtut.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/dbtut.com\/#\/schema\/person\/fc047c39e1e53dce28fc4253529ea408","name":"dbtut","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/dbtut.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/c322c32021bf651d9e103b183963c479a9c9791ead0715f4348203496c39aa54?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c322c32021bf651d9e103b183963c479a9c9791ead0715f4348203496c39aa54?s=96&d=mm&r=g","caption":"dbtut"},"description":"We are a team with over 10 years of database management and BI experience. Our Expertises: Oracle, SQL Server, PostgreSQL, MySQL, MongoDB, Elasticsearch, Kibana, Grafana.","sameAs":["http:\/\/NurullahCAKIR"],"url":"https:\/\/dbtut.com\/index.php\/author\/dbtut\/"}]}},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/posts\/417","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/comments?post=417"}],"version-history":[{"count":0,"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/posts\/417\/revisions"}],"wp:attachment":[{"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/media?parent=417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/categories?post=417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dbtut.com\/index.php\/wp-json\/wp\/v2\/tags?post=417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}