Talentd
FleetCode
Make and follow sheets.Sign in

Combine Two Tables solution and explanation

EasyDatabase7 min read80.1% acceptanceUpdated 9 Apr 2026
LeetCode 175: Combine Two Tables [SQL]Frederik Müller · 8:35 · 49K viewsWatch on YouTube
9 more videos on this problem
  1. LeetCode 175: Combine Two Tables [SQL]Frederik Müller · 8:35 · 49K views
  2. Leetcode Problem 175. Combine Two Tables | Leetcode SQL Interview Problem with SolutionThe Engineer Guy 2.0 · 7:33 · 4K views
  3. Leetcode 175 Combine Two Tables (Python and SQL) [EASY]Ryan & Matt Data Science · 6:25 · 3K views
  4. [MySQL] Leetcode 175. Combine Two Tables [SQL #5]Eric Programming · 4:39 · 2K views
  5. Leetcode 175 | Combine Two Tables | JOIN Interview QuestionBig Tech Mindset · 4:03 · 1K views
  6. 175. Combine Two Tables | LeetCode | SQL | Solution | Interview ProblemData Alchemist · 3:58 · 839 views
  7. SQL LeftJoinVideo - Solution to LeetCode 175 - Combine Two TablesLEETCODE MASTER · 3:09 · 559 views
  8. LeetCode in MySQL 175 Combine Two Tables - Michelle小梦想家Michelle小梦想家 · 12:06 · 391 views
  9. How to Solve "175 Combine Two Tables" on LeetCode? - MySQLBecoming A Better Dev (with David) · 4:28 · 216 views
  10. leetcode 175 Combine Two TablesGourav Midya · 7:45 · 172 views

The problem

Table: Person

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| personId    | int     |
| lastName    | varchar |
| firstName   | varchar |
+-------------+---------+
personId is the primary key (column with unique values) for this table.
This table contains information about the ID of some persons and their first and last names.

Table: Address

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| addressId   | int     |
| personId    | int     |
| city        | varchar |
| state       | varchar |
+-------------+---------+
addressId is the primary key (column with unique values) for this table.
Each row of this table contains information about the city and state of one person with ID = PersonId.

Write a solution to report the first name, last name, city, and state of each person in the Person table. If the address of a personId is not present in the Address table, report null instead.

Return the result table in any order.

The result format is in the following example.

Example 1:

Input: 
Person table:
+----------+----------+-----------+
| personId | lastName | firstName |
+----------+----------+-----------+
| 1        | Wang     | Allen     |
| 2        | Alice    | Bob       |
+----------+----------+-----------+
Address table:
+-----------+----------+---------------+------------+
| addressId | personId | city          | state      |
+-----------+----------+---------------+------------+
| 1         | 2        | New York City | New York   |
| 2         | 3        | Leetcode      | California |
+-----------+----------+---------------+------------+
Output: 
+-----------+----------+---------------+----------+
| firstName | lastName | city          | state    |
+-----------+----------+---------------+----------+
| Allen     | Wang     | Null          | Null     |
| Bob       | Alice    | New York City | New York |
+-----------+----------+---------------+----------+
Explanation: 
There is no address in the address table for the personId = 1 so we return null in their city and state.
addressId = 1 contains information about the address of personId = 2.

Approach overview

Problem Overview: The task joins information from two tables: Person and Address. Each person may or may not have an address entry. You must return every person's first name and last name along with their city and state if the address exists. Missing address rows should still keep the person in the result.

Approach 1: Using SQL LEFT JOIN (Time: O(n), Space: O(1))

The most direct solution uses a LEFT JOIN between the Person and Address tables on PersonId. A left join guarantees that every row from Person appears in the result even if no matching address exists. When no match is found, SQL fills City and State with NULL. Database engines typically optimize joins using indexes or hash join strategies, so the operation runs in roughly linear time relative to the number of rows. This is the cleanest and most idiomatic database solution and heavily relies on concepts from database querying.

Approach 2: Using Subqueries (Time: O(n), Space: O(1))

A correlated or nested subquery can retrieve the city and state for each person. For every row in Person, a subquery searches the Address table using the same PersonId. Modern SQL optimizers often rewrite this internally into a join, so practical performance remains close to O(n). This approach works when you want to fetch related values without explicitly writing join syntax, but it is usually less readable than a direct join.

Approach 3: Using a Sorting Technique (Time: O(n log n), Space: O(1) or O(n))

If the data were provided as in-memory arrays rather than database tables, one approach is to sort both datasets by PersonId. After sorting, iterate through both lists using two pointers, similar to a merge step in merge sort. When the IDs match, attach the address information to the person record. If a person has no matching address, output NULL values. Sorting enables deterministic matching but increases the runtime to O(n log n). This pattern appears frequently in problems involving ordered data and relates to sorting strategies.

Approach 4: Using a HashMap for Quick Lookup (Time: O(n), Space: O(n))

A more efficient in-memory solution builds a hash map keyed by PersonId from the Address dataset. Then iterate through the Person list and perform a constant-time lookup to find the corresponding address. If the key is missing, output NULL fields. Hash maps eliminate sorting and reduce the runtime to O(n) with additional memory overhead. This technique mirrors classic lookup optimization problems and uses concepts from hash maps.

Recommended for interviews: The expected database answer is the LEFT JOIN solution. Interviewers want to see that you understand relational joins and how to preserve rows from the primary table when matches are missing. Mentioning the subquery alternative shows SQL familiarity, while discussing hash-map or sorting approaches demonstrates how you would solve the same relationship problem outside a database environment.

Approach 1: Using SQL LEFT JOIN

To solve this problem, we can utilize SQL's LEFT JOIN operation. The LEFT JOIN operation will allow us to join the Person table with the Address table on the personId. It returns all records from the left table (Person), and the matched records from the right table (Address). If there is no match, NULL values are returned for columns from the right table. This operation is suitable because we need to retrieve each person's details regardless of whether they have an address recorded. Thus, using LEFT JOIN will fulfill the requirement of including persons with NULL city and state when there is no matching entry in the Address table.

SELECT p.firstName, p.lastName, A.city, A.state FROM Person p LEFT JOIN Address A ON p.personId = A.personId;

This SQL query selects the first and last names from the Person table and combines them with the city and state fields from the Address table. We use a LEFT JOIN to ensure that even if a person's address doesn't exist, they still appear in the results, with NULL values for the city and state fields.

Complexity

Time Complexity: O(n + m), where n is the number of rows in the Person table and m is the number of rows in the Address table.
Space Complexity: O(n), where n is the number of rows in the result set.

Try this approach in the editor

Approach 2: Using Subqueries

This approach involves using subqueries to fetch data from the Address table if it exists. This is done by selecting fields from the Person table and then using subqueries to attempt to find the corresponding city and state for each personId from the Address table. If there's no match, the subquery will result in NULL, which aligns with the problem's requirement of reporting NULL when no address is available.


SELECT
    p.firstName, 
    p.lastName, 
    (SELECT city FROM Address WHERE personId = p.personId) AS city,
    (SELECT state FROM Address WHERE personId = p.personId) AS state
FROM Person p;

This solution fetches the first and last names directly from the Person table and utilizes subqueries to search for the corresponding city and state. Each subquery runs a lookup in the Address table for the personId. When no corresponding address is found, NULL is returned for both city and state.

Complexity

Time Complexity: O(n * m), where n is the number of rows in the Person table and m is the number of rows in the Address table because each subquery runs a separate lookup operation for each person.
Space Complexity: O(n), where n is the number of rows in the result set.

Try this approach in the editor

Approach 3: Approach 1: Using a Sorting Technique

This approach involves sorting the input data, which allows us to leverage properties of sorted sequences to efficiently solve the problem.

def solve(arr):
    arr.sort()
    # Logic based on sorted array
    print("Sorted Array:", ' '.join(map(str, arr)))

arr = [3, 1, 4, 1, 5]
solve(arr)
import java.util.Arrays;

public class Solution {
    public static void solve(int[] arr) {
        Arrays.sort(arr);
        // Apply logic on sorted array
        System.out.print("Sorted Array: ");
        for (int num : arr) System.out.print(num + " ");
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5};
        solve(arr);
    }
}
#include <iostream>
#include <algorithm>
#include <vector>

