SELECT all items in common between two users on FOUR tables - mysql

I have now four tables:
item_to_list (to store the relations between list and item)
| item_to_list_id | list_id | item_id |
-----------------------------------------
item_tb
| item_id | item_name |
-----------------------
list_tb (is a list of items that belong to a unique user, a list can't be owned by two users)
| list_id | list_name | user_id |
---------------------------------
user_tb
| user_id | user_name |..etc..
-------------------------------
An item can belong to one or more list.
A list can belong to only one user and a user can have one or more lists...
Different lists can have same items in common (think a grocery list that you buy every week the same items)
basically imagine my website is an e-commerce, the list is an order, and i want to know how many items two users (which i know the name and id) have in common have bought in their entire experience in my website....
So, given the user_id = A and user_id = B how can I do a mysql query to select all the items the belong both to user A and user B?
note: I wrote a similar question but was about three tables not four. And i'm learnign a lot doing these progressive questions thanks!!!
UPDATE: my bad wrote a wrong table name, now is correct

Ill answer in pseudo code hope you understand,basicly idea is create a set of user A items and join it by item_id with set of user B items.
Obviously to generate user A and user B items you need same query with different where clause.
Select A.item_id from
(select user A items) as A
join (select user B items ) as B
on A.item_id=B.item_id
I hope you can create select user X items query

The first issue is getting all of the items a user has ever bought. This is quite easy using item_to_list:
SELECT item_id FROM item_to_list WHERE user_id = A;
However, keeping user_id inside both item_to_list and list_tb leads to lots of data duplication. (Google relational database normalisation for fun stuff on this). So I would remove the user_id field from item_to_list and rely on list_tb to store this information.
The query then becomes:
SELECT DISTINCT item_id
FROM item_to_list AS itl, list_tb AS ltb
WHERE ltb.user_id = A
AND ltb.list_id = itl.list_id;
This is assuming that your item_to_list.item_to_user_id is the same as the item_tb.list_tb
At this point you can generate a table containing a list of all of the items ever bought by a user. Now the problem becomes generating a table for user A and a table for user B and then comparing the results.
SELECT itl.item_id
FROM item_to_list AS aitl, list_tb AS altb
WHERE altb.user_id = A
AND altb.list_id = aitl.list_id;
AND aitl.item_id IN
SELECT DISTINCT item_id
FROM item_to_list AS bitl, list_tb AS bltb
WHERE bltb.user_id = B
AND bltb.list_id = bitl.list_id;
Note: This is thouroughly untested :) and there may well be improvements to be made with regards to efficiency.
EDIT: Completed answer

Related

Get a certain query result from two different mysql tables

Im having the following problem:
I try to implement an achievementsystem. I have two tables. Table 1 contains the achievement_id and achievement_info. Table 2 contains the link to the user, meaning achievement_id and player_id, so that you can tell which user has achieved certain things.
I'm trying to write a method that returns me all achievements, but additionally a flag that tells me if a certain user has achieved this row or not.
E.g.: getPlayerAchievements(playerid) --> returns a list of Achievements with id, info, and a bool flag whether the user has achieved it.
table 1:
achievement_id|achievement_info
1 |info1
2 |info2
3 |info3
table 2:
achievement_id|player_id;
1 |15
3 |15
the result I need by entering the player_id "15":
achievement_id|achievement_info|(bool)achieved
1 |info1 |true
2 |info2 |false
3 |info3 |true
I already have the achievement class so I just have to fill them with my data.
I could always use two seperate sql queries to achieve that, but I thought maybe there was a way to simplify it, since I use php to get my data and don't want two connections and queries in one php script.
You want to select all records from the achievemets table and show them. That's the easy part :-) For every record you want to show whether player 1234 has attained this achievement. You can do this with an EXISTS clause:
select
achievement_id,
achievement_info,
exists
(
select *
from players p
where p.player_id = 1234
and p.achievement_id = a.achievement_id
) as achieved
from achievements a;
Or even simpler with IN:
select
achievement_id,
achievement_info,
achievement_id in (select achievement_id from players where player_id = 1234) as achieved
from achievements;
You can use left join to get a complete list of achievements and the matching records from the user's achievements table:
select t1.achievement_id, t1.achievement_info, (t2.achievement_id is null) as achieved
from table1 t1
left join table2 t2 on t1.achievement_id=t2.achievement_id and t2.player_id=15

What are the ways to model multivalued M : N relationship?

