I'm creating a system that has Groups. These can be thought of like Facebook Groups. Users can create new groups. Currently I have the following types of groups:
City Group - Groups based on a certain city. For example "London Buy and Sell Group"
School Group - Groups based on schools. For example "London University Study Group"
Interest Group - Groups that are not tied to a place. For example "Over 50's Knitting Group"
In the future more group types will be added. Each group can have different types of options, but all groups have the same basic data:
An ID
A creator ID
A name
An option description
I'm struggling on putting together a database design for this. My initial thought was to create different tables for the different groups.
For example have a single table called group. This table has an id, creator id, name, description, member count, timestamps.
Then have other tables to represent the other groups, and link them to group. So I have a city_group table that contains and id, group_id, city_id. And the same for the other group types.
The only problem I have with this is interest_group doesn't have any extra data that a normal group. But for the purpose of being able to query only Interest Groups I thought it might make sense to create an interest_group table. It would only have the following columns: id, group_id, timestamps ... which seems a bit wasteful to have a table just for this purpose.
Here's a diagram to make things easier:
Are there any issues with my solution, or any better ways to solve this design problem?
I've got an idea, which is a workaround basically: have another table like: group_type in which you have id(the PK) and then you have tablename (the full table name of the type).
Then, you should have a FK from your Group table linking to this group_type table.
id tablename
--------------------
1 School Group
2 Interest Group
After all this is done, you could build your queries based on the values from this table, as an example:
JOIN (SELECT tablename FROM group_type WHERE id=group.group_type_id) ON ..
Related
I've got three tables :
conversations (contains private conversations)
conversations-members - structure : convId | userId (contains all ids of members participating to a conversation so multiple people can talk together. One user participating to a conversation equals one row)
users (users table, classic)
What I am trying to do is :
Users have friends. So, the user browsing my application can open a conversation from his friends' ids.
So, first, I want to look up in conversations-members if there is an existing conversation ONLY between user's Id and his friend's Id and then, pick up the conversation id in conversations table that conversations-members gave me.
Is it possible to do this in one request? If I have to do two requests, I don't even know how to build the first one (find the rows that contain user's Id and friend's Id that have conversation Id in common).
My first idea was to make a single conversations table which would also contain member's Id in the form of a string like "55,105,85,22" and then parse it to get an array, but I think the way I want to do it gives me more options and could be simpler if I manage to handle the SQL requests I need.
To find conversations involving just two particular users, you can do something like this.
SELECT convId, COUNT(*)
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
The HAVING line filters out any conversation with any users besides the two you want.
You can use that as a subquery:
SELECT whatever
FROM conversations
WHERE convId IN (
SELECT convId
FROM `conversations-members`
WHERE userId IN (FIRST_USER, SECOND_USER)
GROUP BY convId
HAVING COUNT(*) = 2
)
If you wanted conversations with your two users and any other users, you could change the subquery to HAVING COUNT(*) >= 2.
Pro tip: SQL thinks hyphens - mean subtraction. Avoid them in column and table names.
It's not recommended to use a column with multiple values.
When you have a relation 1-N, normally you need to use a table with two keys, for example, ConvID - UserID. Your index are created by these two columns, for don't allow to generate a duplicate (an UserID two times on the same ConvID, for example).
But, in response to your question, yes, it's possible to do that search. You need to search for an entry on your SQL table where two different IDs are on the same line. You can do it with LIKE clause, but it's not a good option (I think it's the worst possible WHERE clause).
The syntax will be as follows:
SELECT convId FROM conversations-members WHERE userId LIKE '%id1%' AND userId LIKE '%id2%'
For some additional recommendation, if your conversations are always a pair of people, use two new fields on your "conversations" table. If your conversation can have from one to N members, use the "conversations-members" as suggested at the start of the answer.
I have a list of items, and items can be owned either by an individual person or a group of people.
So far I have 3 tables like:
People:
personID (primarykey)
personName
personAddress
...
Groups:
groupID (primarykey)
groupNumber
personID
Items:
itemID (primarykey)
itemName
itemDescription
itemWeight
...
How would you go about assigning ownership of items to an individual, or a specific group?
I did have 2 fields on the item, owner_personID and owner_groupID, but seems kinda janky, and have to manually manage which field to use at a time.
I also tried two linking tables, PeopleOwnerships, and GroupOwnerships, that store a list of items owned. With fields like, personID/groupID and itemID. But, seems even more jankier and can get messy. Have to be extra careful so that both individuals and groups don't own the same item at the same time.
I have also thought about putting all individuals in their own individual group, and just using the Groups table to refer to everything. But that also seems kind of weird.
I would also need to record the transfer of items, between people and groups, when ownership changes. I've thought about having a date field in the linking tables, and using the highest date to determine who owns what.
You could treat a person as a group of 1 person, and only allow groups to own items.
Something better may be to have a class called "Owner" which only has the ID of the item and its ID (the id of the owner), and get both classes Person and Group extending it.
About the transfers, I wouldn't rely that business logic on the database.
I have some booking data from a pair of views in MySQL. They match columns perfectly, and the main difference is a booking code that is placed in one of these rows.
The context is as follows: this is for calculating numbers for a sports camp. People are booked in, but can do extra activities.
View 1: All specialist bookings (say: a football class).
View 2: A general group.
Due to the old software, the booking process results in many people booking for the general group and then are upgraded to the old class. This is further complicated by some things elsewhere in the business.
To be clear - View 1 actually contains some (but are not exclusively all) people from within View 2. There's an intersection of the two groups. Obviously people can't be in two groups at once (there's only one of them!).
Finding all people who are in View 2 is of course easy... as is View 1. BUT, I need to produce a report which is basically:
"View 1" overwriting "View 2"... or put another way:
"View 1" [sort of] UNION "View 2"
However: I'm not sure the best way of doing this as there are added complications:
Each row is as approximately (with other stuff omitted) as follows:
User ID Timeslot Activity
1 A Football
1 A General
2 A General
3 A Football
As you can see, these rows all concern timeslot A:
- User 2 does general activities.
- User 3 does football.
- User 1 does football AND general.
AS these items are non unique, the above is a UNION (distinct), as there are no truly distinct rows.
The output I need is as follows:
User ID Timeslot Activity
1 A Football
2 A General
3 A Football
Here, Football has taken "precedence" over "general", and thus I get the picture of where people are at any time.
This UNION has a distinct clause on a number of fields, but ignores others.
So: does anyone know how to do what amounts to:
"add two tables together and overwrite one of them if it's the same timeslot"
Or something like a:
"selective distinct on UNION DISTINCT".
Cheers
Rick
Try this:
SELECT *
FROM
(SELECT *,
IF(Activity='General',1,0) AS order_column
FROM `Table1`
ORDER BY order_column) AS tmp
GROUP BY UserId
This will add an order_column to your original table that as value 1 if the Activity value is general; Doing this we can select this temporary table ordering by this column (ascending order) and all record with general activity comes after all others. After that we can simply select the result of this temporary table grouping by user id. The group by clouse without any aggregate function takes the first record that match.
EDIT:
If you don't to use group by without aggregate function this is an 'ugly' alternative:
SELECT UserId,
Timeslot,
SUBSTRING(MAX(CASE Activity WHEN "General" THEN "00General" WHEN "Football" THEN "01Football" ELSE Activity END) , 3)
FROM `Table1`
GROUP BY UserId,
Timeslot LIMIT 0 ,
30
Here we need to define each possible value for Activity.
I am developing web application where I have to implement 'Likes' system as facebook has. Application will have a few categories of products that customer can 'like'. So I have started to create database, but I stuck on one obstacle. As I understand there are two ways of doing this:
First. Create one database table with fields of "id, user_id, item_category, item_id". When user click 'like' button information will be saved in this table with various categories of products (item_category).
Second. Create several tables for certain categories of item. For instance, "tbl_item_category_1, tbl_item_category_2, tbl_item_category_3" with fields of "user_id, item_id".
Would be great to get more insight about best practices of this kind database structures. Which works faster? and more logical/practical? I will use only several categories of items.
I would go with the first version with a table structure similar to this:
User Table: PK id
id
username
Category Table: PK id
id
categoryname
Like Table: PK both user_id and catgory_id
user_id
category_id
Here is a SQL Fiddle with demo of table structure with two sample queries to give the Total Likes by user and Total Likes by category
The second one - creating multiple tables is a terrible idea. If you have 50-100 categories trying to query those tables would be horrible. It would become completely unmanageable.
If you have multiple tables trying to get a the total likes would be:
Select count(*)
from category_1
JOIN category_2
ON userid = userid
join category_3
ON userid = userid
join .....
Use one table, no question.
The first method is the correct one. Never make multiple tables for item categories, it makes maintaining your code a nightmare, and makes queries ugly.
In fact, the general rule is that anything that is dynamic (i.e. it changes) should not be stored as a set of static objects (e.g. tables). If you think you might add a new type of 'something' later on, then you need a 'something' types table.
For example, imagine trying to get a count of how many items a user has liked. With the first method, you can just do SELECT COUNT(*) FROM likes WHERE user_id = 123, but in the second method you'd need to do a JOIN or UNION, which is bad for performance and bad for maintainability.
The first method is the correct one. Because you dont know how many categories you will be having and it is very difficult to get the data.
I have the following tables:
users (id, first_name, last_name)
category (id, name)
rank(id, user_id, rank)
Each user can belong to several categories. And all users are in the rank table and have a value between 0.0 and 1.0, where 0 is the lowest rank and 1 is the highest. I’d like to setup additional tables to create the following webpage:
A visitor to the page (identified by either one of the recorded ids in the user table, or a numeric representation of their ip address) chooses a category and is presented with two randomly chosen users from the users table such that:
1) the visiting user_id has not seen this pairing in a period of 24 hours
2) the two users belong to the chosen category
3) the two users are within 1 rank value of each other. Let me explain that last criteria - if the ranks were sorted, the two chosen users would have adjacent ranks.
This is a hard one and I can’t for the life of me figure it out how to do this effeciently
I truly appreciate any help on this front.
Thanks
You just need two more tables and the rest go in your website logic.
user_category(user_id, category_id)
user_pairing(first_user_id, second_user_id, last_seen)
The first table is to represent a ManyToMany relationship between the users and the category, and the second one is for the users pairing.
I agree with #Yasel, i want to add that you properly want another table
candidate(first_user_id, second_user_id);
this table is used to pre-calculate the candidates for each user, this candidate table is prepopulated every hour/day, so when each first_user_id, second_user_id is assigned, this pair is removed from candidate table and moved into user_pairing table. so each time you only need to query candidate table which should be efficient.