This is how I typically design tables for addresses, contacts and e-mails:
Example address types might be "Home", "Mailing", "Work", etc.
address_types
id varchar(15)(P)
The reason city_id
and county_id
can both be NULL is because in Virginia an address is either within a city or a county - but not both. So at the application level I enforce that at least one of these two fields must not be NULL.
addresses
id unsigned int(P)
address_type_id varchar(15)(F address_types.id)
line1 varchar(50)
line2 varchar(50)
city_id unsigned int(F cities.id) Default NULL
county_id unsigned int(F counties.id) Default NULL
zip varchar(6)
zip4 char(4) Default NULL
lat decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL lon decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL
You may or may not want things like fips_number
and the columns listed after it. They are codes that were used by the US government.
cities
id unsigned int(P)
state_id unsigned int(F states.id)
name varchar(50)
lat decimal(10,8) // Provides for accuracy to ~1mm. Default to NULL
lon decimal(11,8) // Provides for accuracy to ~1mm. Default to NULL
fips_number unsigned int // Default NULL
census_code unsigned int // Default NULL
census_class_code char(2) // Default NULL
gsa_code unsigned int // Default NULL
opm_code unsigned int // Default NULL
There's a many-to-many relationship between cities and counties so we define those here. Each column is a foreign key to it's respective table and together they form the primary key.
cities_counties
city_id unsigned int \_ (P) (F cities.id)
county_id unsigned int / (F counties.id)
Example contact types might be "Home", "Work", "Pager", "Cell", etc.
contact_types
id varchar(15)(P)
You may or may not want to break up the phone numbers into their constituent parts. In this example they are broken up. Handy way to query for numbers in a particular country code, area code, etc.
contacts
id unsigned int(P)
contact_type_id varchar(15)(F contact_types.id)
country_code char(3) // Default to NULL
area_code char(3)
exchange char(3)
station char(4)
extension varchar(10) // Default to NULL
This would hold all the counties, parishes and other similar geographical areas in US states and territories.
counties
id unsigned int(P)
state_id unsigned int(F states.id)
name varchar(50)
fips_number unsigned int // Default NULL
Take a look at ISO 3166-1 for more information about country codes.
countries
id char(2)(P)
iso3 char(3)(U)
iso_num char(3)(U)
name varchar(44)(U)
Example contact types might be "Home", "Work", etc.
email_types
id varchar(15)(P)
E-mail addresses - you may or may not want to split them up like in my example.
emails
id unsigned int(P)
email_type_id varchar(15)
mailbox varchar(255)
domain varchar(255)
Take a look at ISO 3166-2 for more information about state codes.
states
id unsigned int(P)
country_id char(2)(F countries.id)
code varchar(3)
name varchar(45)
fips_number unsigned int // Default NULL
And then of course you tie them to an entity:
customer_addresses
id unsigned int(P)
customer_id unsigned int(F customers.id)
address_id unsigned int(F addresses.id)
customer_contacts
id unsigned int(P)
customer_id unsigned int(F customers.id)
contact_id unsigned int(F contacts.id)
customer_emails
id unsigned int(P)
customer_id unsigned int(F customers.id)
email_id unsigned int(F emails.id)