void solve(std::vector<int>& vec) {
    std::sort(vec.begin(), vec.end());
    // Perform operations on sorted vector
    std::cout << "Sorted Vector: ";
    for (int num : vec) std::cout << num << " ";
    std::cout << std::endl;
}

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5};
    solve(vec);
    return 0;
}
function solve(arr) {
    arr.sort((a, b) => a - b);
    // Perform operations on sorted array
    console.log("Sorted Array:", arr.join(' '));
}

let arr = [3, 1, 4, 1, 5];
solve(arr);
#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

void solve(int arr[], int n) {
    qsort(arr, n, sizeof(int), compare);
    // Assume this finds the result based on sorted array
    printf("Sorted Array: ");
    for(int i = 0; i < n; i++) printf("%d ", arr[i]);
}

int main() {
    int arr[] = {3, 1, 4, 1, 5};
    int n = sizeof(arr) / sizeof(arr[0]);
    solve(arr, n);
    return 0;
}
using System;

class Solution {
    public static void Solve(int[] arr) {
        Array.Sort(arr);
        // Use sorted array for further computation
        Console.WriteLine("Sorted Array: {0}", string.Join(" ", arr));
    }

    static void Main() {
        int[] arr = {3, 1, 4, 1, 5};
        Solve(arr);
    }
}

