MySQL database structure for infinite items per user - php

I have a MySQL database with a growing number of users and each user has a list of items they want and of items they have - and each user has a specific ID
The current database was created some time ago and it currently has each users with a specific row in a WANT or HAVE table with 50 columns per row with the user id as the primary key and each item WANT or HAVE has a specific id number.
this currently limits the addition of 50 items per user and greatly complicates searches and other functions with the databases
When redoing the database - would it be viable to instead simply create a 2 column WANT and HAVE table with each row having the user ID and the Item ID. That way there is no 'theoretical' limit to items per user.
Each time a member loads the profile page - a list of their want and have items will then be compiled using a simple SELECT WHERE ID = ##### statement from the have or want table
Furthermore i would need to make comparisons of user to user item lists, most common items, user with most items, complete user searches for items that one user wants and the other user has... - blah blah
The amount of users will range from 5000 - 20000
and each user averages about 15 - 20 items
will this be a viable MySQL structure or do i have to rethink my strategy?
Thanks alot for your help!

This will certainly be a viable structure in mysql. It can handle very large amounts of data. When you build it though, make sure that you put proper indexes on the user/item IDs so that the queries will return nice and quick.
This is called a one to many relationship in database terms.
Table1 holds:
userName | ID
Table2 holds:
userID | ItemID
You simply put as many rows into the second table as you want.
In your case, I would probably structure the tables as this:
users
id | userName | otherFieldsAsNeeded
items
userID | itemID | needWantID
This way, you can either have a simple lookup for needWantID - for example 1 for Need, 2 for Want. But later down the track, you can add 3 for wishlist for example.
Edit: just make sure that you aren't storing your item information in table items just store the user relationship to the item. Have all the item information in a table (itemDetails for example) which holds your descriptions, prices and whatever else you want.

I would recommend 2 tables, a Wants table and a Have table. Each table would have a user_id and product_id. I think this is the most normalized and gives you "unlimited" items per user.
Or, you could have one table with a user_id, product_id, and type ('WANT' or 'HAVE'). I would probably go with option 1.

As you mentioned in your question, yes, it would make much more sense to have a separate tables for WANTs and HAVEs. These tables could have an Id column which would relate the row to the user, and a column that actually dictates what the WANT or HAVE item is. This method would allow for much more room to expand.
It should be noted that if you have a lot of of these rows, you may need to increase the capacity of your server in order to maintain quick queries. If you have millions of rows, they will have a great deal of strain on the server (depending on your setup).

What you're theorizing is a very legitimate database structure. For a many to many relationship (which is what you want), the only way I've seen this done is to, like you say, have a relationships table with user_id and item_it as the columns. You could expand on it, but that's the basic idea.
This design is much more flexible and allows for the infinite items per user that you want.
In order to handle wants and have, you could create two tables or you could just use one and have a third column which would hold just one byte, indicating whether the user/item match is a want or a need. Depending on the specifics of your projects, either would be a viable option.
So, what you would end up with is at least the following tables:
Table: users
Cols:
user_id
any other user info
Table: relationships
Cols:
user_id
item_id
type (1 byte/boolean)
Table: items
Cols:
item_id
any other item info
Hope that helps!

Related

How to maintain multiple unique data for a single user for N number of users in a database

