SQL One-to-One Relationships
Exactly one row on each side corresponds to exactly one row on the other — the least common of the three relationship patterns, but worth recognizing.
What & Why
A one-to-one relationship means each row in table A relates to at most one row in table B, and vice versa. This often shows up when a table is deliberately split in two — perhaps for security reasons, or to avoid a table with an unwieldy number of columns.
See How It Works
Growth reduces sessions to one latest-session row per user, then attaches that summary to each user once.
| id | user_id | started_at | ended_at | page_views | device |
|---|---|---|---|---|---|
| 5001 | 101 | 2024-01-16 14:20:00+00 | 2024-01-16 14:52:00+00 | 8 | desktop |
| 5002 | 102 | 2024-02-10 11:10:00+00 | 2024-02-10 11:18:00+00 | 3 | mobile |
| 5003 | 103 | 2024-03-13 08:10:00+00 | 2024-03-13 09:04:00+00 | 12 | desktop |
| 5004 | 104 | 2024-04-03 12:00:00+00 | NULL | 1 | tablet |
| 5005 | 105 | 2024-04-04 09:00:00+00 | 2024-04-04 09:21:00+00 | 7 | mobile |
| 5006 | 106 | 2024-04-05 10:15:00+00 | 2024-04-05 10:42:00+00 | 5 | tablet |
| 5007 | 107 | 2024-04-06 13:05:00+00 | 2024-04-06 13:38:00+00 | 10 | desktop |
| id | created_at | country | channel | plan | activated_at | churned_at |
|---|---|---|---|---|---|---|
| 101 | 2024-01-15 09:10:00+00 | US | organic | pro | 2024-01-16 14:25:00+00 | NULL |
| 102 | 2024-02-10 11:05:00+00 | CA | paid | free | NULL | 2024-03-20 10:00:00+00 |
| 103 | 2024-03-12 16:35:00+00 | GB | referral | pro | 2024-03-13 08:15:00+00 | NULL |
| 104 | 2024-04-01 13:20:00+00 | US | organic | free | 2024-04-03 12:00:00+00 | NULL |
WITH latest_session AS (
SELECT
user_id,
MAX(started_at) AS latest_session_at
FROM growth.sessions
GROUP BY user_id
)
SELECT
u.id AS user_id,
u.plan,
s.latest_session_at
FROM growth.users u
LEFT JOIN latest_session s ON s.user_id = u.id
ORDER BY user_id;| user_id | plan | latest_session_at |
|---|---|---|
| 101 | pro | 2024-01-16 14:20:00+00 |
| 102 | free | 2024-02-10 11:10:00+00 |
| 103 | pro | 2024-03-13 08:10:00+00 |
| 104 | free | 2024-04-03 12:00:00+00 |
The CTE reduces sessions to at most one row per displayed user.
Practice this concept
Growth wants the first 50 users with at most one latest-session summary row attached to each user.
growthPrefix tables with growth.table_name.
user_idplanlatest_session_atgrowth.sessions| Column | Type |
|---|---|
| id | bigint |
| user_id | integer |
| started_at | timestamp with time zone |
| ended_at | timestamp with time zone |
| page_views | integer |
| device | text |
| internal_session_score | integer |
growth.users| Column | Type |
|---|---|
| id | integer |
| created_at | timestamp with time zone |
| country | text |
| channel | text |
| plan | text |
| activated_at | timestamp with time zone |
| churned_at | timestamp with time zone |
| legacy_user_code | text |
Sign up free to try it on a real business scenario