14. Why Tables Join: Keys and Relationships
14.3 How to Draw a Tiny Relationship Map Before You Write SQL
Why. A two-minute sketch of how tables connect saves you from writing a join on the wrong column. It also tells you which side may have repeats.
What. A relationship map is a small drawing with one box per table and a line from each foreign key to its primary key. A key is unique when COUNT(*) equals COUNT(DISTINCT key) and the key has no NULLs. An orphan is a row whose foreign key value has no match, for example a shipment pointing to a hub that does not exist. A missing key is different: it is NULL, which means the current value is unknown or not available.
How. Check uniqueness in hubs and hub_contacts, then find shipments whose hub does not exist or is missing.
Steps
- Draw boxes for customers, shipments, hubs and hub_contacts, with a line for each key.
- Run the first example: if rows equals distinct_ids, the key is unique.
- Run the second example and mark hub_contacts as having repeats on hub_id.
- Run the third example to list orphans and missing keys, then run the fourth example, which does the same check with NOT EXISTS.
Examples
Example 1 · dataset: swiftship_logistics
SELECT COUNT(*) AS rows,
COUNT(DISTINCT hub_id) AS distinct_ids
FROM hubs;Output
| rows | distinct_ids |
|---|---|
| 5 | 5 |
Meaning. 5 rows and 5 distinct ids, so hub_id is unique in hubs.
Example 2 · dataset: swiftship_logistics
SELECT COUNT(*) AS rows,
COUNT(DISTINCT hub_id) AS distinct_ids
FROM hub_contacts;Output
| rows | distinct_ids |
|---|---|
| 5 | 4 |
Meaning. 5 rows but only 4 distinct ids, so one hub has more than one contact. Joining to this table can repeat rows.
Example 3 · dataset: swiftship_logistics
SELECT shipment_id, hub_id
FROM shipments
WHERE hub_id IS NULL
OR hub_id NOT IN (SELECT hub_id FROM hubs);Output
| shipment_id | hub_id |
|---|---|
| 1009 | 9 |
| 1010 | NULL |
Meaning. 1009 is an orphan: hub 9 does not exist. 1010 has a missing key: its hub is unknown right now. Both need checking, but for different reasons. This NOT IN check is safe here only because hubs.hub_id is the INTEGER PRIMARY KEY, so it can never be NULL.
Example 4 · dataset: swiftship_logistics
SELECT s.shipment_id, s.hub_id
FROM shipments AS s
WHERE s.hub_id IS NULL
OR NOT EXISTS (SELECT 1 FROM hubs AS h
WHERE h.hub_id = s.hub_id);Output
| shipment_id | hub_id |
|---|---|
| 1009 | 9 |
| 1010 | NULL |
Meaning. NOT EXISTS gives the same two shipments. It stays correct even if the other table had a NULL key, so it is the safer habit when you are not sure the key is NOT NULL.
Ravindra Bagale's Tip
Keep the IS NULL check in the orphan query, because NOT IN never returns a row whose own value is NULL. Also, if the subquery itself returns a NULL, NOT IN returns no rows at all, so use NOT EXISTS when the other key might be NULL.
Ravindra Bagale's Tip – मराठी
Orphan query मध्ये IS NULL check ठेवा, कारण ज्या row ची स्वतःची value NULL आहे तो NOT IN कधीच परत देत नाही. तसंच subquery नेच NULL दिला तर NOT IN एकही row देत नाही, म्हणून दुसरा key NULL असू शकत असेल तर NOT EXISTS वापरा.
Ravindra Bagale's Tip – हिंदी
Orphan query में IS NULL check रखिए, क्योंकि जिस row की अपनी value NULL है उसे NOT IN कभी नहीं लौटाता। और अगर subquery ही NULL दे, तो NOT IN एक भी row नहीं देता, इसलिए दूसरी key NULL हो सकती हो तो NOT EXISTS लगाइए।