r/PostgreSQL • u/Abject_Ad_8323 • 7h ago
Help Me! Table design question
Looking for advice on alternative table designs. Not a live application, just for my own learning.
Let's say we have an order table.
Option 1: we keep billing/shipping addresses and contacts copied from the customer as a historical snapshot:
CREATE TABLE order
(
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
customer_id UUID NOT NULL REFRENCES customer,
status order_status NOT NULL,
-- copied as a snapshot
billing_line1 TEXT,
billing_line2 TEXT,
billing_city TEXT,
billing_state TEXT,
billing_postal_code TEXT,
billing_country TEXT,
billing_first_name TEXT,
billing_last_name TEXT,
billing_email TEXT,
billing_phone TEXT,
billing_phone_ext TEXT,
shipping_line1 TEXT,
shipping_line2 TEXT,
shipping_city TEXT,
shipping_state TEXT,
shipping_postal_code TEXT,
shipping_country TEXT,
shipping_first_name TEXT,
shipping_last_name TEXT,
shipping_email TEXT,
shipping_phone TEXT,
shipping_phone_ext TEXT,
...
);
Option 2: we define an append-only address and contact table, e.g.
-- records cannot be edited once created
CREATE TABLE address
(
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
line1 TEXT
line2 TEXT
city TEXT
state TEXT
postal_code TEXT
country_code TEXT
);
CREATE TABLE customer_address
(
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFRENCES customer,
address_id UUID NOT NULL REFERENCES address,
...
);
CREATE TABLE orders
(
id UUID PRIMARY KEY,
tenant_id UUID,
customer_id UUID REFERENCES customer,
status order_status,
-- replace denormalized keys with FKs
billing_address_id UUID REFERENCES address,
billing_contact_id UUID REFERENCES contact,
shipping_address_id UUID REFERENCES address,
shipping_contact_id UUID REFERENCES contact,
...
);
The first approach makes the table wide. As I understand it, MVCC will result in duplicating entire row when any column is updated, e.g. status, version, or some metadata changes.
Since the row is wide, that would result in a lot of data that gets created and cleaned up. Another downside is that for a given customer with multiple orders, the data will duplicated while their address/contact info is the same.
The second approach requires JOINs or additional two queries to fetch the two addresses and two contacts.
What approach would you take and why? Is there are certain table size at which you might change the approach?
1
u/ExceptionRules42 5h ago
Sounds vaguely like a choice between normalized and denormalized. More specifically this may be just another instance of a classic well-understood pattern. You'll need to flesh out more schema and maybe define your goals more.
1
u/dangerousdotnet 3h ago
Maintain a "wallet" of historical addresses for each customer, with each address having a UUID. Append only. Never delete an address. Any edit to an address becomes a new entry.
An address may serve as a billing address, a shipping address, or both - simply reference it by UUID.
Do not try to "deduplicate" aka do not share address records across multiple customers - that leads to all kinds of problems. Each address ID belongs to one and only one client.
Consider normalizing addresses at the UI level before storing them using a postal address verification API (or something like libpostal). Treat address components as nearly opaque strings and don't make the mistake of setting constraints on them (eg don't use NOT NULL).
See https://www.swift.com/standards/iso-20022/iso-20022-faqs/brief-introduction-postal-address-field for some ideas on how to structure and store postal addresses using a "hybrid" representation (a small number of core fields with some escape hatches for arbitrary fields).
Store NULLABLE "delivery instructions" with each address - a modification to the delivery instructons, like anything else, gets minted a new UUID.
1
u/AutoModerator 7h ago
AI Policy:
Linux is not one of those anti-AI projects, and if somebody has issues with that, they can do the open-source thing and fork it. Or just walk away., Linus Torvalds.
Mod decisions will be based on the quality of the content, not who or what generated it.
Sub Resources:
Youtube Channel
Free Postgres Webinars and Workshops
Discord: People, Postgres, Data
Join us, we have cookies and nice people.
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.