I'm developing a sports court booking system and I need to generate a "booking table" that shows the columns in the table header as courts and the rows as time slots for bookings.
E.g.,
___________________________________
| | | |
| Court 1 | Court 2 | Court 3 |
|___________|___________|___________|
| | | |
| 10.00 am | 10.00 am | 10.00 am |
|___________|___________|___________|
| | | |
| 11.00 am | 11.00 am | 11.00 am |
|___________|___________|___________|
Requirements:
A club can have any number of courts
A club can have any time increment for bookings (e.g., 1 hour as shown above, 30 minutes, 40 minutes, etc)
Each cell in the table represents a "booking"
I want to make sure I do this right from the start so I have a few questions:
What entities would you create to achieve this
How would you go about generating this booking table
How would you link a cell in the above table to a booking
Thanks in advance.
Well, I think this is kind of standard?
First, you need a club entity. Each club can have n courts:
Club 1:n Court
Then there is a booking table, which is 1:n to a court:
Court 1:n Booking
I don't know if your second requirement means that one club has one time increment (in which case this is one variable on the club entity) or if it can has many (than there would be a TimeIncrement entity.
Generating the table can be a bit tricky. Thinking about it for a few minutes I got like 5-6 solutions which might work. You could use special objects which you can ask for the booking for a specific court and time and which search a Collection. Our you could build up an array where you have one key for every time and if there is no booking it's null. Have one array for each court, than do 2 nested for loops and read every value from the arrays. You could build up queries which rearrange the data so you can use them directly. Or maybe you can ask the court object itself for the booking on a specific date and time.
But I guess that is what the developer is for... Find out what works best for the given requirements and implement it.
What entities would you create to achieve this
Off the top of my head it looks like you'll need 3: Club, Court, Booking
How would you go about generating this booking table
The table should probably consist of id, court_id, start_time, end_time
How would you link a cell in the above table to a booking
As mentioned above, start/end times are columns in the bookings table.
I would just query the data from the database and turn it into json and pass it into the website. The frontend then can build the table with javascript.
For that I would create a custom entity BookingTable that returns data on request directly as an array which then can be easily turned into json with json_encode.
You can then concentrate on the more detailed pages that show the single booking for which you will automatically create the entities you need (if you didn't already to formulate the DQL for the custom entity for the table).
Related
How would things like customer reviews be stored in a database? I cant imagine there would be rows for each item and columns for each review as one product may have 2 reviews and another may have 100+ - id presume they were stored in a separate file for reviews but then surely not one file per item! I dont know enough about storing data to be able to figure this one out by myself!
A similar situation is something like an online calendar - there is all the information about each appointment (time, duration, location, etc) and there can be many of these on each day, every day, for all users! A logical way would be to have a table for each user with all their appointments in, but at the same time that seems illogical because if you have 1000+ users, thats alot of tables!
Basically Id like to know what the common/best practice way is of storing this 'big dynamic data'.
Customer reviews can easily be stored by using two tables in one-to-many relationship.
Suppose you have a table containing products/articles/whatever worth reviewing. Each of them has an unique ID and other attributes.
Table "products"
+-------------------------------------+
| id | name | attribute1 | attribute2 |
+-------------------------------------+
Then you make another table, with its name indicating what it's about. It should contain at least an unique ID and a column for the IDs from the other table. Let's say it will also have an email of the user who submitted the review and (obviously) the review text itself:
Table "products_reviews"
+--------------------------------------------+
| id | product_id | user_email | review_text |
+--------------------------------------------+
So far, so good. Let's assume you're selling apples.
Table "products"
+-------------------------------+
| 1 | 'Apple' | 'green' | '30$' |
+-------------------------------+
Then, two customers come, each one buys one apple worth 30$ and likes it, so they both leave a review.
Table "products_reviews"
+-------------------------------------------------------------------------------+
| 1 | 2 | alice#mail.com | 'I really like these green apples, they are awesome' |
| 2 | 2 | bob#mail.com | 'These apples rock!' |
+-------------------------------------------------------------------------------+
So now all you have to do is to fetch all the reviews for your apples and be happy about how much your customers like them:
SELECT *
FROM products_reviews
INNER JOIN products ON products_reviews.product_id = products.id
WHERE products.name = 'Apple';
You can now display them under the shopping page for apples (just don't mention they cost 30$).
The same principle applies for things like an online calendar. You have one table with users, and many tables with other stuff - appointments, meetings, etc. which relate to that user.
Keep in mind, however, that things like meetings are better displayed in a many-to-many table, since they are shared by many people (usually). Here's a link that visualizes it very good, and here's a question here on SO with sample code for PHP. Go ahead and test it for yourself.
Cheers :)
What's the best way to store site statistics for specific users? Basically I want to store how many times a user has done a specific task. The data will be coming from a potentially large table and will be referenced frequently, so I want to avoid COUNT() and store them in their own table.
Method A
Have a table with the following fields, then have a row for each user to store the count for each field:
User_id | posted_comments | comment_replies | post_upvotes | post_downvotes
50 12 7 23 54
Method B
Have one table storing the actions, and another storing the count for that action:
Table 1:
Id | Action
1 | posted_comments
2 | comment_replies
3 | post_upvotes
4 | post_downvotes
Table 2
User_id | Action | Count
50 | 1 | 12
50 | 2 | 7
50 | 3 | 23
50 | 4 | 54
I can't see me having more than 25-30 actions in total, but I'm not sure if that is too many to store horizontally as in method A.
I think you answered your question. If you don't know what the actions are, then store each action in a separate row. That would be the second option.
Be sure that you have the proper indexes on the table. One possibility is (user_id, action, count). With this index, it will be fast to denormalize the table at the user level.
If you have a well-defined problem and won't need to be adding/removing/renaming columns in a table, then the first version is also feasible. Otherwise, just stick with inserting rows. The queries may seem a little bit more complicated, but the application is more flexible.
Seems like a typical BI question to me. The real question is not how many "actions" you have in your dimension, but how often they change.
Table A is denormalized and quick and easy to read: with a "SELECT" you get your information in the proper format.
Table B is normalized and easier to maintain It is highly recommended if your list of actions difficult to defined in advance, and is a must if it is dynamic.
To pass back and forth from Table A to Table B is known as pivot operations, for which you find standard tools, but which are never easy to code manually. So do not jump too quickly to the conclusion that Table B is better just because every body tells so since Codd in 1970.
I suggest you to ask yourself the question of how often will your COUNT(*) table(s) will be read. If you can live with the statistics of yesterday, then compute BOTH tables every night.
I am developing a personal finance tracker (for fun!) and I have a table of categories. Each category is an entry in the table and at the end of the month they are all duplicated with their relevant balances reset to the start of the month reading for the new month.
Among others, these categories can be of type 'savings' and so have a running total. If I want to retrieve a category or update it then I used the category_id field and this works fine for the current working month but linking months together is breaking my brain. For the savings categories I want to show how the running_total has increased over the previous six months but in my current DB design, categories don't "know" about their previous months as they are created new at the start of each month.
The only way I could currently retrieve the last 6 months of a savings running_total is to search by the category name but this is potentially unreliable.
I have considered adding a field to the table which is "previous_month_category_id" which would work as a way to link the categories together but would be expensive to implement as it would require 6 MSQL operations each time grabbing the "previous_month_category_id" from the result and then re running the query.
If MYSQL can do some kind of recursion then maybe this could work but I feel like there is a more obvious answer staring me in the face.
I'm using Codeigniter and MYSQL but not scared of vanilla PHP if required.
Help on how to do this would be great.
UPDATE 1:
Below is a sample from what the savings category might look like mixed in amongst other categories. At the end of each month the entry is duplicated with the same category_name, type, buget, year, and users_id but the category_id auto increments, the month updates to the new month number and the running total is the previous running_total + the budget. How would I do one database query to retrieve these without using the category_name? As this could change is the user decided to caller it "Bigger TV" at the end of July
+-------------+--------------+------+--------+---------------+------+-------+----------+
| category_id |category_name | type | budget | running_total | year | month | users_id |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 44 | Big TV | sav | 20 | 240 | 2012 | 8 | 77 |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 32 | Big TV | sav | 20 | 220 | 2012 | 7 | 77 |
+-------------+--------------+------+--------+---------------+------+-------+----------+
| 24 | Big TV | sav | 20 | 200 | 2012 | 6 | 77 |
UPDATE 2:
I'm not sure I'm explaining myself very well So I'll put some more detail around how the app works and see if that helps.
I have tables called "categories", "transactions" and "users". A category can be one of three types, 1: Cash, 2: Regular Payment, 3: Savings. Think of cash and regular payment types as buckets, at the start of each month each bucket is full and the aim is to take money out of it and make sure there is still a bit left at the end of the month (or at least not negative).
This is fine on a month by month basis and works very well (for me, I have used this system for 2 years now I think). The trip up comes with Savings as they are linked month by month and are more like a big bucket that is added to each month (with a set increment called budget) until it overspills and is then drained (like Big TV would be when you buy it), or taken from a little bit here and there and the aim is to build up an emergency fund (like "When my car breaks down" type thing).
When the relevant information is displayed for each category only the current month is shown for cash and regular as that is all that is important, for the savings however the current amount is also shown but it would be nice to show a small history graph of how it had built up (or depleted) over time. To do this I need some way of searching for the previous end of month states of these categories so that the graph can be plotted but currently I can't work out how to link them all by anything other than the category_name.
I have tried to implement a bit of DB normalisation but this is the first schema I've implemented having known about normalisation so I've probably missed some aspects of it and possibly avoided any over normalisation where it didn't feel right.
Below are my tables:
categories
+-------------+--------------+------+--------+---------------+------+-------+----------+
| category_id |category_name | type | budget | running_total | year | month | users_id |
+-------------+--------------+------+--------+---------------+------+-------+----------+
transactions
+----------------+--------------+--------+------+----------+------------------------+
| transaction_id | description | amount | date | users_id | categories_category_id |
+----------------+--------------+--------+------+----------+------+-------+---------+
they are joined on categories_category_id which is a foreign key
I have always worked off the premise that each category needs an new entry for each month but it seems from the comments and answers below that I would be better off having just one category entry regardless of month and then just calculating everything on the fly?
Although, the budgets can be changed by the user and so for record keeping I'm not sure if this would work also the "deposits" never really happen it is just the category being duplicated at the end of the month so I guess that would need to dealt with.....
The aim of this app has always been to decouple financial tracking from the physical transaction that occur in a bank account and provide a layer over someones finances thus allowing the user to avoid hard to explain transactions etc and just focus on over all cash position. There is no concept of an "income" in this system, or a bank account.
It seems to me like your database design could use some work. I'm still not completely familiar with what you're really trying to do, but my initial thoughts would be to store each transaction as a single row in a table, and then query that table in different ways to generate different types of reports on it. Something like this:
transactions:
+----+---------+--------+---------------+-----------+-------------+
| id | user_id | amount | running_total | datestamp | category_id |
+----+---------+--------+---------------+-----------+-------------+
categories:
+----+------+------+
| id | name | type |
+----+------+------+
Don't increment the categories based on time. Add an entry to the categories table when you actually have a new category. If a transaction could possibly belong to multiple categories, then use a third (relational) table that relates transactions (based on transaction ID) to categories (based on category ID).
When you have a deposit, the amount field will be positive and for withdrawals, it will be negative. You can get your current running total by doing something like:
SELECT running_total FROM transactions
WHERE id = (SELECT MAX(id) FROM transactions WHERE user_id = '$userID');
You can find your total difference for a particular month by doing this:
SELECT SUM(amount) FROM transactions WHERE DATE('%c', datestamp) = '$monthNumber';
You can find the total spending for a particular category by doing this:
SELECT SUM(t.amount) FROM transactions t
INNER JOIN categories c ON t.category_id = c.id WHERE c.name = 'Big TV';
There are plenty of other possibilities, but the purpose here is just to demonstrate a possibly better way to store your data.
i am currently planning out my next project which is a text based mmorpg game. I am currently trying to design certain parts of the database and have hit a bit of a problem that i have never had before. One part of the game allows the player to buy a car and add addons to it. I was going to have a different table altogether to manage the addons for the car, but a user could have up to 100 addons for a single car, which would require over 100 fields, of course i am not happy with this many fields in one table as it could become difficult to manage, is there any other way to split them up into multiple table?
Thanks
Why does each addon have to be a separate column? Couldn't you have a many-to-many join table that would link car to addon?
Car
ID | Owner
1 | Jacob
2 | Mary
Addon
ID | Name | Price
1 | Flame decal | $10
2 | CD Changer | $150
Car_Addon
Car_ID | Addon_Id
1 | 1
1 | 2
2 | 2
This indicates that Jacob's car has a flame decal and a cd changer, while Mary's car only has a cd changer.
Advantages of this approach:
You can use foreign key constraints to ensure that no invalid records can be created
It's easy to query in either direction -- which addons does this car have or which cars have a given addon
The meaning of the relation is clear -- you're not relying on decoding serialized data within a single field
You can store data about the association between car and addon -- the car_addon table can have a column for when the addon was added to that car, how it was paid for, whether it was part of a discount package, etc.
You have a many-to-many relationship between cars and addons. You need an intermediary junction table to resolve that relationship.
No.
Split them into multiple tables. If you have 100+ fields in a table, 99.9% of the time you haven't normalized your design enough. A sure sign of a badly structured database is a lot of sparsely populated fields.
Why are you hesitant to split it?
You should have a table for Cars (ID, Name) for example, one table for ADDON (ID, Name) too and another table to link these talbes called CAR_ADDON (idCar, idADDON).
That would be the best approach
Take a look at the items table below, as you can see this table is not normalized. Name should in a separate table to normalize it.
mysql> select * from items;
+---------+--------+-----------+------+
| item_id | cat_id | name | cost |
+---------+--------+-----------+------+
| 1 | 102 | Mushroom | 5.00 |
| 2 | 2 | Mushroom | 5.40 |
| 3 | 173 | Pepperoni | 4.00 |
| 4 | 109 | Chips | 1.00 |
| 5 | 35 | Chips | 1.00 |
+---------+--------+-----------+------+
This table is not normalize because on the backend Admin site, staff simply select a category and type in the item name to add data quickly. It is very quick. There are hundreds of same item name but the cost is not always the same.
If I do normalize this table to something like this:
mysql> select * from items;
+---------+--------+--------------+------+
| item_id | cat_id | item_name_id | cost |
+---------+--------+--------------+------+
| 1 | 102 | 1 | 5.00 |
| 2 | 2 | 1 | 5.40 |
| 3 | 173 | 2 | 4.00 |
| 4 | 109 | 3 | 1.00 |
| 5 | 35 | 3 | 1.00 |
+---------+--------+--------------+------+
mysql> select * from item_name;
+--------------+-----------+
| item_name_id | name |
+--------------+-----------+
| 1 | Mushroom |
| 2 | Pepperoni |
| 3 | Chips |
+--------------+-----------+
Now how can I add item (data) on the admin backend (data entry point of view) because this table has been normalized? I don't want like a dropdown to select item name - there will be thousands of different item name - it will take a lot of of time to find the item name and then type in the cost.
There need to be a way to add item/data quick as possible. What is the solution to this? I have developed backend in PHP.
Also what is the solution for editing the item name? Staff might rename the item name completely for example: Fish Kebab to Chicken Kebab and that will effect all the categories without realising it. There will be some spelling mistake that may need correcting like F1sh Kebab which should be Fish Kebab (This is useful when the tables are normalized and I will see item name updated every categories).
I don't want like a dropdown to select item name - there will be thousands of different item name - it will take a lot of of time to find the item name and then type in the cost.
There are options for selecting existing items other than drop down boxes. You could use autocompletion, and only accept known values. I just want to be clear there are UI friendly ways to achieve your goals.
As for whether to do so or not, that is up to you. If the product names are varied slightly, is that a problem? Can small data integrity issues like this be corrected with batch jobs or similar if they are a problem?
Decide what your data should look like first, based on the design of your system. Worry about the best way to structure a UI after you've made that decision. Like I said, there are usable ways to design UI regardless of your data structuring.
I think you are good to go with your current design, for you name is the product name and not the category name, you probably want to avoid cases where renaming a single product would rename too many of them at once.
Normalization is a good thing but you have to measure it against your specific needs and in this case I really would not add an extra table item_name as you shown above.
just my two cents :)
What are the dependencies supposed to be represented by your table? What are the keys? Based on what you've said I don't see how your second design is any more normalized that your first.
Presumably the determinants of "name" in the first design are the same as the determinants of "item_name_id" in the second? If so then moving name to another table won't make any difference to the normal forms satisified by your items table.
User interface design has nothing to do with database design. You cannot let the UI drive the database design and expect sensible results.
You need to validate the data and check for existence prior to adding it to see if it's a new value.
$value = $_POST['userSubmittedValue']
//make sure you sanitize the variable (never trust user input)
$query = SELECT item_name_id
FROM item_name
WHERE name='$value';
$result = mysql_query($query);
$row = mysql_fetch_row($result);
if(!empty($row))
{
//add the record with the id from $row['item_name_id'] to items table
}
else
{
//this will be a new value so run queries to add the new value to both items and item_name tables
}
There need to be a way to add item/data quick as possible. What is the
solution to this? I have developed backend in PHP.
User interface issues and database structure are separate issues. For a given database structure, there are usually several user-friendly ways to present and change the data. Data integrity comes from the database. The user interface just needs to know where to find unique values. The programmer decides how to use those unique values. You might use a drop-down list, pop up a search form, use autocomplete, compare what the user types to the elements in an array, or query the database to see whether the value already exists.
From your description, it sounds like you had a very quick way to add data in the first place: "staff simply select a category and type in the item name to add data quickly". (Replacing "mushroom" with '1' doesn't have anything to do with normalization.)
Also what is the solution for editing the item name? Staff might
rename the item name completely for example: Fish Kebab to Chicken
Kebab and that will effect all the categories without realising it.
You've allowed the wrong person to edit item names. Seriously.
This kind of issue arises in every database application. Allow only someone trained and trustworthy to make these kinds of changes. (See your dbms docs for GRANT and REVOKE. Also take a look at ON UPDATE RESTRICT.)
In our production database at work, I can insert new states (for the United States), and I can change existing state names to whatever I want. But if I changed "Alabama" to "Kyrgyzstan", I'd get fired. Because I'm supposed to know better than to do stuff like that.
But even though I'm the administrator, I can't edit a San Francisco address and change its ZIP code to '71601'. The database "knows" that '71601' isn't a valid ZIP code for San Francisco. Maybe you can add a table or two to your database, too. I can't tell from your description whether something like that would help you.
On systems where I'm not the administrator, I'd expect to have no permissions to insert rows into the table of states. In other tables, I might have permission to insert rows, but not to update or delete them.
There will be some spelling mistake that may need correcting like F1sh
Kebab which should be Fish Kebab
The lesson is the same. Some people should be allowed to update items.name, and some people should not. Revoke permissions, restrict cascading updates, increase data integrity using more tables, or increase training.