I have 2 Similar Types of Things that I want to point to in a certain field in a Database. One of them is a combination of 1 or more of the other.
How should I Design my Database in this kind of situation?
In my current example I have (Simple)Food Ingredients and (Combined)Food Dishes and I want Either One or these Things to be entries in a Meals/Eating table.
So a User can Either Eat a simple Food like an Apple OR a complex food like an Apple Pie that consists of 200g of Apples and 100g of Flour and 30g of Sugar etc. at one point in time in a Meal. I'm thinking something like this:
Ingredients |IID| |Name| |Calories|
Dishes |DID| |Name| (|Calories|???)
Food Data |DID| |IID| |Amount|
.
Users |UID| |FirstName| |LastName| etc.
Meals |UID| |DID| |Date/Time| |Amount|
I Find this really annoying tho because Every Single Ingredient would have to have Two (Basically Identical)Entries to start with: 1 in the Ingredients Table and 1 in the Dishes Table so it could be paired up in a Meal. Am I missing something Here? Is there a way around this?
Also I don't know if a Dish should have the Calories Listed in the Database. Having the Calories for a Dish in the database is rather Redundant because it could be Calculated when Making a Query(by summing up&calculating its respective ingredients). BUT this seems quite inefficient since it this calculation would have to be done for every single query of a dish(and it would get worse by adding things like Macros/Nutrional Values/Price which I left out for clarity/simplicity here).
Also If I DO have Calories(and other things relating to food in general) for a Dish I could just have 1 single table in this scenario like:
Food |FID| |Name| |Calories| (|Simple[bool]|?)
Food Data |FID| |FID| |Amount|
This would Seem better in general. The Simple field would distinguish between Simple Ingredients or Dish which I think is worth putting in so you don't have to search in Food Data for every item.
BUT If I want to introduce Specific Dish-Only Data then I would to make some Other Table like:
DISH DATA |FID| |TimetoCook| |Presentation| etc. (which seems pretty weird/unintuitive to me)
.
So the Question is: What the BEST General Practice in this kind of scenario?
Is it generally better to do extra calculations when querying rather than have redundant data in these kinds of situations?
Is there something I'm missing that would make this simpler/better in general?
I'm not sure this can be answered as generally as you would like, because the semantics and the use of the database should be taken into account. Even in the simple/complex food context of your example, either of the approaches you describe (ingredients/dishes/food_data or food/food_data/dish_data) can be right, depending on the specifics.
Let me get this out of the way first: I wouldn't look for a third approach. Any other thing I can think of would be semantically obscure, hell to maintain or a nightmare to query.
So your first concern is the semantics of the database. Your first approach seems more natural; most people will easily see the semantic distinction between ingredients and dishes. It is also the only option if the "ingredient" entity has another reason of existence besides being part of a dish, e.g. for managing orders of raw ingredients. If you choose to go with the second approach you will have to make sure that a) it fits your data and b) you choose your table names very very carefully.
For the second approach to "fit your data" semantically, simple dishes must fully fit the description: "dishes that don't have the extra dish_data". The [Simple] flag is also acceptable as a property of dish, though a real need for it can be a hint that you're off base with this approach. But if ingredients and dishes only partially overlap, i.e. if you have ingredients that cannot be dishes, or if they have different properties in general, then you are definitely off base. If you find yourself in need of enforcing business rules that would prevent a customer from ordering a serving of "flour"; if you raise questions like what to put under "calories" for the "pickles" (would it be the calories per 100gr for pickles-as-an-ingredient, or the calories per serving for pickles-as-a-side-dish?); if you find you have fields like "measuring unit" that are meaningless for dishes, then you're dealing with two separate entities (ingredients and dishes), not one entity (dish) with two subcategories (simple and complex). If you are only going to duplicate a tiny bit of information between the two tables and save yourself a lot of trouble and ambiguity, by all means do that.
Your second concern is how the data will be used. Try to answer questions like: Are you going to be querying calories of dishes millions of times per second? Are the ingredients - and therefore the calories - of dishes going to stay the same for ever? Will your customer or cook ever need to query what a dish is made of?
"Don't duplicate" and "don't store calculatable values" are two rules that are as hard as design rules come. Even such rules though should be, not really bent, just "critically adjusted" some times, if that makes sense.
This is a question of understanding the context of your data.
I imagine meals can be simple (unprocessed) or be complex and consist of other meals. If I were to generate a database for meals and their calorific value I would not separate them.
meal | calorific value per 100g | glicemic index
apple | 12345 | 34234
apple-pie | 3233 | 32334
Other table you would join it with could be a meal composition for a specific person.
2020-02-27|Johny Doe | Breakfast |apple | 300 g
2020-02-27|Johny Doe | Breakfast |sausage| 150 g
2020-02-27|Johny Doe | Breakfast |apple-juice | 500 g
By joinning the two tables you would learn how much Johny Doe ate callories and perhaps what was the glicemic index...
Then... it is not yet an SQL question but a the question of understanding first the process one would like to describe with SQL.
Here's a fairly straightforward many-to-many mapping of Nerf gun toys to the price range that they fall under. The Zombie Strike and Elite Retaliator are pricey, while both the Jolt Blaster and Elite Triad are cheaper (in the $5.00-$9.99) range.
So far so good. But what happens when I want to start tracking the prices of other items? These other items have different columns, but still need PRICE_RANGES mappings. So I can potentially still use the PRICE_RANGES table, but I need other tables for the other items.
Let's add board games. How should I model this new table, and others like it?
Should I add multiple many-to-many tables, one for each new type of item I'm tracking?
Or should I denormalize PRICE_RANGES, get rid of the mapping tables altogether, and just duplicate PRICE_RANGES tables for every item type?
The second solution has the advantage of being much similar, but at the cost of duplicating all the ranges in PRICE_RANGES. (and there may be many thousands of PRICE_RANGES, depending on how small the increments are). Is that denormalization still a valid solution?
Or maybe there's a third way that's considered better than these two?
Thanks for the help!
Why do you have a "price ranges" table at all? That would make it highly restrictive. Unless there is a really compelling reason I am missing... Here is what I would consider.
Drop the mapping tables
Drop the price ranges tables
Add a min price and max price to each table you want to track price ranges. If there is no range, you can either allow max price to be null, or make both be the same price. Then you can just query the tables to find items within whatever range you want.
Another thought I would consider... how many different types of products are you trying to track? If you are going to make a separate table for every single kind of product... that will quickly become unmanageable if you expect to have hundreds or thousands of items. Consider having a "Product" table that has columns that share attributes, such as price, across all the products. It would have a ProductType column that either references a lookup table or just puts the types directly in the column. Then have either a separate key/value table to cover other random things like bolt capacity. Or even consider putting that in an xml/json/blob column to cover all the extra bits of info.
I need a new perspective on how to design a reliable and efficient SQL database to store multi-level arrays of data.
This problem applies to many situations but I came up with this example:
There are hundreds of products. Each product has an undefined number of parts. Each part is built from several elements.
All products are described in the same way. All parts would require the same fields to describe them (let's say: price, weight, part name), all elements of all parts also have uniform design (for example: element code, manufacturer). Plain and simple.
One element may be related to only part, and each part is related to one product only.
I came up with idea of three tables:
Products:
--------------------------------------------
prod_id prod_name prod_price prod_desc
1 hoover 120 unused
next
Parts:
----------------------------------------------------
part_id part_name part_price part_weight prod_id
3 engine 10 20 1
and finally
Elements:
---------------------------------------
el_id el_code el_manufacturer part_id
1 BFG12 GE 3
Now, select a desired product, select all from PARTS where prod_id is the same, and then select all from ELEMENTS where part_id matches - after multiple queries you've got all data.
I'm just not sure if this is the right approach.
I've got also another idea, without ELEMENTS table.
That would decrease queries but I'm a bit afraid it might be lame and bad practice.
Instead of ELEMENTS table there are two more fields in the PARTS table, so it looks like this:
part_id, part_name, part_price, part_weight, prod_id, part_el_code, part_el_manufacturer
they would be text type, and for each part, information about elements would be stored as strings, this way:
part_el_code | code_of_element1; code_of_element2; code_of_element3
part_el_manufacturer | manuf_of_element1; manuf_of_element2; manuf_of_element3
Then all we need is to explode() data from those fields, and we get arrays, easy to display.
Of course this is not perfect and has some limitations, but is this idea ok?
The reason I came up with the second variant is that the third table - Elements - would eventually become quite big. If there is 10,000 products, 4 parts for every product, and on average 3 elements per part - it means there must be 120,000 rows in the Elements table. And to be honest - I don't really know if that would contribute to performance problems.
Should I just go with the first idea? Or maybe there is a better approach to this problem?
Describing relationships between things is exactly what relational databases do. Any book on SQL should give many examples and I suggest looking for such a book to understand these issues better but I will give a quick answer to your question.
You are on the right track for the simple solution with your 3 tables, but you don't need three select statements. That is what the join is used for. With your example the following select statement would give you all the data for a particular product.
select *
from product
join parts on parts.prod_id = product.prod_id
join elements on elements.part_id = part.parts_id
where product.prod_id = x
This is fine and will do exactly what you require.
As far as you 2nd idea, it is really bad. You are working with a relational database, it allows you to store sets of data and relate it. To store a list in a string value is to ignore the exact functionality that relational databases are designed to do well.
However, there is a way to improve your design. I think you might not always have a one-to-one relationship for product and part or part and element. So I would suggest the following.
First remove prod_id from the parts table and then remove part_id from the elements table.
Then create two "joining tables". Lets call them prodparts and partselement. They would look like this:
prodparts would contain two columns prod_id and part_id (and maybe a count?)
partselement would contain two columns part_id and el_id (and maybe a count?)
Now each row in these tables would describe the relationship. BUT and this is the neat part, multiple products could use the same part and multiple parts could use the same element.
The select is only slightly more complicated and looks like this:
select *
from product
join prodparts on procparts.prod_id = product.prod_id
join parts on parts.part_id = prodparts.part_id
join partselement on partelement.part_id = parts.part_id
join elements on elements.el_id = partelement.parts_id
where product.prod_id = x
You're first approach seems quite solid. The second approach would make it difficult to maintain the data of the elements. One thing I noticed in the first design is that the product and the parts both have a "price". Do they mean something different or does the sum of the parts prices equal the product's final price? If so, I recommend removing the price field at the product level and use the sum() function at the parts level.
Other than that... good job!
I am designing a system for a client, where he is able to create data forms for various products he sales him self.
The number of fields he will be using will not be more than 600-700 (worst case scenario). As it looks like he will probably be in the range of 400 - 500 (max).
I had 2 methods in mind for creating the database (using meta data):
a) Create a table for each product, which will hold only fields necessary for this product, which will result to hundreds of tables but with only the neccessary fields for each product
or
b) use one single table with all availabe form fields (any range from current 300 to max 700), resulting in one table that will have MANY fields, of which only about 10% will be used for each product entry (a product should usualy not use more than 50-80 fields)
Which solution is best? keeping in mind that table maintenance (creation, updates and changes) to the table(s) will be done using meta data, so I will not need to do changes to the table(s) manually.
Thank you!
/**** UPDATE *****/
Just an update, even after this long time (and allot of additional experience gathered) I needed to mention that not normalizing your database is a terrible idea. What is more, a not normalized database almost always (just always from my experience) indicates a flawed application design as well.
i would have 3 tables:
product
id
name
whatever else you need
field
id
field name
anything else you might need
product_field
id
product_id
field_id
field value
Your key deciding factor is whether normalization is required. Even though you are only adding data using an application, you'll still need to cater for anomalies, e.g. what happens if someone's phone number changes, and they insert multiple rows over the lifetime of the application? Which row contains the correct phone number?
As an example, you may find that you'll have repeating groups in your data, like one person with several phone numbers; rather than have three columns called "Phone1", "Phone2", "Phone3", you'd break that data into its own table.
There are other issues in normalisation, such as transitive or non-key dependencies. These concepts will hopefully lead you to a database table design without modification anomalies, as you should hope for!
Pulegiums solution is a good way to go.
You do not want to go with the one-table-for-each-product solution, because the structure of your database should not have to change when you insert or delete a product. Only the rows of one or many tables should be inserted or deleted, not the tables themselves.
While it's possible that it may be necessary, having that many fields for something as simple as a product list sounds to me like you probably have a flawed design.
You need to analyze your potential table structures to ensure that each field contains no more than one piece of information (e.g., "2 hammers, 500 nails" in a single field is bad) and that each piece of information has no more than one field where it belongs (e.g., having phone1, phone2, phone3 fields is bad). Either of these situations indicates that you should move that information out into a separate, related table with a foreign key connecting it back to the original table. As pulegium has demonstrated, this technique can quickly break things down to three tables with only about a dozen fields total.
I understand the concept of database normalization, but always have a hard time explaining it in plain English - especially for a job interview. I have read the wikipedia post, but still find it hard to explain the concept to non-developers. "Design a database in a way not to get duplicated data" is the first thing that comes to mind.
Does anyone has a nice way to explain the concept of database normalization in plain English? And what are some nice examples to show the differences between first, second and third normal forms?
Say you go to a job interview and the person asks: Explain the concept of normalization and how would go about designing a normalized database.
What key points are the interviewers looking for?
Well, if I had to explain it to my wife it would have been something like that:
The main idea is to avoid duplication of large data.
Let's take a look at a list of people and the country they came from. Instead of holding the name of the country which can be as long as "Bosnia & Herzegovina" for every person, we simply hold a number that references a table of countries. So instead of holding 100 "Bosnia & Herzegovina"s, we hold 100 #45. Now in the future, as often happens with Balkan countries, they split to two countries: Bosnia and Herzegovina, I will have to change it only in one place. well, sort of.
Now, to explain 2NF, I would have changed the example, and let's assume that we hold the list of countries every person visited.
Instead of holding a table like:
Person CountryVisited AnotherInformation D.O.B.
Faruz USA Blah Blah 1/1/2000
Faruz Canada Blah Blah 1/1/2000
I would have created three tables, one table with the list of countries, one table with the list of persons and another table to connect them both. That gives me the most freedom I can get changing person's information or country information. This enables me to "remove duplicate rows" as normalization expects.
One-to-many relationships should be represented as two separate tables connected by a foreign key. If you try to shove a logical one-to-many relationship into a single table, then you are violating normalization which leads to dangerous problems.
Say you have a database of your friends and their cats. Since a person may have more than one cat, we have a one-to-many relationship between persons and cats. This calls for two tables:
Friends
Id | Name | Address
-------------------------
1 | John | The Road 1
2 | Bob | The Belltower
Cats
Id | Name | OwnerId
---------------------
1 | Kitty | 1
2 | Edgar | 2
3 | Howard | 2
(Cats.OwnerId is a foreign key to Friends.Id)
The above design is fully normalized and conforms to all known normalization levels.
But say I had tried to represent the above information in a single table like this:
Friends and cats
Id | Name | Address | CatName
-----------------------------------
1 | John | The Road 1 | Kitty
2 | Bob | The Belltower | Edgar
3 | Bob | The Belltower | Howard
(This is the kind of design I might have made if I was used to Excel-sheets but not relational databases.)
A single-table approach forces me to repeat some information if I want the data to be consistent. The problem with this design is that some facts, like the information that Bob's address is "The belltower" is repeated twice, which is redundant, and makes it difficult to query and change data and (the worst) possible to introduce logical inconsistencies.
Eg. if Bob moves I have to make sure I change the address in both rows. If Bob gets another cat, I have to be sure to repeat the name and address exactly as typed in the other two rows. E.g. if I make a typo in Bob's address in one of the rows, suddenly the database has inconsistent information about where Bob lives. The un-normalized database cannot prevent the introduction of inconsistent and self-contradictory data, and hence the database is not reliable. This is clearly not acceptable.
Normalization cannot prevent you from entering wrong data. What normalization prevents is the possibility of inconsistent data.
It is important to note that normalization depends on business decisions. If you have a customer database, and you decide to only record a single address per customer, then the table design (#CustomerID, CustomerName, CustomerAddress) is fine. If however you decide that you allow each customer to register more than one address, then the same table design is not normalized, because you now have a one-to-many relationship between customer and address. Therefore you cannot just look at a database to determine if it is normalized, you have to understand the business model behind the database.
This is what I ask interviewees:
Why don't we use a single table for an application instead of using multiple tables ?
The answer is ofcourse normalization. As already said, its to avoid redundancy and there by update anomalies.
This is not a thorough explanation, but one goal of normalization is to allow for growth without awkwardness.
For example, if you've got a user table, and every user is going to have one and only one phone number, it's fine to have a phonenumber column in that table.
However, if each user is going to have a variable number of phone numbers, it would be awkward to have columns like phonenumber1, phonenumber2, etc. This is for two reasons:
If your columns go up to phonenumber3 and someone needs to add a fourth number, you have to add a column to the table.
For all the users with fewer than 3 phone numbers, there are empty columns on their rows.
Instead, you'd want to have a phonenumber table, where each row contains a phone number and a foreign key reference to which row in the user table it belongs to. No blank columns are needed, and each user can have as few or many phone numbers as necessary.
One side point to note about normalization: A fully normalized database is space efficient, but is not necessarily the most time efficient arrangement of data depending on use patterns.
Skipping around to multiple tables to look up all the pieces of info from their denormalized locations takes time. In high load situations (millions of rows per second flying around, thousands of concurrent clients, like say credit card transaction processing) where time is more valuable than storage space, appropriately denormalized tables can give better response times than fully normalized tables.
For more info on this, look for SQL books written by Ken Henderson.
I would say that normalization is like keeping notes to do things efficiently, so to speak:
If you had a note that said you had to
go shopping for ice cream without
normalization, you would then have
another note, saying you have to go
shopping for ice cream, just one in
each pocket.
Now, In real life, you would never do
this, so why do it in a database?
For the designing and implementing part, thats when you can move back to "the lingo" and keep it away from layman terms, but I suppose you could simplify. You would say what you needed to at first, and then when normalization comes into it, you say you'll make sure of the following:
There must be no repeating groups of information within a table
No table should contain data that is not functionally dependent on that tables primary key
For 3NF I like Bill Kent's take on it: Every non-key attribute must provide a fact about the key, the whole key, and nothing but the key.
I think it may be more impressive if you speak of denormalization as well, and the fact that you cannot always have the best structure AND be in normal forms.
Normalization is a set of rules that used to design tables that connected through relationships.
It helps in avoiding repetitive entries, reducing required storage space, preventing the need to restructure existing tables to accommodate new data, increasing speed of queries.
First Normal Form: Data should be broken up in the smallest units. Tables should not contain repetitive groups of columns. Each row is identified with one or more primary key.
For example, There is a column named 'Name' in a 'Custom' table, it should be broken to 'First Name' and 'Last Name'. Also, 'Custom' should have a column named 'CustiomID' to identify a particular custom.
Second Normal Form: Each non-key column should be directly related to the entire primary key.
For example, if a 'Custom' table has a column named 'City', the city should has a separate table with primary key and city name defined, in the 'Custom' table, replace the 'City' column with 'CityID' and make 'CityID' the foreign key in the tale.
Third normal form: Each non-key column should not depend on other non-key columns.
For example, In an order table, the column 'Total' is dependent on 'Unit price' and 'quantity', so the 'Total' column should be removed.
I teach normalization in my Access courses and break it down a few ways.
After discussing the precursors to storyboarding or planning out the database, I then delve into normalization. I explain the rules like this:
Each field should contain the smallest meaningful value:
I write a name field on the board and then place a first name and last name in it like Bill Lumbergh. We then query the students and ask them what we will have problems with, when the first name and last name are all in one field. I use my name as an example, which is Jim Richards. If the students do not lead me down the road, then I yank their hand and take them with me. :) I tell them that my name is a tough name for some, because I have what some people would consider 2 first names and some people call me Richard. If you were trying to search for my last name then it is going to be harder for a normal person (without wildcards), because my last name is buried at the end of the field. I also tell them that they will have problems with easily sorting the field by last name, because again my last name is buried at the end.
I then let them know that meaningful is based upon the audience who is going to be using the database as well. We, at our job will not need a separate field for apartment or suite number if we are storing people's addresses, but shipping companies like UPS or FEDEX might need it separated out to easily pull up the apartment or suite of where they need to go when they are on the road and running from delivery to delivery. So it is not meaningful to us, but it is definitely meaningful to them.
Avoiding Blanks:
I use an analogy to explain to them why they should avoid blanks. I tell them that Access and most databases do not store blanks like Excel does. Excel does not care if you have nothing typed out in the cell and will not increase the file size, but Access will reserve that space until that point in time that you will actually use the field. So even if it is blank, then it will still be using up space and explain to them that it also slows their searches down as well.
The analogy I use is empty shoe boxes in the closet. If you have shoe boxes in the closet and you are looking for a pair of shoes, you will need to open up and look in each of the boxes for a pair of shoes. If there are empty shoe boxes, then you are just wasting space in the closet and also wasting time when you need to look through them for that certain pair of shoes.
Avoiding redundancy in data:
I show them a table that has lots of repeated values for customer information and then tell them that we want to avoid duplicates, because I have sausage fingers and will mistype in values if I have to type in the same thing over and over again. This “fat-fingering” of data will lead to my queries not finding the correct data. We instead, will break the data out into a separate table and create a relationship using a primary and foreign key field. This way we are saving space because we are not typing the customer's name, address, etc multiple times and instead are just using the customer's ID number in a field for the customer. We then will discuss drop-down lists/combo boxes/lookup lists or whatever else Microsoft wants to name them later on. :) You as a user will not want to look up and type out the customer's number each time in that customer field, so we will setup a drop-down list that will give you a list of customer, where you can select their name and it will fill in the customer’s ID for you. This will be a 1-to-many relationship, whereas 1 customer will have many different orders.
Avoiding repeated groups of fields:
I demonstrate this when talking about many-to-many relationships. First, I draw 2 tables, 1 that will hold employee information and 1 that will hold project information. The tables are laid similar to this.
(Table1)
tblEmployees
* EmployeeID
First
Last
(Other Fields)….
Project1
Project2
Project3
Etc.
**********************************
(Table2)
tblProjects
* ProjectNum
ProjectName
StartDate
EndDate
…..
I explain to them that this would not be a good way of establishing a relationship between an employee and all of the projects that they work on. First, if we have a new employee, then they will not have any projects, so we will be wasting all of those fields, second if an employee has been here a long time then they might have worked on 300 projects, so we would have to include 300 project fields. Those people that are new and only have 1 project will have 299 wasted project fields. This design is also flawed because I will have to search in each of the project fields to find all of the people that have worked on a certain project, because that project number could be in any of the project fields.
I covered a fair amount of the basic concepts. Let me know if you have other questions or need help with clarfication/ breaking it down in plain English. The wiki page did not read as plain English and might be daunting for some.
I've read the wiki links on normalization many times but I have found a better overview of normalization from this article. It is a simple easy to understand explanation of normalization up to fourth normal form. Give it a read!
Preview:
What is Normalization?
Normalization is the process of
efficiently organizing data in a
database. There are two goals of the
normalization process: eliminating
redundant data (for example, storing
the same data in more than one table)
and ensuring data dependencies make
sense (only storing related data in a
table). Both of these are worthy goals
as they reduce the amount of space a
database consumes and ensure that data
is logically stored.
http://databases.about.com/od/specificproducts/a/normalization.htm
Database normalization is a formal process of designing your database to eliminate redundant data. The design consists of:
planning what information the database will store
outlining what information users will request from it
documenting the assumptions for review
Use a data-dictionary or some other metadata representation to verify the design.
The biggest problem with normalization is that you end up with multiple tables representing what is conceptually a single item, such as a user profile. Don't worry about normalizing data in table that will have records inserted but not updated, such as history logs or financial transactions.
References
When not to Normalize your SQL Database
Database Design Basics
+1 for the analogy of talking to your wife. I find talking to anyone without a tech mind needs some ease into this type of conversation.
but...
To add to this conversation, there is the other side of the coin (which can be important when in an interview).
When normalizing, you have to watch how the databases are indexed and how the queries are written.
When in a truly normalized database, I have found that in situations it's been easier to write queries that are slow because of bad join operations, bad indexing on the tables, and plain bad design on the tables themselves.
Bluntly, it's easier to write bad queries in high level normalized tables.
I think for every application there is a middle ground. At some point you want the ease of getting everything out a few tables, without having to join to a ton of tables to get one data set.