MySQL
SQL Join
Database
Join Tables
SQL Tutorial

MySQL how to join tables on two fields

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Joining on two fields in MySQL means that rows must match on more than one column before they are considered related. This is common when the real relationship is a composite key, such as order_id plus line_number, or user_id plus account_type.

Basic Join Syntax

The pattern is simple: add both match conditions in the ON clause and connect them with AND.

sql
1SELECT o.order_id, o.customer_id, p.status
2FROM orders AS o
3JOIN payments AS p
4  ON o.order_id = p.order_id
5 AND o.customer_id = p.customer_id;

This query returns rows only when both columns match. If only one column matches, the row pair is excluded.

Example With Composite Identity

Suppose you have order items and shipment items. The pair order_id and line_no identifies each item uniquely.

sql
1CREATE TABLE order_items (
2    order_id INT NOT NULL,
3    line_no INT NOT NULL,
4    sku VARCHAR(20) NOT NULL,
5    qty INT NOT NULL,
6    PRIMARY KEY (order_id, line_no)
7);
8
9CREATE TABLE shipment_items (
10    order_id INT NOT NULL,
11    line_no INT NOT NULL,
12    shipped_qty INT NOT NULL,
13    PRIMARY KEY (order_id, line_no)
14);
15
16SELECT oi.order_id, oi.line_no, oi.sku, si.shipped_qty
17FROM order_items AS oi
18JOIN shipment_items AS si
19  ON oi.order_id = si.order_id
20 AND oi.line_no = si.line_no;

This is the correct form because matching only on order_id would mix different lines from the same order and produce incorrect duplicates.

Use LEFT JOIN When Missing Matches Matter

If you want all rows from the left table even when no match exists in the right table, use LEFT JOIN with the same two-column condition.

sql
1SELECT oi.order_id, oi.line_no, oi.sku, si.shipped_qty
2FROM order_items AS oi
3LEFT JOIN shipment_items AS si
4  ON oi.order_id = si.order_id
5 AND oi.line_no = si.line_no;

Rows with no shipment yet will still appear, and shipped_qty will be NULL.

Why Conditions Belong In ON

For inner joins, putting the conditions in ON or WHERE can produce the same result, but ON is clearer because it defines the relationship between tables. For outer joins, it matters a lot.

Compare these two patterns:

sql
1-- Preferred
2SELECT *
3FROM a
4LEFT JOIN b
5  ON a.id = b.id
6 AND a.type = b.type;
sql
1-- Often wrong for outer joins
2SELECT *
3FROM a
4LEFT JOIN b
5  ON a.id = b.id
6WHERE a.type = b.type;

The second query can accidentally turn the outer join into inner-join behavior by filtering out the NULL rows after the join.

Indexing Matters

Two-column joins are often correct logically but slow operationally if the database cannot use suitable indexes. If the join key is (order_id, line_no), create composite indexes in that order.

sql
CREATE INDEX idx_shipment_items_order_line
    ON shipment_items (order_id, line_no);

Column order matters. An index on (line_no, order_id) is not always equivalent for the optimizer. Match the index order to the most common join and filter pattern.

Watch Out For Data Type Mismatches

The join columns should have compatible types and collations. Joining an INT to a VARCHAR, or mixing differently collated text columns, can force conversions and degrade performance or produce unexpected matches.

A quick schema check helps:

sql
SHOW CREATE TABLE order_items;
SHOW CREATE TABLE shipment_items;

If the columns represent the same business key, define them with the same type, length, sign, and collation.

Common Pitfalls

The most common mistake is joining on only part of a composite key. That usually produces too many rows and looks like duplication, even though the join is behaving exactly as written.

Another mistake is moving one of the join conditions into WHERE during a LEFT JOIN. That changes the semantics and often hides unmatched rows that you wanted to keep.

A third issue is missing indexes. Even a correct two-field join can become slow on large tables if MySQL has to scan and compare far more rows than necessary.

Summary

  • Join on two fields by putting both equality checks in the ON clause.
  • Use the full composite key, not only part of it.
  • Prefer LEFT JOIN when unmatched left rows should remain visible.
  • Keep outer-join conditions in ON, not WHERE.
  • Add composite indexes that match the join column order.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.