Combine Two Tables solution and explanation
9 more videos on this problem
LeetCode 175: Combine Two Tables [SQL]Frederik Müller · 8:35 · 49K views
Leetcode Problem 175. Combine Two Tables | Leetcode SQL Interview Problem with SolutionThe Engineer Guy 2.0 · 7:33 · 4K views
Leetcode 175 Combine Two Tables (Python and SQL) [EASY]Ryan & Matt Data Science · 6:25 · 3K views
[MySQL] Leetcode 175. Combine Two Tables [SQL #5]Eric Programming · 4:39 · 2K views
Leetcode 175 | Combine Two Tables | JOIN Interview QuestionBig Tech Mindset · 4:03 · 1K views
175. Combine Two Tables | LeetCode | SQL | Solution | Interview ProblemData Alchemist · 3:58 · 839 views
SQL LeftJoinVideo - Solution to LeetCode 175 - Combine Two TablesLEETCODE MASTER · 3:09 · 559 views
LeetCode in MySQL 175 Combine Two Tables - Michelle小梦想家Michelle小梦想家 · 12:06 · 391 views
How to Solve "175 Combine Two Tables" on LeetCode? - MySQLBecoming A Better Dev (with David) · 4:28 · 216 views
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.
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.
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.
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 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.
Time Complexity: O(n log n), Space Complexity: O(1) using in-place sorting
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.
Time Complexity: O(1) average for insert/search, Space Complexity: O(n).
Complexity comparison
| Approach | Complexity |
|---|---|
| Using SQL LEFT JOIN | Time Complexity: O(n + m), where n is the number of rows in the |
| Using Subqueries | Time Complexity: O(n * m), where n is the number of rows in the |
| 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
| Approach | Time | Space | When to Use |
|---|---|---|---|
| SQL LEFT JOIN | O(n) | O(1) | Standard relational database queries where all rows from the main table must appear |
| SQL Subqueries | O(n) | O(1) | When retrieving related values without explicitly writing join syntax |
| Sorting + Two Pointers | O(n log n) | O(1)–O(n) | When datasets are arrays and can be sorted before matching records |
| HashMap Lookup | O(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.
