Best database schema for country, region, county, town - sql

I have country, region, county, town data and I'm currently deciding between 2 schema designs (if there's a better one, do tell).
I first thought
Country
Id
Name
Region
Id
CountryId
Name
County
Id
RegionId
Name
Town
Id
CountyId
Name
Does the job however to get all towns in a country you have to 3 inner joins to do the filtering. I guess this could be ok but potentially expensive?
The other design was:
Country
Id
Name
Region
Id
Name
County
Id
Name
Town
Id
CountryId
RegionId
CountyId
Name
This way all hierarchical data so to speak is at the bottom and you can go back up however if you want all regions in a country you're a bit screwed which makes we wonder whether the first design is best.
What do you think is the best schema design?

The best database design depends on how the data is being used.
If this is pretty static data that will all be updated at one time and external references are all to towns, then I would probably go for a denormalized dimension. That is, store the information all in one row:
Town Id
Town name
County name
Region name
Country name
Under the above scenario, the ids for county, region, and country are not necessary (by assumption).
If the data is being provided as separate tables with separate ids, and these tables can be updated independently or row-by-row, then a separate table for each makes sense. Putting all the ids into the towns table may or may not be a good idea. You will have to verify and maintain the hierarchies when data is inserted and updated.
If ids for each level are necessary for your, then you should have appropriate table structure for declaring foreign key constraints. But, this can get complicated. Will an external entity have a "geography" attribute that can be at any level? Will an external always know what level it is going to refer to as?
In other words, you need to know how the data is going to be used in order to define an appropriate data model.

Related

Design Sql Tables with common columns

I have 5 tables that have the same structure and same columns: id, name, description. So I wonder what is the best way to design or to avoid having 5 tables that have the same columns:
Create a category table that will include my three common
columns and another column "enum" that will differentiate my categories
ex (city, country, continent, etc.)
Create a category table that will include my three common
columns and create the other five tables that will just include an
id.
Note that I would have an assocation table that should include the id of cities, id countries, id continents, etc. so i can display them into a report
Thank you for your advice.
The decision on how many tables to have under these circumstances simply depends.
The most important factor is whether the five things are independent entities or whether they are related. A simple way to understand this is by understanding foreign key relationships: Will other tables have a column that could refer to any of the five (say "geoid")? Or will other tables have a column that generally refers to one of the five ("cityid", "countryid")? The ability to define helpful foreign key constraints often drives the table structure.
There are other considerations. If your data is at the geographic level, then it might represent hierarchies . . . cities are in countries, countries are on continents. Some databases (such as MySQL) do not support hierarchical queries at all. Under these circumstances, you might consider denormalizing the data for querying purposes.
Other considerations can also come into play. If your application is going to be internationalized, then having all the reference tables in a single place is handy -- for providing cultural-specific information (language, currency symbol, and so on). One way of handling this situation is to put all such references in a single table (and perhaps using more sophisticated foreign key relationships).
The column names are not important, just the data in the columns. If City description, country description and continent description are different information then you are already doing this the right way. The only time you would aim to reduce this data would be if you were repeating information but for the titles of the data it's fine.
In fact. You are doing this correctly. Country will have different values from city for every field mentioned. Id is just an id, every table should have one. Name and description wont be the same across country and city.
Also, this way if you want a countrys name you dont have to go through every country, continent and city. You only have 192 or so entries to go through. If you had all of that in one massive table you would have to use it for everything and go through every result every time you want data. You would also have to distinguish between cities, countries and continents in some other way than the separate tables.
Eg:
method 1, with 5 tables:
SELECT * FROM country
does the same as
method 2, 1 table:
SELECT * FROM table WHERE enumvalue = 'country';
If you have tables representing city, country and continent, and they all have exactly the same fields, you have a fundamental problem. In real life, every city is in a country and every country is in at least one continent (more or less) but your data structure does not reflect that. Your city table should look something like this:
id (primary key)
countryId (foreign key to country)
name
other fields
You'll need a similar relationship between countries and continents. However, before you do so, you have to decide what to do about countries like Russia which is in two continents and Palau which isn't really in any.
You may also want to have a provinceStateTerritory table to help you sort out the 38 places in the United States named Springfield. Or, you may want to handle that situation differently.

PK for table that have not unique data

