Database Design for user defined groups - sql

I'm trying to figure out the best way to design a database to support private user-defined groups. Pretty much identical to how Google Circles are. These are to be for JUST the user, much like circles are - that's why creating a user group design like I found here: https://stackoverflow.com/a/9805712/2580503 would be undesirable.
So far the only solution I can come up with is to have a table like this:
USER_ID | GROUP_ID | ARRAY(USER_ID)
Where the PKEY would actually be a compound key of (USER_ID, GROUP_ID). This way a user could have multiple groups.
Would greatly appreciate any feedback on this proposed solution and would love to hear if there is a better way to do it.
Thanks!
Edit: Just to clarify, GROUP_ID would not reference a separate table, it would just indicate the number group for that user. Also there would be a name etc. for the group as well - just wasn't necessary to include as part of the question.

This must involve at least three (3) tables if you want a normalized design. USERS, USER_GROUPS, and USER_GROUPS_MEMBERS. You are correct that the PK of USER_GROUPS would be a dyad (USER, GROUP). The PK of USER_GROUPS_MEMBERS would be a triad (USER, GROUP, USER).

What about?
Groups (GROUP_ID, USER_ID, GROUP_NAME)
Members (MEMBER_ID, GROUP_ID, USER_ID)
Although Groups might appear backwards, it actually lists the USER_ID that owns a GROUP_ID while Members gives the MEMBER_ID to which could be associated rows that have to do with this USER_ID in the given GROUP_ID.

How about
**Users**
id, name
**Groups**
id, name
**User_Groups**
id, user_id, group_id
**Group_users**
id, user_group_id, user_id
I have separated groups and user_groups assuming that there could be possibilities that you wish to have a few default groups for every user, If this is not the case, you can move the group_name directly to the user_groups and ignore the groups table

Related

SQL database – track user's activity

I have a simple database structure, with models and relations:
Models:
User
Group
Activity
Relations:
User/Group –> User belongs to Group, Group has many Users
User/Activity –> User has many Activities, Acitivty belongs to User
Group/Activity –> Activity belongs to Group, Group has many Activities
My problem – I want to be able to track number of activities performed in the group by the user within a given period (probably per week, but possibly per day) and I do not know what's the best/ most performant way to achieve this.
Theoretically, I can just perform a query that would count those activities based on the created_at date attribute but I assume this is not the most performant way (am I wrong?)
Does anyone know how to properly structure something like this?
As per the relationship provided by you, your activity table has a foreign key reference to user_id and group_id, you can get the count of a user activity under a group in a day.
SELECT a.user_id, a.group_id, count(a.user_id)
FROM activity a
WHERE a.user_id = '123'
AND a.group_id = '1'
AND a.activity_time >= '2019-08-31'
AND a.activity_time < '2019-08-31' + INTERVAL 1 DAY
Create a composite key on user_id, group_id, activity_time for faster retrieval if table size increases in the future.
Please note this query is in MySQL.

SQL schema Site Leader Board

So I am trying to set up a site which has challenges and then want to convert that to leader boards for each challenge, and then an all time leaderboard.
So I have a challenges table that looks like this:
Challenge ID Challenge Name Challenge Date Sport Prize Pool
Then I need a way so each challenge has its own leader board of say 50 people.
linked by the challenge ID where that will = Leaderboard ID
I have a leader board of 50 people for that challenge that will look something like this:
Challenge ID User Place Prize Won
My question is 2 things:
How can I make a table auto create when a new challenge is added to the challenges table?
How can I get an A site wide leader board for every challenge so it will show the following:
Rank USER Prize Money Won(total every challenge placed)
and then base rank order by how much money won..
I know this is a lot of questions all wrapped in one, schema design and logic.
Any insights greatly appreciated
A better approach than one table per challenge is one table for all of them. That way you can compute grand totals and individual challenge rankings all with the same table. You'd also want to not record the place directly but compute it on the fly with the appropriate window function depending on how you want to handle ties (rank(), dense_rank(), and row_number() will have different results in those cases); that way you don't have to keep adjusting it as you add new records.
A table something like (You didn't specify a SQL database, so I'm going to assume Sqlite. Adjust as needed.):
CREATE TABLE challenge_scores(user_id INTEGER REFERENCES users(id),
challenge_id INTEGER REFERENCES challenges(id),
prize_amount NUMERIC,
PRIMARY KEY(user_id, challenge_id));
will let you do things like
SELECT *
FROM (SELECT user_id,
sum(prize_amount) AS total,
rank() OVER (ORDER BY sum(prize_amount) DESC) AS place
FROM challenge_scores
GROUP BY user_id)
WHERE place <= 50
ORDER BY place;
for the global leaderboard, or the similar:
SELECT *
FROM (SELECT user_id,
prize_amount,
rank() OVER (ORDER BY prize_amount DESC) AS place
FROM challenge_scores
WHERE challenge_id = :some_challenge_id
GROUP BY user_id)
WHERE place <= 50
ORDER BY place;
for a specific challenge's.