I have to develop a shopping site where I need to maintain data like "Add to Cart" and "Previous Orders" for a single user, for N number of users in a database. E.g. if there is a user named "Mark" and I need to store his 5 items previously added to cart and 4 items as previous order and there are say 100 users like him whose data I need to store, How should I solve this problem? I am using PHP and MySQL.
Each user is assigned a unique ID (you can exploit MySQL's AUTO_INCREMENT for this purpose when creating the table of users.
Then, for items the users select, you create another table including the User_ID (from the previous table), the Item_ID (from a similar table for items), the status (that would indicate the status of the item for the specific user) and an optional field that would contain the order number when the user places the order (for one or more items).
In summary, and since you are actually asking for help in designing your database, I would suggest you sit with a pencil and a piece of paper and make lists of informational elements that you need, marking the one-to-one relation between them (when applicable). This will (approximately) suggest what needs to be arrange in a record of a table (e.g. all the stable details of a user would normally go together into a single table).

mySQL Database - Storing Multi-Criteria Ratings

I've been doing a lot of searching and reading about rating systems, but couldn't find a solution to what I'm trying to achieve...
I have a website where users, once logged in, can submit a product. Now I want other users to be able to rate those products according to 3 different criteria. I'm using php and mySQL databases to store all of the information which is working great, I'm just not sure how to incorporate the ratings now.
At the moment I have a PRODUCTS database, which holds various tables according to their category. Here's an example of a table:
TOASTERS
---
ID (auto-incrementing)
Brand
Set
Number
Name
Edition
Image (stores the location of the image the user uploads)
Any user can then rate that row of the table out of 10 for 3 criteria (Quality, Price, Aesthetic). The user average of each criteria is displayed on each product page but I would like to store each of the user's individual ratings so that I can show a short history of their ratings on their profile page. Or have a live feed of the latest user ratings on the homepage.
What I'm trying to do is quite a lot like awwwwards.com. (See bottom-right of page to see the livefeed I'm talking about)
Thanks in advance!
I think you should use single PRODUCTS table or at least create PRODUCTS table and emulate inheritance between it and category tables.
Having a table for each category can give some advantages if each category has some specific properties, but it can lead to neccesity of writing separate code to work with each table. Alternatively you can use two tables to store all custom properties 'vertically': PROPERTIES(propertyID,PropertyName), PROPVALUES(productID,propertyID,PropertyValue).
If you choose to have multiple tables and emulate inheritance, it can be achieved like this:
PRODUCTS
---
ID (auto-incrementing)
Brand
Set
Number
Name
Edition
Image
VoteCount <+
SumQuality +-updated by trigger
SumPrice |
SumAesthetic <+
TOASTERS
---
productID (PK and FK to PRODUCTS)
(toaster specific fields go here, if any)
Than you will be able to create table VOTES, referencing table PRODUCTS
VOTES
---
productID (FK to PRODUCTS)
userID (FK to USERS)
Quality
Price
Aesthetic
VoteDateTime
If it is true that overall product rating is queried much more often than voting history, as an optimization you can add fields VoteCount, AvgQuality, AvgPrice, AvgAesthetic to PRODUCTS table, as srdjans already supposed. You can update this extra fields by trigger on table VOTES or manually in PHP code.
Create separate table for storing user individual ratings (primary key, user id, product id and ratings). Create additional fields in "products" to store averages. Every time some user rates some product, you insert record in "ratings" table, then calculate averages again for given product, and update rows in products. Doing this you will have easy access to ratings, and also, you can analyse user individual ratings.
Ps - You may also wish to store how many users rated some product.

Database modeling: best aproach for multiple categories for multiple elements

Let's say I have 10 books, each book has assigned some categories (ex. :php, programming, cooking, cookies etc).
After storing this data in a DB I want to search the books that match some categories, and also output the matched categories for each pair of books.
What would be the best approach for a fast and easy to code search:
1) Make a column with all categories for each book, the book rows would be unique (categs separated by comma in each row ) -> denormalisation from 1NF
2) Make a column with only 1 category in each row and multiple rows per book
I think it is easier for other queries if I store the categories 1 by 1 (method 2), but harder for that specific type of search. Is this correct?
I am using PHP and MySQL.
PPS : I know multi relational design, I prefer not joining every time the tables. I'm using different connection for some tables but that's not the problem. I'm asking what's the best approach for a db design for this type of search: a user type cooking, cookies, potatoes and I want to output pairs of books that have 1,2 more or all matched categs. I'm looking for a fast query, or php matching technique for this thing... Tell me your pint of view. Hope I'm understood
Use method 2 -- multiple rows per book, storing one category per row. It's the only way to make searching for a given category easy.
This design avoids repeating groups within a column, so it's good for First Normal Form.
But it's not just an academic exercise, it's a practical design that is good for all sorts of things. See my answer to Is storing a comma separated list in a database column really that bad?
What you want to do is have one table for books, one table for categories, and one table for connecting books and categories. Something like this:
books
book_id | title | etc
categories
category_id | title | etc
book_categories
book_id | category_id
This is called a many-to-many relationship. You should probably google it to learn more.
This relationship is a Many-To-Many (a book can have multiple categories and a category can be used in several books).
Then we have the following:
Got it?
=]
I would recommend approach number 2. This is because approach 1 requires a full text search of the category column.
You may have some success by splitting it up into two tables: One table has one line per book and a unique id (call the table books), and the other has one line per book per category and references the book id from the first table (call the table bookcategories). Then if you only need book data you use table books, where if you need categories you join both tables.

How should I design the database structure for this problem?

