sql server hub / joins guide
SQL Server joins guide
SQL Server joins are how queries combine rows from related tables. The join type decides which rows stay, which rows disappear, and where duplicates can appear.
Use the examples to check join types, ON clauses, missing matches, row multiplication, and APPLY queries without guessing from syntax alone.
- 01What a SQL Server JOIN does
- 02Sample tables used in the examples
- 03SQL Server INNER JOIN example
- 04SQL Server LEFT JOIN example
- 05SQL Server RIGHT JOIN
- 06SQL Server FULL OUTER JOIN example
- 07SQL Server CROSS JOIN example
- 08SQL Server CROSS APPLY and OUTER APPLY
- 09SQL Server JOIN ON clauses
- 10SQL Server join duplicates and row multiplication
- 11JOINs, UNION, MERGE, PIVOT, and APPLY
- 12Performance notes for SQL Server joins
- 13Quick reference table
- 14Need a SQL Server performance review?
What a SQL Server JOIN does
A SQL Server JOIN combines rows from two table sources. The join predicate says how the rows relate. In most day-to-day queries that means matching a foreign key to a primary key, such as Orders.CustomerID to Customers.CustomerID.
The logical join type decides which rows can appear in the result: matching rows only, all rows from one side, all rows from both sides, or every possible combination. SQL Server then chooses a physical join algorithm to run the query. Microsoft documents nested loops, merge, hash, and adaptive joins, but those are execution choices. They do not change the logical meaning of your query.
The main types of SQL join in SQL Server are INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN, and CROSS JOIN. MSSQL is common shorthand for Microsoft SQL Server, so a join MSSQL example usually means the same T-SQL joins, sometimes typed as T SQL joins, shown here.
JOIN without another keyword means INNER JOIN. Write INNER JOIN when clarity matters. Future readers should not need to remember default syntax while debugging a production report.
Sample tables used in the examples
The examples below use temporary tables so the queries are runnable in SQL Server Management Studio or Azure Data Studio. The sample data intentionally includes customers with no orders, an order with a missing customer, and orders with multiple lines.
That gives the joins something useful to show. Perfect demo data makes joins look cleaner than real reporting data. Real data usually contains missing rows, optional relationships, one-to-many joins, and old records that do not fit the neat diagram.
Create sample tables
Creates temporary tables for customers, orders, order lines, products, and calendar dates.
Run this setup once before running the examples. Order 1004 has CustomerID 99, which does not exist in #Customers. Customer 3 and Customer 4 have no matching orders. Order 1001 has two order lines, which is useful for duplicate-row examples.
SQL Server INNER JOIN example
An INNER JOIN returns rows where the join predicate matches on both sides. If a customer has no orders, that customer is not returned. If an order points to a missing customer, that order is not returned either.
This is the common SQL Server join for normal lookups: orders with customers, order lines with products, users with departments, and other relationships where unmatched rows are not useful for the result. If someone searches for MSSQL inner join, this is the SQL Server INNER JOIN pattern they usually need.
SQL Server INNER JOIN example
Returns only customers that have matching orders.
Acme Ltd appears twice because it has two orders. No Orders Co and Quiet Shop do not appear because they have no matching orders. Order 1004 does not appear because its CustomerID is missing from #Customers.
Multiple inner joins keep narrowing the result to rows that match across all joined tables. That is often correct, but it can also hide missing data. If a report suddenly loses rows after another join is added, check which relationship is no longer optional.
Multiple INNER JOINs
Joins customers, orders, order lines, and products.
The result is now at order-line grain, not order grain. One order can appear more than once when it has several lines. That multiplication is normal for one-to-many joins.
SQL Server LEFT JOIN example
A LEFT JOIN returns all rows from the left table and matching rows from the right table. When no right-side row exists, SQL Server returns NULL for the right-side columns.
Use a left join in SQL Server when the left-side entity must stay in the result: all customers, all products, all databases, all jobs, all expected dates. The right side is optional.
SQL Server LEFT JOIN example
Returns every customer, including customers without orders.
Customers without orders stay in the result. Their order columns are NULL. This is the usual pattern for finding missing related data.
The common mistake is filtering the right-side table in the WHERE clause. That can remove the NULL rows and turn the practical result into an inner join.
LEFT JOIN filter placement
Shows the difference between filtering in the ON clause and filtering in the WHERE clause.
Put optional-side filters in the ON clause when unmatched left rows must remain. Put right-side filters in WHERE only when removing unmatched rows is intended. This is one of the fastest ways to break a report without changing the join keyword.
SQL Server RIGHT JOIN
A RIGHT JOIN preserves rows from the right table and returns matching rows from the left table. It is valid SQL Server outer join syntax, but in normal work it is usually clearer to swap the table order and write a LEFT JOIN.
People read joins from left to right. If the preserved table matters, put it first. That keeps the query easier to review when the FROM clause grows.
RIGHT JOIN example
Preserves customers from the right side. A LEFT JOIN with swapped table order is usually easier to read.
SQL Server FULL OUTER JOIN example
A FULL OUTER JOIN returns matching rows plus unmatched rows from both sides. It is useful for reconciliation, audit queries, and finding rows that exist in one source but not the other.
People often search for full join in SQL Server or full join MSSQL. In T-SQL, use FULL OUTER JOIN. It is less common for normal lookup queries. If the result has many nullable columns from both sides, check whether you are comparing sets or trying to build a daily report with the wrong join.
SQL Server FULL OUTER JOIN example
Finds customers without orders and orders without customers.
Rows with a NULL customer side are orders that do not match a customer. Rows with a NULL order side are customers without orders. This is useful for reconciliation because both missing directions are visible.
SQL Server CROSS JOIN example
A CROSS JOIN returns every combination of rows from both sides. If one side has 2 rows and the other side has 3 rows, the result has 6 rows. If one side has 10,000 rows and the other side has 10,000 rows, the result has 100,000,000 rows. SQL Server will return the requested combinations, even when the row count is much larger than intended.
A planned cross join can be useful for building date/product combinations, test matrices, or expected reporting rows before left joining actual data. People often search this as cross join MSSQL; in SQL Server, the important part is the same row-count multiplication. An accidental cross join usually means a missing join predicate.
SQL Server CROSS JOIN example
Builds every active product and calendar-date combination.
The active product rows multiply by the calendar date rows. Check row counts before running a cross join on large tables. If a normal join suddenly returns far too many rows, look for a missing ON predicate.
SQL Server CROSS APPLY and OUTER APPLY
APPLY is a T-SQL table operator used in the FROM clause. It lets the right-side table expression reference columns from the left side. That makes it useful for top-N-per-row queries, table-valued functions, and correlated row sets.
CROSS APPLY behaves like an inner join against the applied row set: if the right side returns nothing, the left row disappears. OUTER APPLY behaves like a left join against the applied row set: the left row stays and right-side columns become NULL. Searches for cross apply SQL Server, outer apply SQL Server, or outer apply SQL usually refer to this T-SQL pattern.
CROSS APPLY and OUTER APPLY examples
Returns the latest order per customer, first with matching customers only and then with all customers preserved.
CROSS APPLY returns only customers with at least one order. OUTER APPLY preserves customers without orders. This pattern is often clearer than joining to a grouped subquery when you need one correlated row per outer row.
SQL Server JOIN ON clauses
The ON clause is where the join relationship belongs. Searches for SQL JOIN ON usually mean this part of the query: the predicate that tells SQL Server how two table sources match. Keep join predicates there so the relationship is visible before other filters. Microsoft also recommends specifying join conditions in the FROM clause because it separates join logic from search conditions.
Good join predicates use the actual key relationship. Weak predicates join on names, descriptions, dates, or partial business labels because those columns happen to look similar. That works until spelling changes, duplicates appear, or two regions use the same label.
JOIN ON with additional predicate
Keeps the key relationship and date predicate visible in the join.
Use table aliases so each column source is clear. Join key data types should match. Implicit conversions can hurt plans and estimates. Composite keys need all key columns in the join predicate.
SQL Server join duplicates and row multiplication
Duplicate rows after a join usually mean the query changed grain. Joining orders to order lines moves the result from one row per order to one row per order line. That is not SQL Server being weird. That is the relationship doing exactly what it was asked to do.
DISTINCT can hide duplicate output, but it does not fix a bad join. First decide the intended grain: one row per customer, one row per order, one row per product per date, or one row per order line. Then join and aggregate to that grain.
Check row multiplication
Finds orders that return more than one joined row after joining to order lines.
This is expected for order 1001 because it has two order lines. If the multiplication is not expected, inspect the keys on both sides. Fix the join or aggregate before using DISTINCT.
JOINs, UNION, MERGE, PIVOT, and APPLY
A JOIN combines columns side by side from related table sources. UNION stacks compatible result sets. MERGE changes target data based on another source. PIVOT reshapes row values into columns. APPLY evaluates a right-side table expression for each left-side row.
Those features sometimes appear in the same search because people are trying to merge tables or reshape report data. If you search for SQL merge tables, check whether you really need a join, a UNION, or the MERGE statement. For MS SQL pivot questions, keep the intent separate: PIVOT reshapes data, while joins retrieve related columns.
| Feature | Use when |
|---|---|
| JOIN | You need related columns from two or more table sources. |
| UNION | You need to stack compatible result sets into one list. |
| MERGE | You need to insert, update, or delete target rows from a source. |
| PIVOT | You need row values turned into output columns. |
| APPLY | You need a right-side table expression that can reference the left row. |
Performance notes for SQL Server joins
Join performance starts with correct row counts and useful access paths. Indexes on join keys matter. Matching data types matter. Accurate statistics matter. The actual execution plan will show whether SQL Server used nested loops, merge, hash, or adaptive join behavior.
Do not tune a join only from the keyword. A slow INNER JOIN might be a missing index, bad estimate, implicit conversion, stale statistics, parameter-sensitive plan, oversized result, blocking chain, or memory grant issue.
Check the actual execution plan, not only estimated cost.
Look for implicit conversions on join keys.
Confirm row estimates near the join are close enough to reality.
Index the join keys when the workload and write cost justify it.
Filter early when it reduces rows without changing the intended result.
Use the waits and blocking guides when a join query is slow under load rather than slow in isolation.
Related reads: SQL Server indexing, SQL Server waits, and SQL Server blocking.
Quick reference table
| Type | Rows returned | Use it for |
|---|---|---|
| INNER JOIN | Matching rows only. | Normal lookups where unmatched rows do not belong in the result. |
| LEFT JOIN | All left rows, with matching right rows where available. | Optional relationships and missing-data checks. |
| RIGHT JOIN | All right rows, with matching left rows where available. | Valid syntax, but usually clearer as a LEFT JOIN with table order swapped. |
| FULL OUTER JOIN | All rows from both sides, matched where possible. | Reconciliation and finding rows missing from either side. |
| CROSS JOIN | Every combination of rows from both sides. | Calendars, matrices, and deliberate row expansion. |
| CROSS APPLY | Left rows with at least one correlated right-side result. | Top-N-per-row and table-valued expressions where missing rows should disappear. |
| OUTER APPLY | All left rows, with correlated right-side rows where available. | Top-N-per-row and table-valued expressions where left rows should remain. |
Need a SQL Server performance review?
Request a performance review when a join query returns the wrong rows, runs slowly under load, or keeps changing plans after small query or index changes.
Start with the symptom, urgency, and what changed recently. I can ask for query text, plans, or index details later if they are needed.
Next step
If the query returns the wrong rows or slows down under real load, start with the SQL Server performance review.
Next useful reads: SQL Server indexing for access paths, SQL Server waits for runtime diagnosis, and SQL Server blocking for lock pressure.