I am working on the project that requires multivalued M : N relationship.
For eg.
There is a list of products in the Products table.
The user can purchase 1 or more products and are maintained in the table Orders. Along with this information, there is one more table that maintains analytical information.
This table should contain the hard coded data like. If the order contains product 1 and product 2 then does product 3 and product 4 also appeared in the user's order. These are basically hard coded rules
actual products | expected products
1,3 | 2,4
5,6,7,8 | 3,4
Now from these tables, I need to find information like if user's order number 1 had products like 1 and 3 then return 2 and 4.
I need suggestions as to how to describe this multivalued M to N relationship. If there are any other options other than RDBMS feel free to suggest. Thanks
You could create three additional tables:
ProductSet(productset_id) - Stores product sets headers(one row from your sample)
ProductSetPart(productset_id,product_id) - Stores required products to make set, in your case column actual product
ProductSetAdditional(productset_id,product_id) - stores expected products.
Having given Order we can detect which additional products should be added.
EDIT: sample added
Example query that return list of Sets that are fulfilling requirements:
SELECT psp.productset_id FROM
ProductSetPart psp LEFT JOIN
OrderLines ol
ON
psp.product_id=ol.product_id
GROUP BY
psp.productset_id
HAVING
-- trick - COUNT(*) will return count of all products required by aggregatet set
-- COUNT(psp.product_id) will count only not null products
-- (which will be null if they aren't in order line)
-- so if all product sets are in order line then we know,
-- that this set requirements are full filled
COUNT(*) = COUNT(psp.product_id)

Mysql table relations

I have read various topics regarding table relations and while i am building my database i am a bit confused on what should i do.
I have 3 type of registration on my site(artist, fan, companies). Each registered user gets a unique key and username and the appropriate type of user (ex. fan). I am trying to involve music genres to all types of registration but genres will also be added to uploaded music files. At the moment i am storing one music genre per track and user by an array list that is shown in a form. Then system is storing it to the appropriate field. But i want some users to have more than 1 genres stored.
Now what i have done is below:
Users table (total 14 columns)
ID | username | email | password | type | signup | lastlogin | etc.
Settings table (total 10 columns)
ID | username | description | banner | country | genres | avatar etc.
Music table
ID | username | artist | title | maingenre | othergenre | cover | fileurl
By having in mind performance and let's assume that thousand of thousand users is registering...
Should i just add all settings column in the users table or it's ok to keep as i have them now? Settings can be updated by user while users table is updated by the system.
Should i split the user table according to users type? For example Artist table, fan table etc. that will store the appropriate registration and settings? Type of user needs to be in a column as is important for some functions of the site.
Regarding music table i was thinking to making a table for each genre that will store the uploads according to the genre specified by the user. Is this a good way of storing tracks to database or not? So when i want to call tracks of disco music i just use the disco music table.
Any help will be much appreciated.
not quiet sure I understand everything completely how your table is correlated, or what exactly you want or plan to do, but here is one idea about how to store genres in your database. And to connect it with your Setting table
First create table Genres in which you will store all genres. That table could look like this
Table: Genres
ID | genres_name | description etc.
ID - will be primary key auto increment
genres_name - will hold the name of genre (blues, jazz, disco...)
description - this column i added just if you want to add something specific by every genre it's not necessary
Next step is to create table Settings_genres. This table will store relation between your Setting table and Genres table and will look like this
Table: Settings_genres
settings_id | genres_id
So data in this table will look like this (for the setting ID 1 which will have 3 different Genres)
settings_id | genres_id
------------------------
1 | 2
------------------------
1 | 4
------------------------
1 | 5
------------------------
settings_id and genres_id will be primary key pair which means that you wont be able to store two identical pair int this table (You can have only one relation between one settings column and one genre column)
That is something called Many to many relationship and I'm sure that you can easily find more about that if you google it just a little.
When you want to pull data off from database which will show all settings and all genres you can do it with query like this
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
ORDER BY ID
Here is SQL Fiddle to see how it's look like.
When you want to pull data from settings table where that table is connected with specific genre you do that like this
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
WHERE Genres.genres_name = 'Rock'
ORDER BY ID;
This can also be achieved by this query which may be a little faster but let's not go into detail...
SELECT Settings.*, Genres.genres_name, Genres.description
FROM Settings
INNER JOIN Settings_Genres
ON Settings.ID = Settings_Genres.settings_id
INNER JOIN Genres
ON Settings_Genres.genres_id = Genres.ID
AND Genres.genres_name = 'Rock'
ORDER BY ID;
Here is FIDDLE for that...
So basically I suggest you to learn a little bit about relation between tables especially many to many relationship. And on than you will see how to improve your data table design.
Hope I help a little.
GL!
i think the way your table is, is okay. you dont have to split the table based on the type of users you have. but i think what you could use is font end technologies to allow users preform activities you want them, which is restricting them to only what you want them to do, they by controlling flow of information within the system. i hope that helps.

database design for user subscriptions

So I'm new to databases in the scope of the subject and looking for some advice for what I am sure is fairly simple. first I'm using MySql as my db I currently have two tables one for storing user accounts and details :
TABLE user
id | username | password | email_address | user_devices | contact_method
and another for storing video content by producers which looks like:
TABLE series
id | series_title | still_broadcasting | last_updated |
I would like to implement a feature where Users can select series which they wish to be notified of when new releases are made available and also select how to be notified about these releases (email or push notification ) and how often to be notified (on arrival, hourly, daily, weekly ) I am wondering whats the best way to go about doing this?
I've thought of these ideas by myself but am looking for a second opinion/ better way altogether: (all ideas minus 4 involve storing how to notify user along with how often in user table)
adding a text column to user table called following and just having csv's for each series
adding multiple boolean column's to user table one for each series
adding text column to series table with csv's of user's Id numbers following series
creating an entirely new table for notifications though i don't really see the purpose of this as its very redundant
I then plan to just add cron jobs to my server to actually go about regulaurly sending notifications to user's
Thanks in advance for any help.
First of all, it might be worth giving some articles on basic database design a read. A quick google turned up this which covers identifying relationships
http://www.datanamic.com/support/lt-dez005-introduction-db-modeling.html
Your best bet is to use a linking table i.e.
CREATE TABLE userHasSeries (
userID INT,
seriesID INT
);
This can then be used in an INNER JOIN query to get the users choices. What you are doing here is an n:m link between 2 tables. An example inner join would be
SELECT
u.id AS userID,
u.username,
s.seriesID,
s.series_title,
s.still_broadcasting,
s.last_updated
FROM users AS u
INNER JOIN userHasSeries AS uhs
ON uhs.userID = u.id
INNER JOIN series AS s
ON s.id = uhs.seriesID
If users.user_devices is also a comma seperated list I would advise heavily that you adopt a similar n:m approach there also.
A partial answer which complements what has been written in other answers:
Don't keep a list of devices in the 'user_devices' field - break this out into a separate table. In fact, you'll need two tables: one to list the various devices, and one a join table which has two fields: user_id and device_id. This will enable you to track which user has which device, but also to provide a list of users per device.
If I were you I would add a third table as following:
TABLE user
id | username | password | email_address | user_devices | contact_method |notification_type
TABLE series
id | series_title | still_broadcasting | last_updated
TABLE followings
id | user_id | series_id
In notification_type I would put (on arrival, hourly, daily, or weekly), now in the followings tables I will store all the user's preferred series.
Doing this way makes easy to add, delete, update, or select all user's preferred series. All will be simple SQL queries. Also you avoid parsing comma separated strings.
for example, if you want to get all preferred series of an user:
SELECT * FROM followings AS f INNER JOIN series AS s ON f.series_id = s.id WHERE f.user_id = ?
if want to get all users that prefer a serie:
SELECT * FROM followings AS f INNER JOIN user AS u ON f.user_id = u.id WHERE f.series_id = ?

How do I store bookmarks for multiple users in a mySQL table(s)?

Suppose I have users
u1,u1,u3...
and bookmarks
b1,b2,b3....
How do I store the bookmarks most effectively.
Each user has his own set of bookmarks that load on to his page. Then, if the user wants to know who else has the same bookmarks, he can select that bookmark and see a list of users who also have it.
This is just a question about general design. If my understanding is correct so far this is a many to many relationship.
the common approach would be to use a third table (many to many relation) you may also store a timestamp in this table to determine when the bookmark was added by the user or a user specific comment for his bookmark.
Another great thing about this approach is that the bookmarks table doesn't store any user specific data or has duplicate entries (4 times the same bookmark cause 4 users added it)
+-------------------+
| bookmarks_users |
|-------------------|
| id (int, primary) |
| user_id (int) |
| bookmark_id (int) |
+-------------------+
Select all bookmarks of one user (just a basic query - you maybe should join the needed data ;) )
SELECT bookmark_id
FROM bookmarks_users
WHERE user_id = 123
select all users having a specific bookmark
SELECT user_id
FROM bookmarks_users
WHERE bookmark_id = 123
show users having the at least one common bookmark as the specified user (this query is very bad (performance wise) but it serves well as an example)
SELECT DISTINCT user_id
FROM bookmarks_users
WHERE bookmark_id IN(
SELECT bookmark_id
FROM bookmarks_users
WHERE user_id = 123
)
AND user_id != 123
hope this helps - tim