{"id":93971,"date":"2022-04-21T18:40:53","date_gmt":"2022-04-21T18:40:53","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=93971"},"modified":"2022-04-21T18:40:53","modified_gmt":"2022-04-21T18:40:53","slug":"the-ins-and-outs-of-joining-tables-in-sql-server","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/the-ins-and-outs-of-joining-tables-in-sql-server\/","title":{"rendered":"The ins and outs of joining tables in SQL Server"},"content":{"rendered":"<p>There are times when you need to join multiple tables to retrieve the result set needed. SQL allows for multiple tables to be included in the <code>FROM<\/code> clause along with the criteria for how to join tables. In SQL Server there are a number of different ways to join tables. In this article, I will be discussing the ins and outs of joining two tables in SQL Server using the inner and outer join operators.<\/p>\n<h2>The Three Parts of a Join Operations<\/h2>\n<p>When two tables are joined using a join operation, the records of the two sets are logically separated into three different buckets. Those different buckets can be represented using a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Venn_diagram\">Venn diagram<\/a> like the one in Figure 1.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93972\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-27.png\" alt=\"An images showing a Venn diagram. The left is Yellow holding rows from Set A, the right is blue holding rows from Set b. The intersection is brown.\" width=\"472\" height=\"368\" \/><\/p>\n<p><strong>Figure 1: Three parts of the JOIN Operation<\/strong><\/p>\n<p>Each section of the Venn diagram is represented by a different color: yellow, brown, and blue. The members of Set A that don\u2019t match with SET B will be contained in the yellow bucket. Members of SET A and B that match end up in the brown bucket. Lastly, the members in the blue bucket are those members in SET B that don\u2019t match with SET A. The Venn diagram can visually represent the resulting sets created when an inner or outer join operation is performed.<\/p>\n<h2>Test Data<\/h2>\n<p>To demonstrate how the different inner and outer join operations work, the examples use two test data tables created in the <em>tempdb<\/em> database. These two tables are <em>SetA <\/em>and <em>SetB. <\/em>If you want to follow along and execute the examples in this article, you can create the two test tables using the code in Listing 1.<\/p>\n<p><strong>Listing 1: Creating <em>SetA <\/em>and <em>SetB<\/em><\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO\r\nCREATE TABLE SetA (Col1 tinyint, Col2 char(1));\r\nINSERT INTO SetA VALUES \r\n\t(1, 'A'), (2, 'B');\r\nCREATE TABLE SetB (ColA tinyint, ColB char(1), ColC char(1));\r\nINSERT INTO SetB VALUES\r\n\t(1, 'A', 'B'), (2, 'C', 'D'), (3, 'E', 'F');\r\nGO<\/pre>\n<p>The two tables created when Listing 1 is run can be represented by the Venn diagram in Figure 2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93973\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-28.png\" alt=\"An image showing a Venn diagram. Set A is a yellow circle. Set B is a blue circle. They do not intersect.\" width=\"546\" height=\"293\" \/><\/p>\n<p><strong>Figure 2: Tables <em>SetA <\/em>and <em>SetB<\/em><\/strong><\/p>\n<p>Table <em>SetA<\/em> consists of a set with two columns (<code>Col1<\/code> and <code>Col2<\/code>) and two different rows. Table <em>SetB<\/em> has three columns (<code>ColA<\/code>, <code>ColB<\/code>, and <code>ColC<\/code>) and contains three different rows.<\/p>\n<h2>INNER JOIN operator<\/h2>\n<p>The INNER JOIN operator is probably the most used join operation found in T-SQL code. This operator returns rows that match when two sets are joined based on one or more search criteria. The brown section in Figure 3 represents the result set of an INNER JOIN operation between the two test data tables, <em>SetA <\/em>and <em>SetB, <\/em>based on the search criterion <code>SetA.Col2 = SetB.ColB<\/code><em>.<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93974\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-29.png\" alt=\"An images showing a Venn diagram. The yellow left section has rows that do not match from SetA 2,B. The middle brown section has values that match, the right blue section has rows that do not match\" width=\"373\" height=\"300\" \/> <br \/>\n<strong>Figure 3: Venn Diagram representing an INNER JOIN <\/strong><\/p>\n<p>The T-SQL code in Listing 2 demonstrates one way to write an <code>INNER<\/code> <code>JOIN<\/code> operation to return the matching rows found in the brown section of the Venn diagram in Figure 3.<\/p>\n<p><strong>Listing 2: Performing INNER JOIN operation<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO\r\nSELECT * FROM SetA INNER JOIN SetB\r\nON SetA.Col2 = SetB.ColB;\r\nGO <\/pre>\n<p>Report 1 shows the results when the code in Listing 2 is executed.<\/p>\n<p><strong>Report 1: Output produced when Listing 2 is run<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"172\" height=\"36\" class=\"wp-image-93975\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-30.png\" \/><\/p>\n<p>The code in Listing 2 identifies the two tables, <em>SetA <\/em>and <em>SetB<\/em>, to be joined in the FROM clause, with the <code>INNER<\/code> <code>JOIN<\/code> operator between them, followed by an ON clause. The ON clause identifies the search criteria to be used when joining the two tables. In Listing 2, the ON clause specifies <code>SetA.Col2 = SetB.ColB<\/code> as the search criterion. This criterion specifies that the INNER JOIN operation is to find all the rows in <em>SetA<\/em> that have corresponding matching rows in <em>SetB, <\/em>where <code>Col2<\/code> in <em>SetA <\/em>is equal to <code>ColB<\/code> in <em>SetB<\/em>. Let\u2019s walk through how this join operation works.<\/p>\n<p>While the technical details of how the engine joins the tables together are beyond the scope of this article, logically, SQL Server has to compare each row in <em>SetA <\/em>with each row in <em>SetB <\/em>to determine which rows match the join condition. To perform this comparison, SQL Server first finds the value for <code>Col2<\/code> in the first row of <em>SetA, <\/em>which has a value of <em>A<\/em>. Next SQL Server scans the rows in <em>SetB<\/em> to determine if any <code>ColB<\/code> values match the value of <em>A<\/em>. SQL Server only finds a single row in <em>SetB <\/em>with a value of <em>A<\/em> in <code>ColB<\/code><em>. <\/em>SQL server then repeats this compare process between <em>SetA <\/em>and <em>SetB <\/em>for each row in <em>SetA, <\/em>trying to find matches. The second row in <em>SetA <\/em>doesn\u2019t match with any rows in <em>SetB. <\/em>Once all the comparisons between <em>SetA <\/em>and <em>SetB <\/em>are completed, the column values from matching rows between<em> SetA <\/em>and <em>SetB<\/em> are merged, and the final results of the <code>INNER<\/code> <code>JOIN<\/code> are returned to the calling program.<\/p>\n<p>The matching search criterion used in Listing 2 uses SQL-92 join syntax. This is not the only join syntax supported by SQL Server.<\/p>\n<h2>Comparing join syntax between versions of SQL<\/h2>\n<p>In the early days of SQL, the various database vendors started implementing SQL code differently. This led to inconsistencies in how SQL code was written. In the 1980s, the American National Standards Institute (ANSI) started working on a SQL language standard. Over time the SQL standards changed the way joins could be written.<\/p>\n<p>The code in Listing 2 above uses SQL-92 join syntax, which requires a join operator to be specified between the joined sets and an ON clause to identify the join criteria. Prior to SQL-92, a different syntax for joining data was used which is shown in Listing 3.<\/p>\n<p><strong>Listing 3: Performing INNER JOIN using SQL-86 join syntax<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA, SetB\r\nWHERE SetA.Col2 = SetB.ColB;\r\nGO<\/pre>\n<p>The pre-SQL-92 syntax required a comma to be placed between tables being joined and required the join criteria be specified in the <code>WHERE<\/code> clause. By moving the join conditions to the ON clause, the SQL-92 standards separated the join conditions from other search conditions a query might have in the <code>WHERE<\/code> clause. SQL Server still supports the older method of doing the <code>INNER<\/code> <code>JOIN<\/code> operation using pre-SQL-92 syntax, but it no longer supports the outer join method using the pre-SQL-92 syntax. SQL-92 standard is now the recommended standard for doing join operations. For the rest of the join examples in this article, only the ANSI SQL-92 syntax will be shown.<\/p>\n<p>The <code>INNER<\/code> <code>JOIN<\/code> operation returns rows with matching members in both the left and right sides of joined sets. However, there are times when rows might need to be included in result sets even when they don\u2019t match. This is where the outer join operations can help.<\/p>\n<h2>LEFT OUTER JOIN<\/h2>\n<p>The <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation returns all the rows from the table on the left, even when they don\u2019t match any of the rows on the right based on the search criteria. The Venn diagram in Figure 4 logically shows the results of a <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation between <em>SetA <\/em>and <em>SetB<\/em> based the search criterion of <code>SetA.Col2 = SetB.ColB<\/code><em>.<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-93976\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-31.png\" alt=\"An image showing a Venn diagram. The left circle contains all rows from SetA matching SetB and including rows from SetA that don't match. The right section is blue representing SetB rows that don't match\" width=\"438\" height=\"349\" \/><\/p>\n<p><strong>Figure 4: Venn diagram representing LEFT OUTER JOIN<\/strong><\/p>\n<p>By comparing Figure 4 to the Venn diagram for the <code>INNER<\/code> <code>JOIN<\/code> found in Figure 3, the matched row of <em>SetA, <\/em>in the brown section, and the unmatched members in the yellow section of <em>SetA <\/em>have been combined into a single yellow circle. The members of this yellow circle are the members of <em>SetA <\/em>and<em> SetB <\/em>that will be returned from the <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operations. Listing 4 contains the T-SQL code to produce the <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> results represented by Figure 4.<\/p>\n<p><strong>Listing 4: LEFT OUTER JOIN<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA LEFT OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB;\r\nGO<\/pre>\n<p>Report 2 shows the output when Listing 4 is executed.<\/p>\n<p><strong>Report 2: Output from running Listing 4<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"184\" height=\"50\" class=\"wp-image-93977\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-32.png\" \/><\/p>\n<p>The results in Report 2 have one row that matched the search criterium and one that didn\u2019t match. The row that didn\u2019t match the column values for the table on the right side of the <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation is set to <code>NULL<\/code>. It is easy to identify those rows that did not match the search criterium by checking to see if any of the columns from the right table is null, as done in Listing 5.<\/p>\n<p><strong>Listing 5: Finding rows on the left that don\u2019t match rows on the right<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA LEFT OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB\r\nWHERE SetB.ColA is null;\r\nGO<\/pre>\n<p>When the code in Listing 5 is run, only the last row in Report 2 will be returned.<\/p>\n<h2>RIGHT OUTER JOIN<\/h2>\n<p>The <code>RIGHT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation is similar to the <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation. The difference is that all the rows in the right table are returned, whether or not they match the rows in the left table. To show this consider the code in Listing 6.<\/p>\n<p><strong>Listing 6: Right outer join example<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA \r\nRIGHT OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB;\r\nGO<\/pre>\n<p>Report 3 shows the results when Listing 6 is executed.<\/p>\n<p><strong>Report 3: Results when Listing 6 is run<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"182\" height=\"74\" class=\"wp-image-93978\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-33.png\" \/><\/p>\n<p>Report 3 shows that all the rows from the right table, <em>SetB<\/em>, are returned. For those rows in <em>SetB<\/em> that don\u2019t match <em>SetA,<\/em> the <code>NULL<\/code> value was returned for the <em>SetA <\/em>columns in the result set.<\/p>\n<p>If you need to find all the rows in <em>SetB <\/em>that don\u2019t match the search criterion of <code>SetA.Col2 = SetB.ColB<\/code><em>, <\/em>then the code in Listing 7 can be run.<\/p>\n<p><strong>Listing 7: Finding rows in <em>SetB <\/em>that don\u2019t match <em>SetA<\/em><\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA \r\nRIGHT OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB\r\nWHERE SetA.Col1 is null;\r\nGO<\/pre>\n<p>Report 4 shows the results produced when Listing 7 is executed.<\/p>\n<p><strong>Report 4: Results from running Listing 7<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"178\" height=\"48\" class=\"wp-image-93979\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-34.png\" \/><\/p>\n<h2>FULL OUTER JOIN<\/h2>\n<p>The <code>FULL<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation combines both the <code>LEFT<\/code> and <code>RIGHT<\/code> <code>OUTER<\/code> JOIN and the <code>INNER<\/code> <code>JOIN<\/code> in a single operation. Listing 8 uses the <code>FULL<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operator to find all the rows that match and don\u2019t match the search criterion <code>SetA.Col2 = SetB.ColB<\/code><em>.<\/em><\/p>\n<p><strong>Listing 8: Finding all matching and unmatched rows<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA FULL OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB;\r\nGO<\/pre>\n<p>Report 5 shows the results when Listing 7 is run.<\/p>\n<p><strong>Report 5: Output from Listing 8<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"196\" height=\"86\" class=\"wp-image-93980\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-35.png\" \/><\/p>\n<p>The first row in Report 5 is the only row that meets the search criterion identified in Listing 8, hence why each column has a value. The second row is the left row that doesn\u2019t match any rows on the right; it could be found using the <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code>. The third and fourth rows are the rows from the right that don\u2019t match the rows from the left; they could be found using a <code>RIGHT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operation.<\/p>\n<p>The code in Listing 9 shows how to find only those rows in either the left or right table that don\u2019t match the search criterion.<\/p>\n<p><strong>Listing 9: Using the FULL OUTER JOIN<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO \r\nSELECT * FROM SetA FULL OUTER JOIN SetB\r\nON SetA.Col2 = SetB.ColB\r\nWHERE SetA.Col1 is null or SetB.ColA is null;\r\nGO<\/pre>\n<p>Report 6 shows the output when Listing 9 is executed.<\/p>\n<p><strong>Report 6: Output when Listing 9 is run<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"194\" height=\"62\" class=\"wp-image-93981\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-36.png\" \/><\/p>\n<p>The rows from the left table that don\u2019t match rows in the right table will have nulls in the columns from the right table. Inversely, rows from the right table that don\u2019t match rows in the left table will have nulls identified for all the columns from the left table.<\/p>\n<h2>Using Multiple comparison operations<\/h2>\n<p>The comparison between two sets in a join operation can also have multiple comparison operations, like the code in Listing 10.<\/p>\n<p><strong>Listing 10: Using multiple comparison operators in a join operation<\/strong><\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE tempdb;\r\nGO\r\nSELECT * FROM SetA INNER JOIN SetB \r\nON SetA.Col1 = SetB.ColA AND SetA.Col2 = SetB.ColB;\r\nGO<\/pre>\n<p>When Listing 10 is executed the results in Report 7 is produced.<\/p>\n<p><strong>Report 7: Output when Listing 10 is run<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"168\" height=\"34\" class=\"wp-image-93982\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/04\/word-image-37.png\" \/><\/p>\n<p>In Listing 10 only the rows where both <code>Col1<\/code> from Set1 matches <code>ColA<\/code> from SetB and <code>Col2<\/code> from SetA matches <code>ColB<\/code> from SetB.<\/p>\n<h2>The Ins and Outs of Joining tables<\/h2>\n<p>Joining tables is a key skill every T-SQL programmer needs to master. In this article, I used Venn diagrams and T-SQL code examples to show on how the <code>INNER<\/code> <code>JOIN<\/code>, <code>LEFT<\/code> <code>OUTER<\/code> <code>JOIN<\/code>, <code>RIGHT<\/code> <code>OUTER<\/code> <code>JOIN<\/code> and <code>FULL<\/code> <code>OUTER<\/code> <code>JOIN<\/code> operations can be used to find different sets of records when joining two sets. Hopefully, next time you need to find the rows in one of the three parts produced when joining two sets you will know which join operation and search criteria to use.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most database queries require joining tables. In this article, Greg Larsen explains the ins and outs of joining tables in SQL Server.&hellip;<\/p>\n","protected":false},"author":78478,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,143525],"tags":[5134],"coauthors":[11330],"class_list":["post-93971","post","type-post","status-publish","format-standard","hentry","category-featured","category-learn","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93971","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\/78478"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=93971"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93971\/revisions"}],"predecessor-version":[{"id":94149,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/93971\/revisions\/94149"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=93971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=93971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=93971"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=93971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}