Python lists have a built-in sort method which alters the list in place. The sorted list can then be easily manipulated as per problem's requirement.

Complexity

Time Complexity: O(n log n), Space Complexity: O(1) using in-place sorting

Try this approach in the editor

Approach 4: Approach 2: Using a HashMap for Quick Lookup

This approach uses a hash map (or dict) to store elements for quick access. This is particularly useful if you need to quickly check for the existence of an element or store counts.

def solve(arr):
    hashmap = {}
    for num in arr:
        if num in hashmap:
            hashmap[num] += 1
        else:
            hashmap[num] = 1
    # Check presence as example
    if 4 in hashmap:
        print("4 found!")
    else:
        print("4 not found!")

arr = [3, 1, 4, 1, 5]
solve(arr)
import java.util.HashMap;

public class Solution {
    public static void solve(int[] arr) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int num : arr) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        // Demonstrate finding an element
        if (map.containsKey(4))
            System.out.println("4 found!");
        else
            System.out.println("4 not found!");
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5};
        solve(arr);
    }
}
#include <iostream>
#include <unordered_map>
#include <vector>

void solve(const std::vector<int>& vec) {
    std::unordered_map<int, int> map;
    for (int num : vec) {
        map[num]++;
    }
    // Example to check presence
    if (map.find(4) != map.end())
        std::cout << "4 found!" << std::endl;
    else
        std::cout << "4 not found!" << std::endl;
}

int main() {
    std::vector<int> vec = {3, 1, 4, 1, 5};
    solve(vec);
    return 0;
}
function solve(arr) {
    let map = new Map();
    for (let num of arr) {
        map.set(num, (map.get(num) || 0) + 1);
    }
    // Check if a number is present
    if (map.has(4)) {
        console.log("4 found!");
    } else {
        console.log("4 not found!");
    }
}

let arr = [3, 1, 4, 1, 5];
solve(arr);
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define TABLE_SIZE 100

typedef struct Item {
    int key;
    int value;
} Item;

typedef struct HashTable {
    Item* table[TABLE_SIZE];
} HashTable;

unsigned hash(int key) { return key % TABLE_SIZE; }

HashTable* createTable() {
    HashTable* ht = malloc(sizeof(HashTable));
    for(int i = 0; i < TABLE_SIZE; i++)
        ht->table[i] = NULL;
    return ht;
}

void insert(HashTable* ht, int key, int value) {
    unsigned idx = hash(key);
    while (ht->table[idx] != NULL && ht->table[idx]->key != key) {
        idx = (idx + 1) % TABLE_SIZE;
    }
    if (ht->table[idx] == NULL)
        ht->table[idx] = malloc(sizeof(Item));
    ht->table[idx]->key = key;
    ht->table[idx]->value = value;
}

bool search(HashTable* ht, int key) {
    unsigned idx = hash(key);
    while (ht->table[idx] != NULL && ht->table[idx]->key != key) {
        idx = (idx + 1) % TABLE_SIZE;
    }
    return ht->table[idx] != NULL && ht->table[idx]->key == key;
}

