How my system works is that a user may be assigned to several things.
A column is called group and they may be assigned to several groups, e.g 1,5,8 etc... Is there a way to store them in the same column?
Don't store relations in single column. Normalize your data and introduce new table which will hold references for user group relations like
Table user_groups
user_id group_id
1 1
1 5
1 8
Also have a look at Is storing a delimited list in a database column really that bad?
Related
This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 3 years ago.
I want to store user information in Mysql for my Python Program.
One of the things I want to store is username history (static list)
Another is which groups they are a member of (dynamic list)
I am new to storing data in Mysql so am trying to figure out the best structure to achieve this. It seems like I could create 1 table for each user and have the name hsitory as a column, but everything I read tells me this would be wasteful and inefficient.
so for example....
Table = users
user_ID | current_username | username_history | groups_joined | groups_banned
========|==================|==================|===============|==============
01567 | Dave |Michael,Geoff, |group1,group2, |group4,group5
| |Bob,Nigel,Colin |group2 |group5,group7
========|==================|==================|===============|==============
01568 | Fred |Martin,Simon, |group3,group4, |group4,group3
| |Leo,Nick,Arthur |group6 |group2,group12
My first thought was to do something like the above and when I have a list to store like username_history I would convert the list to a string with comma seperated values and store it in a LONGTEXT field as shown. Then to add usernames as the user changes them I could use concat to add to the string.
This would work I guess, but it feels ugly and im sure there must be a better way. Also I think this would be very inefficient if I needed to search for a name in username history, find out all users that were called Fred for example.
My next thought was to create an entire table per user and populate the username_column with one name per field.
But googling around I found similar questions from database noobs all with replies saying this would create thousands of tables and be very inefficient.
ok so now im looking at relational tables (correct terminology??)....
table = username_history
user_id | usernames
========|==========
01567 | but I still need a list here....
I'm sure this is a very common problem for beginners, but I just can't seem to get my head around how the structure for my usage would look.
Thanks to anyone taking the time to help and advise :)
Create a table username_history with:
create table username_history (
id int not null auto_increment,
user_id varchar(10),
username varchar(30),
created_at datetime default CURRENT_TIMESTAMP,
primary key (`id`),
index (user_id)
);
You can then get the usernames in list format using GROUP_CONCAT-function.
You really need three tables:
Groups
Users
UserGroups
The first contains a list of the groups which user may join. It could be as simple as a unique ID and Group Name.
The second table is similar to your table in the question. For this purpose, the only columns we are concerned with are the unique ID for each user and their user name.
The third table has two columns: user_id and group_id. When a user joins a group, a row is inserted into this table with the unique IDs of the user and group.
You could also have a column with a timestamp of when the row was added, and a column for the user's status if needed.
The timestamp column would let you know when the user joined the group.
The status column could indicate if the user is banned from the group, or if the user left the group.
I am trying to store an array/set of unique elements in a MySQL cell.It would be something like following:
user_id liked_friends_id
1 [3,6,4]
2 [1,2,4]
Here the liked_friends_id is an array/set which stores all unique ids.
Is there any way to implement it?
How can I implement pushing to the array/set while enforcing unique array elements.
Don't store CSV data in your MySQL table. Unlike a database like Postgres, MySQL does not have an array type. Instead, store each user-friend relationship in a single record:
user_id liked_friends_id
1 3
1 6
1 4
2 1
2 2
2 4
Proceeding this way leaves your database relatively normalized. If you want to ensure that you do not store duplicate user-friend relationships, then add a unique constraint on these two columns:
ALTER TABLE friends ADD UNIQUE unq_idx (user_id, liked_friends_id)
Perhaps you could re-think your schema to check if you really want to store liked_friends_id in the same table. You could split it into another table with each liked friends id as a foreign constraint to the primary key user_id. That way, you can enforce uniqueness via MySQL itself.
Another way of doing it would be to serialize the array and storing it as varchar if you're using PHP/Java/.. for handling the queries. In that case, you will have to enforce uniqueness via the programming language.
I have the following situation. The entries in a table come from a list of users for a particular id. That way id 7 has 3 rows for, say, 2, 6, 7 (these three are unique ids for a user data table). To clarify the table looks like this.
ID USERID KEYID
Where KeyID is auto_inc and is the table's primary key.
These entries come from a multiple select field. So a user might want to delete user 2 and add user 8. So the function that needs to update the table gets the array 8,6,7 for id 7. The quick way I found to do the syncronization is simply to delete every entry in the table for ID 7 and add 3 new entries of the from (ID,USERID) (7,8) (7,6) (7,7).
However I don't know if this is how it's supposed to be. Is there a better way? Also this methods drives the keyid up really fast (for every modification in the table, basically). Is that a problem? I'm an newbie with these things, so please be patient.
Well, you can delete rows selectively like this:
DELETE FROM my_table WHERE user1 = 7 AND user2 NOT IN (8,6,7)
where (8,6,7) is list of pairs (7,6),(7,8),(7,7) , that should be preserved, kept, not deleted, and it will delete all other pairs (7,?)
Your next question may be "Then how to add pairs while not making duplicities?"
You will first need to teach your table what duplicity is. By adding unique key on pair of fields (user1,user2). That would forbid duplicities. Now, when inserting new rows that may be already there, use "INSERT IGNORE" to ignore such exception and simply, continue on.
I am creating a site that is sort of ecommerce-ish. I want to give my users a perfect search ability using specific attributes that differ from product to product. I plan to create 1 products table storing the basic information that is shared among products i.e Name, Description, Price and a few others. Then I plan to create several "details" table say categories_computers with columns Processor, HDD, RAM, etc and another table say table_shoes with columns MATERIAL, SIZE, GENDER, etc.
I am new to Mysql but not to the concept of Databases. I don't think I will have a problem storing this data to each table. My issue comes about from reads. It won't be hard to query a product id but I think it would be extremely wasteful to query all details tables to get the details of the product since 1 product can only have 1 details.
So my question is how can I store a reference to a table in a column so that a product has say ID, Name, Description, Price, Details_Table_ID or something similar to save on queries. Do tables have unique ids in Mysql? Or how does the Stackoverflow community suggest I go about this? Thanks.
EDIT
Silly me, I have just remembered that every table name is uniques so I can just use that, so my question changes to how I can write a query that contains one cell in a table A to be used as a reference to a Table name.
Don't use separate details tables for each category, use a generic details table that can store any attribute. Its columns would be:
Product_ID INT (FK to Products)
Attribute VARCHAR
Value VARCHAR
The unique key of this table would be (Product_ID, Attribute).
So if Product_ID = 1 is a computer, you would have rows like:
1 Processor Xeon
1 RAM 4GB
1 HDD 1TB
And if Product_ID = 2 is shoes:
2 Material Leather
2 Size 6
2 Gender F
If you're worried about the space used for all those attribute strings, you can add a level of indirection to reduce it. Create another table Attributes that contains all the attribute names. Then use AttributeID in the Details table. This will slow down some queries because you'll need to do an additional join, but could save lots of space
Think about just having a single ProductDetails table like this:
ProductDetailID (PK)
ProductID (foreign key to your Products table)
DetailType
DetailValue
this way you do not have to create new columns every time you add a new product detail type. and you'll have many ProductDetail rows for each productid, which is fine and will query ok. Just be sure to put an index on ProductDetails.ProductID !
Since this is an application so you must be generating the queries. So lets generate it in 2 steps. I assume you can add a column product_type_id in your Product table that will tell you which child table to user. Next create another table Product_type which contains columns product_type_id and query. This query can be used as the base query for creating the final query e.g.
Product_type_id | Query
1 | SELECT COMPUTERS.* FROM COMPUTERS JOIN PRODUCT ON COMPUTERS.PRODUCT_ID = PRODUCT.PRODUCT_ID
2 | SELECT SHOES.* FROM SHOES JOIN PRODUCT ON COMPUTERS.PRODUCT_ID = PRODUCT.PRODUCT_ID
Based on the product_id entered by the user lookup this table to build the base query. Next append your where clause to the query returned.
I want to store user followers and following member list. Now in order to this, i am thinking to create two columns namely FOLLOWING and FOLLOWER in USER table to store comma separated values of following and followers respectively.
USER TABLE FIELDS:
userid
firstname
lastname
date_of_birth
following //in this we store multiple following_id as comma separated
follower //in this we store multiple follower_id as comma separated
Another way is to create tables namely FOLLOWER and FOLLOWING to store user's followers and following members id in it.
USER TABLE FIELDS:
userid
firstname
lastname
date_of_birth
and
FOLLOWER TABLE FIELDS:
userid
follower_id (also is an user)
and
FOLLOWING TABLE FIELDS:
userid
following_id (also is an user)
Since i am learning database designing, i don't have enough knowledge. So, here i am not getting proper idea of which way is proper? I have searched that using comma separated way is not a good idea but at the same time is it a good way to have multiple tables with NF ? Is there any drawback of using JOINS? Or is there any other effective way to deal with this scenario?
You need just two tables - one to list your users, and one to list who each user is following.
The resulting tables would be like your second proposal, except that the followers table is unnecessary because all of the required data is already in the following table - it's just keyed from the second column instead of the first. That table will need an index on both columns.
The following table whould have one row per relationship per direction. If the users are following each other, you would put two entries in the following table.
CREATE TABLE following (
userid ... NOT NULL,
following_id ... NOT NULL
);
CREATE INDEX idx_user ON following(userid);
CREATE INDEX idx_following on following(following_id);
CREATE UNIQUE INDEX idx_both ON following (userid, following_id); // prevents duplicates
To find the IDs that a particular user is following:
SELECT following_id FROM following WHERE userid = ?
or to find that user's followers:
SELECT userid FROM following WHERE following_id = ?
Use appropriate JOIN clauses if required to expand those queries to return the users' names.
None of the above. One row per follower. Normalize your data and using it will be easy. Make it an abstract mess like you're proposing and you're life will get tougher and tougher as your application grows.