I am rebuilding the background system of a site with a lot of traffic.
This is the core of the application and the way I build this part of the database is critical for a big chunk of code and upcoming work. The system described below will have to run millions of times each day. I would appreciate any input on the issue.
The background is that a user can add what he or she has been eating during the day.
Simplified, the process is more or less this:
The user arrives to the site and the site lists his/her choices for the day (if entered before as the steps below describes).
The user can add a meal (consisting of 1 to unlimited different items of food and their quantity). The meal is added through a search field and is organized in different types (like 'Breakfast', 'Lunch').
During the meal building process a list of the most commonly used food items (primarily by this user, but secondly also by all users) will be shown for quick selection.
The meals will be stored in a FoodLog table that consists of something like this: id, user_id, date, type, food_data.
What I currently have is a huge database with food items from which the search will be performed. The food items are stored with information on both the common name (like "pork cutlets") and on producer (like "coca cola"), along with other detailed information needed.
Question summary:
My problem is that I do not know the best way to store the data for it to be easily accessible in the way I need it and without the database going out of hand.
Consider 1 million users adding 1 to 7 meals each day. To store each food item for each meal, each day and each user would potentially create (1*avg_num_meals*avg_num_food_items) million rows each day.
Storing the data in some compressed way (like the food_data is an json_encoded string), would lessen the amount of rows significally, but at the same time making it hard to create the 'most used food items'-list and other statistics on the fly.
Should the table be split into several tables? If this is the case, how would they interact?
The site is currently hosted on a mid-range CDN and is using a LAMP (Linux, Apache, MySQL, PHP) backbone.
Roughly, you want a fully normalized data structure for this. You want to have one table for Users, one table for Meals (one entry per meal, with a reference to User; you probably also want to have a time / date of the meal in this table), and a table for MealItems, which is simply an association table between Meal and the Food Items table.
So when a User comes in and creates an account, you make an entry in the Users table. When a user reports a Meal they've eaten, you create a record in the Meals table, and a record in the MealItems table for every item they reported.
This structure makes it straightforward to have a variable number of items with every meal, without wasting a lot of space. You can determine the representation of items in meals with a relatively simple query, as well as determining just what the total set of items any one user has consumed in any given timespan.
This normalized table structure will support a VERY large number of records and support a large number of queries against the database.
First,
Storing the data in some compressed way (like the food_data is an
json_encoded string)
is not a recommended idea. This will cause you countless headaches in the future as new requirements are added.
You should definitely have a few tables here.
Users
id, etc
Food Items
id, name, description, etc
Meals
id, user_id, category, etc
Meal Items
id, food_item_id, meal_id
The Meal Items would tie the Meals to the Food Items using ids. The Meals would be tied to Users using ids. This makes it simple to use joins in order to get detailed lists of data- totals, averages, etc. If the fields are properly indexed, this should be a great model to support a large number of records.
In addition to what's been said:
be judicious in your use of indexes. Properly applying these to your database could significantly speed up read access to your tables.
Consider using language-specific features to minimize space. You mention that you're using mysql; consider using ENUM when appropriate (food types, meal types) to minimize database size and to simplify management.
I would split up your meal table into two tables, one table stores a single row for each meal, the second table stores one row for each food item used in a meal, with a foreign key reference to the meal it was used in.
After that, just make sure you have indices on any table columns used in joins or WHERE clauses.

Selecting rows from MySQL

I'm trying to create a web index. Every advertiser in my database will be able to appear on a few categories, so I've added a categorys column, and in that column I'll store the categories separated by "," so it will look like:
1,3,5
The problem is that I have no idea how I'm supposed to select all of the advertisers in a certain category, like: mysql_query("SELECT * FROM advertisers WHERE category = ??");
If categories is another database table, you shouldn't use a plain-text field like that. Create a "pivot table" for the purpose, something like advertisers_categories that links the two tables together. With setup, you could do a query like:
SELECT A.* FROM advertisers AS A
JOIN advertisers_categories AS AC ON AC.advertiser_id = A.id
WHERE AC.category_id = 12;
The schema of advertisers_categories would look something like this:
# advertisers_categories
# --> id INT
# --> advertiser_id INT
# --> category_id INT
You should design your database in another way. Take a look at Atomicity.
Short: You should not store your value in the form of 1,3,5.
I won't give you an answer because if you starting you use it this way now, you going to run into much more severe problems later. No offense :)
It's not possible having comma-separated values to do this strictly in an SQL query. You could return every row and have a PHP script which goes through each row, using explode($row,',') and then if(in_array($exploded_row,'CATEGORY')) to check for the existence of the category.
The more common solution is to restructure your database. You're thinking too two-dimensionally. You're looking for the Many to Many Data Model
advertisers
-----------
id
name
etc.
categories
----------
id
name
etc.
ad_cat
------
advertiser_id
category_id
So ad_cat will have at least one (usually more) entry per advertiser and at least one (usually more) entry per category, and every entry in ad_cat will link one advertiser to one category.
The SQL query then involves grabbing every line from ad_cat with the desired category_id(s) and searching for an advertiser whose id is in the resulting query's output.
Your implementation as-is will make it difficult and taxing on your server's resources to do what you want.
I'd recommend creating a table that relates advertisers to categories and then querying on that table given a category id value to obtain the advertisers that are in that category.
That is a very wrong way to define categories, because your array of values cannot be normalized.
Instead, define another table called CATEGORIES, and use a JOIN-table to match CATEGORIES with ADVERTIZERS.
Only then you will be able to properly select it.
Hope this helps!

Categories