INNER JOIN: Preserve matches

We have customers on one table and orders on another. The two contain different information about the same operation: one identifies the person and the other records what happened with their order.

If we want to show only the customers who do have orders, we need to relate both tables and discard the rows that do not find a match. That’s the job of INNER JOIN.

The idea

INNER JOIN compares a column in the first table with a column in the second. The row appears in the result only when the condition indicated after ON is met.

The relationship does not have to use columns with the same name, although it is usually built with a primary key and a foreign key. The primary key uniquely identifies a row; the foreign key stores the value that points to that identifier from another table.

Syntax

SELECT
    tabla_a.Columna,
    tabla_b.OtraColumna
FROM TablaA AS tabla_a
INNER JOIN TablaB AS tabla_b
    ON tabla_b.IdRelacionado = tabla_a.Id;

The aliases tabla_a and tabla_b allow you to clearly indicate where each column comes from. In small queries they could be omitted, but they become especially useful when the tables share names such as Nombre, Estado, or FechaRegistro.

Example

In the example database, Cliente contains these rows:

ClientId Name
1 Ana Torres
2 Bruno Diaz
3 Carla Ruiz
4 Diego Soto
5 Elena Mora

Pedido contains:

OrderId ClientId OrderDate State
1 1 2026-03-01 10:15 Confirmed
2 1 2026-03-04 17:20 Sent
3 2 2026-03-02 12:35 Pending
4 4 2026-02-15 09:05 Canceled
5 5 2026-03-05 08:50 Confirmed

The intersection contains identifiers 1, 2, 4, and 5. The result preserves each match; That’s why Ana appears twice, once for each order.

Intersection of customers and orders in the INNER JOIN example

We can join them using IdCliente:

SELECT
    c.IdCliente,
    c.Nombre,
    p.IdPedido,
    p.FechaPedido,
    p.Estado
FROM dbo.Cliente AS c
INNER JOIN dbo.Pedido AS p
    ON p.IdCliente = c.IdCliente
ORDER BY
    c.IdCliente,
    p.FechaPedido;

How to read the result

ClientId Name OrderId OrderDate State
1 Ana Torres 1 2026-03-01 10:15 Confirmed
1 Ana Torres 2 2026-03-04 17:20 Sent
2 Bruno Diaz 3 2026-03-02 12:35 Pending
4 Diego Soto 4 2026-02-15 09:05 Canceled
5 Elena Mora 5 2026-03-05 08:50 Confirmed

Carla does not appear because there is no order whose IdCliente is 3. That doesn’t mean Carla’s row has disappeared from Cliente; It was only left out of the result produced by this query.

Ana appears twice because she has two orders. INNER JOIN does not automatically produce one row per client: it produces one row for each join that meets the join condition.

What should be checked

An unexpected multiplication of rows usually indicates that the relationship was not one-to-one, meaning that one row could find more than one match in the other table. Before adding DISTINCT to hide it, check whether:

  • a row of the first table has several related records;
  • a condition was missing within ON;
  • the columns used actually identify the relationship;
  • the result needed to be grouped instead of keeping each match.

If we also need to show customers without orders, INNER JOIN no longer represents the full question. In that case we will use LEFT JOIN.