I have 2 tables like
Company( #id_company, ... )
addresses( address, *id_company*, *id_city* )
cities( #id_city, name_city, *id_county* )
countries( #id_country, name_country )
What i want is :
It is a good design ? ( a company can have many addresses )
And the important thing is that you my notice that i didn't add a PK for addresses table because every address of a companies will be different, so am I right ?
And i will never have a where in a select that specify a address.
First of all we should distinguish natural keys and technical keys. As to natural keys:
A country is uniquely identified by its name.
A city can be uniquely identified by its country and a unique name. For instance there are two Frankfurt in Germany. To make sure what we are talking about we either use the distinct names Frankfurt/Main and Frankfurt/Oder or use the city name with its zip codes range.
A company gets identified by its full name usually. Or use some tax id, code, whatever.
To uniquely identify a company address we would take the company plus country, city and address in the city (street name and number usually).
You've decided to use technical keys. That's okay. But you should still make sure that names are unique. You don't want France and France in your table, it must be there just once. You don't want Frankfurt and Frankfurt without any distinction in your city table for Germany either. And you don't want to have the same address twice entered for one company.
company( #id_company, name_company, ... ) plus a unique constraint on name_country or whatever makes a company unique
countries( #id_country, name_country ) plus a unique constraint on name_country
cities( #id_city, name_city, id_county ) plus a unique constraint on name_city, id_country
addresses( address, id_company, id_city ) with a unique constraint on all three columns
From what you say, it looks like you want the addresses only for lookup. You don't want to use them in any other table, not now and not in the future. Well, then you are done. As you need a unique constraint on all three columns, you could just as well declare this as your primary key, but you don't have to.
Keep in mind, that to reference a company address in any other future table, you would have to store address + id_company + id_city in that table. At that point you would certainly like to have an address id instead. But you can add that when needed. For now you can do without.
It's okay - you might want to add some non-unique index on company_id so company address queries are sped up. Another option would be making a joining table between Company and Address, but that would probably only be justified if Address stored more data(so searches would be slower).
This design is fine.
A (relational) table always has a (candidate) key. (One of which you can choose as the primary key, but candidate keys, aka keys, are what matter.) Because if no subset of columns smaller than set of all columns is unique then the key is the set of all columns.
Since every table has one, in SQL you should declare it. Eg in SQL if you want to declare a FOREIGN KEY constraint to the key of this table then you have to declare that column set a key via PRIMARY KEY, KEY or UNIQUE. Also, telling the DBMS what you know helps optimize your use of it.
What matters to determining keys are subsets of columns that are unique that don't have smaller subsets that are unique. Those are the keys.
A company, address or city is not unique since you are going to have multiple of each.
A (city,address) is not unique normally.
A (city,company) is not unique normally.
A (company,address) is not unique normally.
So (company,address,city) is the (only) (candidate) key.
Note that if there were only ever one city, then (company,address) would be the key. And if there were only ever one company, then (address,city) would be the key. So your given reason that the "because every address[+city?] of a company [?] will be different" isn't sound unless we're supposed to assume other things.
I'm making this an answer instead of a comment because of length. As to the address table having a defined primary key, the answer is yes. There are several good reasons but just consider this one.
Suppose a company had several addresses and a move required you to delete one of the addresses. You can't just delete where comp_id = x as that would delete all the addresses for that company. You have to have where comp_id = x and something_else where the something else must differentiate the one address from all the others for that company. So you have to have someone look at the different addresses to see how they differ and select the one difference that correctly identifies the one address and then write that correctly into the where clause.
That's a lot of work to do every time you want to delete (or update) an address.
It also means it's more difficult to write a parameterized delete statement that can be used to delete any address. Suppose a company has several locations in the same building: Shipping in Suite 101, Marketing in Suite 202 and IT in (of course) the basement. So the street, city, state, everything is the same, different only in Suite_No or whatever is used to refine the address.
Then consider your user. Most of the time, a user isn't going to be interested in seeing every single address you have listed for a company. He's only interested in Product Testing. You should be able to give them Product Testing's address and no other. Users are not known for their patience when presented with a data dump every time they do a query and it's up to them to select the one they're looking for.
It just solves so many problems to be able to specify where addr_id = x.
An address is a thing and should have its own table.
An address can exist without a company, therefore it should not have a foreign key to company. Also, what if you start selling to/buying from individuals?
A company can have zero, one, or many addresses.
Two or more companies can have the exact same address. You assumption is flawed.
Use a junction table:
company -< company_address >- address

How to include these requirements in my database design?

I am developing an intranet application for my company. The company has a complicated structure in which there are many business lines, departments, divisions, units and groups. I have to take care of all of these things. Some employees work under department level, some of them work under unit level and so on. The problem now is with database design. I am confused about how to design the database. At the beginning, I decided to design it as following:
Employees: Username, Name, Title, OrgCode
Departments: OrgCode, Name
Divisions: OrgCode, Name
Units: OrgCode, Name
but the problem as I said before, some employees are working under departments, so how to make relations between all of these tables. Is it possible to have OrgCode in Employees table as a foreign key to OrgCode in Departments, Divisions and Units tables?
Could you please recommend me how to design it?
UPDATE:
#wizzardz put a nice database design. All what I need now is to have an example of data that fit this database design
Here's a set of data that I am using in the database:
let us assume that we have Employee with the following information:
Username: JohnA
Name: John
Title: Engineer
OrgCode: AA
And let us assume we have department AA, how I will distribute this data into the database design?
You could do a design something like this
rather than going for separate tables for Departments, Divisions etc try to store them in a single table with a TypeId to distingush Departments , Divisitions etc.
Could you try a design like this
In the Level table you need to enter the values like 'Deparments,Divisions', Groups, etc (By keeping it in a separate table you can handle any future addition of new levels by your organisation.)
In the OrganisationLevels table you need to store Department Names,Division Name, GroupName etc.
The Employee table has a forigen key reference with the table Organisation level, that will store which Level an employee is working in the organisation.
If you wants to store the work history of a particular employee/ there is a chance that an employee can be moves to one level to another I would suggest you to go for this design
Sample data wrt the design
Level
Id LevelType
1 Department
2 Division
3 Group
OrganisationLevels
Id Name LevelId Parent*(Give a proper name to this column)*
13 AA 1 NULL
.
.
21 B 2 13 (This column refer to the Id of department it belongs to.)
Employees
Id UserName Name Title
110 JohnA John Engineer
EmployeeWorkDetails
Id EmployeeId OrganisationLevelId StartDate EndDate IsActive
271 110 13 20/09/2011 NULL true
OrgCode from the Employee Table can be removed, because I thought it is the employee code of the employee with that organisation.
I hope this helps.
Employees: Username, Name, Title, OrgCode
Departments: OrgCode, Name
Divisions: OrgCode, Name
Units: OrgCode, Name
To the above mentioned DB design, have one more table called Org containing OrgCode and another column as type (which we give insights on which type of org is it i.e. Departments,Divisions and Units)
then you can have employee table's OrgCode to have refer the OrgCode of Org table(parent-child relationship).
I suggest that you learn the difference between analysis and design. When you design a database, you are inventing tables, columns and constraints that will affect how the data is stored and retrieved. You are concerned with ease of update and query, including operations you will learn about later.
When you analyze the data requirements, you aren't engaged in inventing things, you are engaged in discovering things. And the things you are discovering are things about the "real world" the subject matter is supposed to represent. You break the subject matter down into "entities" and relationships among those entities. Then you relate every value stored in the database to an instance of an attribute, and every attribute to some aspect of either an entity or a relationship. This results in a conceptual model.
In your case, the relationships between employees, departments, units, etc. sound quite complex. It's worth quite a bit of effort to come up with a model that reflects this complex reality accurately.
Once you have a good conceptual model, you can create SQL tables, columns, and constraints that adequately represent the conceptual model. This involves design skills that can be learned. But if you have a lousy conceptual model, you're doomed, no matter how good you are at design.

sql - denormalize address

Customer Table - CustomerId, Street, City, State, Zipcode
ZipCode Table - ZipCodeId, ZipCode, CityId, StateId
However, what I am confused about it is - should I put CityId, StateId and ZipCodeId or should I put CityName, StateName and ZipCode in Customer Table? And should these be set up as referencing foreign key in city, state and zipcode tables? Should I get rid of City Table altogether and just repeat city's name in ZipCode table and Customer table?
Nominally, the customer table should only contain the street address and zip code ID (not the city or state). Note that data entry for such a normalized scheme is not necessarily straight-forward; people will expect to enter city, state, zip code (or maybe just zip code) and the onus will be on the application to map that correctly and disambiguate when necessary.
I too live in a zip code used by two cities; they even happen to be in different counties, which leads to questions about which county I live in on occasion. One of the cities has multiple other zip codes; the other only has (part of) the one zip code. There's no problem: you would have two separate ZipCodeID entries for the same ZipCode, one for each city. Note that this means that there would not be a unique index on the ZipCode column in the ZipCode table.
Where would you store the +4 of the Zip+4 scheme? Good question! That belongs with the street address.
If it is possible to have more than one city with same zip code, then a better solution that you have a City table, where you have zipcode column (just varchar or int column, no foreign key). In City table you keep foreign key from State table. At the end you should keep CityId in Customer table.
Reason for this: City and State tables are small tables, and joining them you don't make performance problem, just make index on them and everything will be fine.
Your design looks ok to me - keep full address in customer table. Just make sure that you permit to enter multiple entries into ZipCode table having the same zipcode but different cities (i.e. do not make ZipCode.ZipCode unique key).
See here:
Zip codes relate only to the mail system and have absolutely nothing
to do with political boundaries. Some cities will be covered by more
than one zip code; some zip codes will cover more than one city.
It is very frustrating when you call insurance company and after telling them zipcode they would give you really bad quote - simply because you seem to live in different city across the road, and that city has exactly the same zip code, but bad crime situation.
You can also incorporate USPS address correction into your application to standardize and keep this information normalized. See: https://ribbs.usps.gov/index.cfm?page=aec

Storing region / city data in User table

Suppose I have the following:
Table region_city
id name parent_id
==============================
1 North null
2 South null
3 Manchester 1
4 London 2
In my user table I store the ID of the City that the user is in.
Now in my search form I need to be able to perform a top-level search, i.e. find all Users that belong to a given region (North or South).
Will it make life easier if I included a region_id field in my user table? Or is that going against the normalisation concept?
It does denormalize the table structures and it could introduce data update anomalies. Consider: the user moves from Manchester to London and the city_id changes. The region_id could still point to the North.
The region_id only depends on the city therefore it does not belong in the user table. Since it can be derived from the city.
If the design absolutely calls for only two levels (region and city) and you are willing to forgo the possible addition of other levels in the future (not a decision I would be inclined to make, but you know your data better than I do) then do not include the regionID in your user table; that would denormalize your database. Instead, you have several choices for representing the data (including two related tables, region and city) and you would perform your search by JOINing the city table to the user table or using an IN clause in your search.