![]() |
|
|
By: Jeremy Kadlec | Read Comments (11) | Print Jeremy is the CTO @ Edgewood Solutions, co-founder of MSSQLTips.com and SQL Server MVP since 2009. Related Tips: 1 | 2 | 3 | 4 | More |
|
I am new to SQL Server and want to learn about the JOIN options. What are all of the JOIN options in SQL Server? What is the significance of each of the options? I am a little confused on the differences and syntax, can you provide some examples and explanations? Are JOINs only for SELECT statements? Check out this tip to learn about SQL Server Joins.
Joining tables to obtain the needed data for a query, script or stored procedure is a key concept as you learn about SQL Server development. In a nutshell, joins are typically performed in the FROM clause of a table or view for the SELECT, INSERT...SELECT, SELECT...INTO, UPDATE and DELETE statements. In previous versions of SQL Server, join logic could also have been included in the WHERE clause with = (INNER JOIN), *= (LEFT OUTER JOIN), =* (RIGHT OUTER JOIN), etc. syntax, but the support has been reduced and the best practice in SQL Server is to use the syntax outlined in the examples below.
Before we jump into code, let's provide some baseline information on the joins options in SQL Server:
Let's walk through examples from the AdventureWorks sample database that is available for SQL Server to provide examples of each type of join then provide some insight into the usage and sample result sets.
In this example we are joining between the Sales.SalesOrderDetail and Production.Product tables. The tables are aliased with the following: SOD for Sales.SalesOrderDetail and P for Production.Product. The JOIN logic is based on matching records in the SOD.ProductID and P.ProductID columns. The records are filtered by only returning records with the SOD.UnitPrice greater than 1000. Finally, the result set is returned in order with the most expensive first based on the ORDER BY clause and only the highest 100 products based on the TOP clause.
USE MSSQLTips; GO SELECT TOP 100 P.ProductID, P.Name, P.ListPrice, P.Size, P.ModifiedDate, SOD.UnitPrice, SOD.UnitPriceDiscount, SOD.OrderQty, SOD.LineTotal FROM Sales.SalesOrderDetail SOD INNER JOIN Production.Product P ON SOD.ProductID = P.ProductID WHERE SOD.UnitPrice > 1000 ORDER BY SOD.UnitPrice DESC GO
In this example we are combining two concepts to show that more than two tables can be JOINed in one SELECT statement and more than one JOIN type can be used in a single SELECT statement. In the sample code below, we are retrieving the matching data between the Person.Contact and Sales.SalesPerson tables in conjunction with all of the data from the Sales.SalesPerson table and matching data in the Sales.SalesTerritory table. For records that exist Sales.SalesPerson table and not in the Sales.SalesTerritory table, NULL values are returned for the columns in the Sales.SalesTerritory. In addition, this code uses two columns to order the data i.e. ST.TerritoryID and C.LastName.
USE MSSQLTips; GO SELECT C.ContactID, C.FirstName, C.LastName, SP.SalesPersonID, SP.CommissionPct, SP.SalesYTD, SP.SalesLastYear, SP.Bonus, ST.TerritoryID, ST.Name, ST.[Group], ST.SalesYTD FROM Person.Contact C INNER JOIN Sales.SalesPerson SP ON C.ContactID = SP.SalesPersonID LEFT OUTER JOIN Sales.SalesTerritory ST ON ST.TerritoryID = SP.TerritoryID ORDER BY ST.TerritoryID, C.LastName GO
In an effort to explain how the RIGHT OUTER JOIN and LEFT OUTER JOIN is logically a reciprocal on one another, the code below is re-written version of the LEFT OUTER JOIN above. As you can see the JOIN order and tables are different, but the final result set matches the LEFT OUTER JOIN logic. In the sample code below, we are retrieving the matching data between the Person.Contact and Sales.SalesPerson tables in conjunction with all of the data from the Sales.SalesPerson table and matching data in the Sales.SalesTerritory table. For records that exist Sales.SalesPerson table and not in the Sales.SalesTerritory table, NULL values are returned for the columns in the Sales.SalesTerritory.
USE MSSQLTips; GO SELECT C.ContactID, C.FirstName, C.LastName, SP.SalesPersonID, SP.CommissionPct, SP.SalesYTD, SP.SalesLastYear, SP.Bonus, ST.TerritoryID, ST.Name, ST.[Group], ST.SalesYTD FROM Sales.SalesTerritory ST RIGHT OUTER JOIN Sales.SalesPerson SP ON ST.TerritoryID = SP.TerritoryID INNER JOIN Person.Contact C ON C.ContactID = SP.SalesPersonID ORDER BY ST.TerritoryID, C.LastName GO
In this example, we are actually self joining to the HumanResources.Employee table. We are doing this to obtain the information about the Employee and Manager relationship in the HumanResources.Employee table. In conjunction with that JOIN logic we are also joining to the Person.Contact twice in order to capture the name and title data based on the original Employee and Manager relationships. In addition, another new concept introduced in this query is aliasing each of the columns. Although we could have done so in the previous examples, we made point of doing so in this query to differentiate between the Employee and Manager related data.
USE MSSQLTips; GO SELECT M.ManagerID AS 'ManagerID', M1.ContactID AS 'ManagerContactID', M1.FirstName AS 'ManagerFirstName', M1.LastName AS 'ManagerLastName', M.Title AS 'ManagerTitle', E.EmployeeID AS 'EmployeeID', E1.ContactID AS 'EmployeeContactID', E1.FirstName AS 'EmployeeFirstName', E1.LastName AS 'EmployeeLastName', E.Title AS 'EmployeeTitle' FROM HumanResources.Employee E INNER JOIN HumanResources.Employee M ON E.ManagerID = M.EmployeeID INNER JOIN Person.Contact E1 ON E1.ContactID = E.ContactID INNER JOIN Person.Contact M1 ON M1.ContactID = M.ContactID ORDER BY M1.LastName GO
As indicated above, please heed caution when running or modifying this query in any SQL Server database environment. The result set is intentionally limited by the TOP 100 clause and the WHERE clause to prevent a Cartesian product, which is the result of each of the rows from the left table multiplied by the number of rows in the right table.
USE MSSQLTips; GO SELECT TOP 100 P.ProductID, P.Name, P.ListPrice, P.Size, P.ModifiedDate, SOD.UnitPrice, SOD.UnitPriceDiscount, SOD.OrderQty, SOD.LineTotal FROM Sales.SalesOrderDetail SOD CROSS JOIN Production.Product P WHERE SOD.UnitPrice > 3500 ORDER BY SOD.UnitPrice DESC GO
In our last example, we have modified the logic from the LEFT OUTER JOIN example above and converted the LEFT OUTER JOIN syntax to a FULL OUTER JOIN. In this circumstance, the result set is the same as the LEFT OUTER JOIN where we are returning all of the data between both tables and data not available in the Sales.SalesTerritory is returned as NULL.
USE MSSQLTips; GO SELECT C.ContactID, C.FirstName, C.LastName, SP.SalesPersonID, SP.CommissionPct, SP.SalesYTD, SP.SalesLastYear, SP.Bonus, ST.TerritoryID, ST.Name, ST.[Group], ST.SalesYTD FROM Person.Contact C INNER JOIN Sales.SalesPerson SP ON C.ContactID = SP.SalesPersonID FULL OUTER JOIN Sales.SalesTerritory ST ON ST.TerritoryID = SP.TerritoryID ORDER BY ST.TerritoryID, C.LastName GO
| Share: | Share | Tweet |
|
![]() |
![]() |
Connect with MSSQLTips.com |
| Thursday, February 11, 2010 - 8:31:09 AM - syousuf | Read The Tip |
|
Thanks a lot.It was helpful. |
|
| Saturday, October 29, 2011 - 4:30:09 PM - Raul | Read The Tip |
|
Very good. |
|
| Sunday, October 30, 2011 - 7:29:32 AM - JustJay | Read The Tip |
|
+1 |
|
| Friday, January 06, 2012 - 6:05:11 PM - torontom | Read The Tip |
|
self join sample: GO SELECT M.ManagerID AS 'ManagerID', M1.ContactID AS 'ManagerContactID', M1.FirstName AS 'ManagerFirstName', M1.LastName AS 'ManagerLastName', M.Title AS 'ManagerTitle', E.EmployeeID AS 'EmployeeID', E1.ContactID AS 'EmployeeContactID', E1.FirstName AS 'EmployeeFirstName', E1.LastName AS 'EmployeeLastName', E.Title AS 'EmployeeTitle' FROM HumanResources.Employee E INNER JOIN HumanResources.Employee M ON E.ManagerID = M.ManagerID INNER JOIN Person.Contact E1 ON E1.ContactID = E.ContactID INNER JOIN Person.Contact M1 ON M1.ContactID = M.ContactID ORDER BY M1.LastName should be: E.ManagerID =M.EmployeeId, isn't it? |
|
| Monday, January 16, 2012 - 8:56:25 AM - Jeremy Kadlec | Read The Tip |
|
torontom, Thank you for the feedback. From the initial publication of this tip to today, it looks like Microsoft has changed the AdventureWorks database and this query no longer parses. In the short term, I will update the tip so that the code makes sense and plan to re-write this tip to include a data model to make sure the examples are clear. Thank you, |
|
| Saturday, April 28, 2012 - 4:52:51 PM - manal | Read The Tip |
|
Hello My name is Manal I'm So interesting in SQL server but I found problem in Grouping for example I have three Tables(Customer,Payments,Invoice) Customer related with Payment and Customer related with Invoice but no relation between Payment and Invoice and I want To make groupink to give me (customername,sum(payment.Amount),sum(Invoice.Value) for every customer but actauly I try alot but there always some error in result not in statments |
|
| Saturday, April 28, 2012 - 4:55:02 PM - manal | Read The Tip |
|
Thanke you very much |
|
| Monday, April 30, 2012 - 8:56:20 AM - Dom | Read The Tip |
|
I think you should have pointed out that the ON clause can do more than just present the matching columns. For example, you can use: FROM Sales.SalesOrderDetail SOD Also, it is crucial to keep in mind the logical processing sequence. All joins, without exceptions, are logically cross joins, then the ON clause is executed to filter out the records that do not return true. One more point. The tables in the from clause are processed from left to right. |
|
| Monday, April 30, 2012 - 9:00:08 AM - Dom | Read The Tip |
|
Manal: Does this help? Select c.Name, sum (p.Amount) as Total_Amt, sum (v.Value) as Total_Value from Customer c join Payment p on c.??? = p.???? join Invoice v on c.??? = v.??? group by c.Name |
|
| Monday, April 30, 2012 - 2:54:45 PM - manal | Read The Tip |
|
fisrt of all thank you very much but it does't work the statement is correct but the way of grouping is wrrong
|
|
| Tuesday, May 01, 2012 - 6:14:59 AM - manal | Read The Tip |
|
Hello about my last question I make tow viows and I make join between them after that but now I want to ask you about how can I make trigger on insert statment and work in this way ( if the rowId -in inserte statment -is founded make update in the samw row and if not insert new one) |
|
|
privacy | disclaimer | copyright | advertise | about authors | contribute | feedback | giveaways | user groups Some names and products listed are the registered trademarks of their respective owners. Edgewood Solutions LLC | MSSharePointTips.com | MSSQLTips.com |