void solve(int arr[], int n) {
    HashTable* ht = createTable();
    for (int i = 0; i < n; i++) {
        insert(ht, arr[i], 1);
    }
    // Example purpose, search for a value
    printf(search(ht, 4) ? "4 found!\n" : "4 not found!\n");
}

int main() {
    int arr[] = {3, 1, 4, 1, 5};
    int n = sizeof(arr) / sizeof(arr[0]);
    solve(arr, n);
    return 0;
}
using System;
using System.Collections.Generic;

class Solution {
    public static void Solve(int[] arr) {
        Dictionary<int, int> map = new Dictionary<int, int>();
        foreach (int num in arr) {
            if (map.ContainsKey(num))
                map[num]++;
            else
                map[num] = 1;
        }
        if (map.ContainsKey(4))
            Console.WriteLine("4 found!");
        else
            Console.WriteLine("4 not found!");
    }

    static void Main() {
        int[] arr = {3, 1, 4, 1, 5};
        Solve(arr);
    }
}

Python dictionaries are hash maps, perfect for O(1) average time operations for insertion and lookup, making it ideal for tracking element presence and counts.

Complexity

Time Complexity: O(1) average for insert/search, Space Complexity: O(n).

Try this approach in the editor

Complexity comparison

ApproachComplexity
Using SQL LEFT JOIN

Time Complexity: O(n + m), where n is the number of rows in the Person table and m is the number of rows in the Address table.
Space Complexity: O(n), where n is the number of rows in the result set.

Using Subqueries

Time Complexity: O(n * m), where n is the number of rows in the Person table and m is the number of rows in the Address table because each subquery runs a separate lookup operation for each person.
Space Complexity: O(n), where n is the number of rows in the result set.

Approach 1: Using a Sorting Technique

Time Complexity: O(n log n), Space Complexity: O(1) using in-place sorting

Approach 2: Using a HashMap for Quick Lookup

Time Complexity: O(1) average for insert/search, Space Complexity: O(n).

Complexity analysis

ApproachTimeSpaceWhen to Use
SQL LEFT JOINO(n)O(1)Standard relational database queries where all rows from the main table must appear
SQL SubqueriesO(n)O(1)When retrieving related values without explicitly writing join syntax
Sorting + Two PointersO(n log n)O(1)–O(n)When datasets are arrays and can be sorted before matching records
HashMap LookupO(n)O(n)Best for in-memory datasets needing fast key-based lookups

Frequently asked questions

Is Combine Two Tables easy or hard?

Combine Two Tables is categorized as an Easy problem on LeetCode with a high acceptance rate around 77%. It mainly tests understanding of SQL LEFT JOIN and how to keep unmatched rows from the primary table.

Combine Two Tables Python/Java solution

If the data were stored in arrays instead of SQL tables, a common solution uses a HashMap keyed by PersonId. Build the map from the Address list, then iterate through the Person list and perform O(1) lookups to attach city and state values.

How to solve Combine Two Tables in O(n)?

Use a LEFT JOIN on PersonId to directly match rows from the Person table with Address rows. Each row is scanned once while the database performs efficient matching using indexes or internal hash join structures, giving near O(n) performance.

What is the best approach for Combine Two Tables?

The best approach is using an SQL LEFT JOIN between the Person and Address tables on PersonId. LEFT JOIN ensures every person appears in the result even if they do not have an address record. The query runs in roughly O(n) time depending on indexing and database execution strategy.

Is Combine Two Tables asked at Google/Amazon/Meta?

SQL join questions like Combine Two Tables frequently appear in database or data engineering interviews at companies such as Amazon, Google, and Meta. Candidates are expected to understand INNER JOIN vs LEFT JOIN behavior and when each is required.

What data structure is used in Combine Two Tables?

In SQL, the problem relies on relational join operations rather than explicit data structures. Conceptually, database engines may use hash tables, merge joins, or indexed lookups internally to match PersonId values between tables.

What is the time complexity of Combine Two Tables?

The typical SQL LEFT JOIN solution runs in O(n) time relative to the number of rows processed by the query engine. Database optimizers often use indexed lookups or hash joins to keep the operation near linear complexity.

Comments

Now write it yourselfClose this page and solve Combine Two Tables in the editor, without looking.
Practise Combine Two Tables