How do I store multiple pieces of individual data within a single column?

I have a table which stores information about a group. Information such as the group name, group id, the number of members... However, I would also like to store the codes of each individual member within a group.
The way I initially tried to store the member codes was by creating individual columns. So I would have columns named MemberCode1, MemberCode2, MemberCode3. But the problem is a group can have 100 members which would mean 100 columns would be required to store the member codes individually.
My question: is there a way I can store an x amount of member codes within a single column within my table, or do simply make y columns and limit the number of users in a group to y?
This is a great example of when to use a separate table. You can create a second table, called "group", and store your group data there (group ID, group name).
I'm assuming that a member can belong to multiple groups, and a group can have many members.
If so, you can then have a table in the middle of those two tables to capture the members in each group. You can call this table "memberships" or "group_member" or something.
It would contain the Primary Keys of each of the entries in the member and group tables.
So your table structure could be:
MEMBER (member ID... other fields)
GROUP (group ID, group_name)
MEMBERSHIP (member_id, group_id)
Finally, to find the number of members in each group:
SELECT group_id, COUNT(*)
FROM membership
GROUP BY group_id;
Or to get group data with that:
SELECT m.group_id, g.group_name, COUNT(m.*)
FROM membership m
INNER JOIN group g ON m.group_id = g.group_id
GROUP BY m.group_id;
Generally speaking it is not a good idea to store multiple pieces of information in the same field. It violates normal form and, more practically, it is hard to maintain.
The best solution to your issue would be to create a separate table for the members (Member) and include a foreign key, perhaps groupID or group_id, referring to the group table (Group).
In the event that a "member" can belong to more than one "group", you would create a third table, MemberGroup, with a composite primary key (member_id, group_id) made up of foreign keys referring to Member and Group respectively.

How to select the total unique number in SQL

Say if I want to select total number of unique group which type is 'A' in User table, how can I write the query?
*multiple users may belongs to a same group, there is a field called 'group', another field called 'user type'
Searched on google but didn't find the right one, any answers are welcomed!
Your question isn't terribly clear, but it sounds like you might be after:
SELECT COUNT(DISTINCT group)
FROM table
WHERE type = 'A'
Without the full design, I have to imply a lot of things.
In this exemple, I suppose that :
Users have an Id, a Name and that GroupId is the Foreign Key.
Group has an Id as Primary Key.
This would do it. In that case.
SELECT COUNT(DISTINCT id), username FROM Users
JOIN Groups ON Users.GroupId = Groups.Id
WHERE Users.UserType = 'A'
GROUP BY username

sql query with "with and in clause"

i have a table which store user name, hobby and city .hobby field contain different hobby joined using "," operator eg swimming, basket, cricket. I want to search user name who match at least one hobby according to my search criteria.
You should not have multiple attributes in one column. That's one of the number one rules of 3nf database design. Now you have to figure out ways to parse this data. This issue only gets worse and worse each and every day. Seperate the hobbies as multiple rows in your database.
I agree with #JonH that there shouldn't be more than one piece of information in a column. It stops the row being truly atomic.
But you are where you are, and you can use the LIKE clause to return rows that match a substring within a column.
Something like:
select hobbycolumn from hobbytable where hobbycolumn like '%swimming%'
for example
To do this properly you need to restructure your tables if possible. For what you are looking for a possible way would be to have 3 tables. I'm not sure who the city belongs to, so I put it with the user.
1 for user with the following cols:
id
name
city
A table for for hobbies:
id
name
And a user_hobbies join table that allows each user to have multiple hobbies, and each hobby to have multiple users:
id
user_id (foreign key)
hobby_id (foreign key)
Then searching for a user with a certain hobby is:
SELECT user.id, user.name FROM user
INNER JOIN 'user_hobbies' on user_hobbies.user_id=user.id
INNER JOIN 'hobbies' on hobbies.id = user_hobbies.hobby_id
WHERE hobbies.name LIKE "query";