How to design one user "likes" another? - mysql

I would imagine this is kind of like friends, but doesn't need to be two-way. So like this:
from_user to_user
-------------------
John Mary
Mary John
John Susan
Mary Dave
I was curious if there were any issues with doing it as shown below.
user1 user2 mutuality
--------------------------------
John Mary 2
John Susan 1
Mary Dave 1
The second seems like it would have more complicated queries and checks, but wouldn't it end up saving space? For example, if Susan ends up falling for John one day, you'd check user2 for Susan and user1 for John. If it exists, update the mutuality to 2. If not, insert a new row [Susan, John, 1]. Something like that?
What's the best way of doing this?

I would choose the first method. You will not end up saving enough space with the second method to be an issue. Since each choice (like) is an action, having a separate record to store that action matches the work flow. John likes Mary, add a record. Mary likes John, add another record. Mary no longer likes John, delete that single record. This is easier to maintain (for me). I find keeping things in smaller granular parts keeps it simple.
In terms of design, I would recommend that instead of using string names in the likes table, you should have a users table with each user have a primary key id (unique). This way, users can modify names without breaking the relationships.
The table data would look like this:
Users table
id Name
1 John
2 Mary
3 Susan
4 Dave
Likes table
From To
1 2
2 1
1 3
2 4
The tables would have the following structure
users
-----
id (integer autoincrement)
name (varchar)
likes
-----
id (integer autoincrement)
fromid (integer)
toid (integer)
The sql would look like this to produce the list
SELECT f.name as fromName, t.name as toName
FROM (likes JOIN users AS f ON likes.fromid = f.id)
JOIN users AS t ON likes.toid = t.id;

The second method allows the following anomaly:
user1 user2 mutuality
--------------------------------
John Mary 2
Mary John 1
Do John and Mary like each other mutually (as implied by the first row), or just Mary likes John uni-directionally (as implied by the second row)?
The first method doesn't suffer from such inconsistencies, and data integrity is generally more important than saving some space.
BTW, if you introduce an integer surrogate key and reference it from the junction table (instead of the actual names), the junction table will become much leaner, negating much of the space advantage of the second method.

Related

Mysql delete 5th element of a table

I have a table:
ID Friend
John Rita
John Jack
Jack Rita
Rita John
John Peter
John Owen
John Eric
John Louis
I want to write a query where I delete all ID's after the 5th element. In other words I want to keep for each ID maximum 5 friends.
I have tried with a group by or something like that, but I don't now exactly how I have to do the delete.
The result must be:
ID Friend
John Rita
John Jack
Jack Rita
Rita John
John Peter
John Owen
John Eric
Short answer, no. The reason is that RDBMS's do not do not guarantee the order of entries.
If you need to be certain of the rows you are deleting, then you will need to know the order. I would recommend starting by adding an auto incrementing primary key column. From that, you will at least be certain of the order of entries. Eg:
ALTER TABLE table_name ADD column_name INT AUTO_INCREMENT PRIMARY KEY;
Note: This will only guarantee the order of future entries, not previously inserted entries.

MySQL query get column value similar to given

Sorry if my question seems unclear, I'll try to explain.
I have a column in a row, for example /1/3/5/8/42/239/, let's say I would like to find a similar one where there is as many corresponding "ids" as possible.
Example:
| My Column |
#1 | /1/3/7/2/4/ |
#2 | /1/5/7/2/4/ |
#3 | /1/3/6/8/4/ |
Now, by running the query on #1 I would like to get row #2 as it's the most similar. Is there any way to do it or it's just my fantasy? Thanks for your time.
EDIT:
As suggested I'm expanding my question. This column represents favourite artist of an user from a music site. I'm searching them like thisMyColumn LIKE '%/ID/%' and remove by replacing /ID/ with /
Since you did not provice really much info about your data I have to fill the gaps with my guesses.
So you have a users table
users table
-----------
id
name
other_stuff
And you like to store which artists are favorites of a user. So you must have an artists table
artists table
-------------
id
name
other_stuff
And to relate you can add another table called favorites
favorites table
---------------
user_id
artist_id
In that table you add a record for every artist that a user likes.
Example data
users
id | name
1 | tom
2 | john
artists
id | name
1 | michael jackson
2 | madonna
3 | deep purple
favorites
user_id | artist_id
1 | 1
1 | 3
2 | 2
To select the favorites of user tom for instance you can do
select a.name
from artists a
join favorites f on f.artist_id = a.id
join users u on f.user_id = u.id
where u.name = 'tom'
And if you add proper indexing to your table then this is really fast!
Problem is you're storing this in a really, really awkward way.
I'm guessing you have to deal with an arbitrary number of values. You have two options:
Store the multiple ID's in a blob object in JSON format. While MySQL doesn't have JSON functions built in, there are user defined functions that will extract values for you, etc.
See: http://blog.ulf-wendel.de/2013/mysql-5-7-sql-functions-for-json-udf/
Alternatively, switch to PostGres
Add as many columns to your table as the maximum number of ID's you expect to have. So if /1/3/7/2/4/8/ is the longest entry, have 6 columns in your table. Reason this is bad: you'll have sparse columns that'll unnecessarily slow your tables.
I'm sure you could write some horrific regex to accomplish the task, but I caution on using complex regex's on enormous tables.

What's the best db structure to represent the relationships within a family if the base-object is a Person

I have a large DB full or people called PERSONS.
I want to store their relationship to one another in a separate table.
I am thinking something like this:
FAMILY
PERSON_IDS: 123, 345, 678
RELATIONS: self, brother, daughter
Using a 'self' flag to indicate the person referring the relation-ship from. So, in the example 123 would be a man, 345 is his brother and 678 is a woman, his daughter. I.e, the gender information could be derived from the relationship.
Is this the right way to go about it?
Or would it be more efficient to have only 2 PERSONS per row and one relationship-type
PERSON_IDS: 123, 456
RELATION: brother
-
PERSON_IDS: 123, 678
RELATION: daughter
Or is it better to have a male/female field per person and the relation-ship table only specify eg "sibling".
Or should I have a table with PARENTS only and store the gender with the PERSON?
TABLE PARENTS:
PARENT_ID: 123
CHILD_ID: 678
The aim is to display related people when a Person is displayed. The design should be optimized for performance & speed rather than ease of administration.
Thanks for your help.
It's a many-to-many relationship: each person can have many relatives and also be a relative of many people. Assuming your Person table has a primary key named Id, you could have a table, which holds the relationship between two people as well as the kind of relationship they have. It would be useful to also have a table with all the possible kinds of relationships, something like this:
Relationship(Id, Kind)
Family(Person_Id, Relative_Id, Relationship_Id)
So if you have the following data for Person and Relationship tables:
Person: Relationship:
Id | Name Id | Kind
1 | John 1 | Father
2 | Mike 2 | Sister
3 | Susan
and the following for Family table:
P_Id | Rel_Id | Relation_Id
1 | 2 | 1
1 | 3 | 1
2 | 3 | 2
Family table is saying that John is the father of Mike and Susan, and that Mike's sister is Susan. I think this could be a good approach, but of course, as almost everything in databases design, this solution is arguable.

finding duplicates in MySQL with a null field when some SHOULD have a null field

Need some help from a MySQL expert here. I have a terrible database to work with and I'm trying to fix the structure a bit but this one has me baffled. The table initially had an id, name and 4 sell columns. I converted that to an id, name and single sell column as basically a pivot table. That was fine, next issue was to get rid of duplicates since not every entry had 4 sell entries.
So after the first operation I ended up with something like this:
id name sellid
1 bob 111
1 bob
1 bob
2 mary 112
2 mary 113
2 mary 114
2 mary 115
3 fred
3 fred
3 fred
3 fred
So by doing group by I managed to get it to the point where it looks like this:
id name sellid
1 bob 111
1 bob
2 mary 112
2 mary 113
2 mary 114
2 mary 115
3 fred
Now here is where I hit a wall. Fred is fine, he is supposed to have an entry but no sellid, Mary is also fine she has all 4 sellids full. Bob is the issue. How do I remove the empty sellid for him without affecting Fred?
I'd say what I tried but I am just at a complete loss here so I really haven't tried anything yet.
You are looking for an outer join between your names and other data:
SELECT * FROM
(SELECT DISTINCT id, name FROM my_table) t1 NATURAL LEFT JOIN
(SELECT * FROM my_table WHERE sellid IS NOT NULL) t2
See it on sqlfiddle.
But really, you should normalise your schema further so that you have a table of (personid, name) and a table of (personid, sellid) pairs (from which you essentially perform the above outer join as & when required to obtain the necessary records including NULLs).

Designing database: Linking lots of values between tables

Not worded my question very well, but with these tables:
USER TABLE ANIMALS
u_id username a_id animal
-------------------------- ---------------------------
2 alice 1 cat
4 brian 2 small dog
7 carla 3 big dog
4 rabbit
5 guinea pig
etc.
I want a user to be able to add however many animals they own to their profile.
What new tables/fields and datatypes would be the best way for me to go about this?
Thank you.
If you need to allow multiple types of the same animal per user (Janet can have more than one Rabbit) then do the following. Make UserId and AnimalID your primary key.
I would just do
UserAnimals
------------
UserId
AnimalID
Filled with data your table might look like this:
UserAnimals
------------
UserId || AnimalId
4 || 3
4 || 2
7 || 4
Brian has a small dog and a big dog. Carla has a rabbit.
Essentially you need a table which will map user ids to animal ids.
If you want to add them 1 at a time, you could just use a table like so:
UserAnimals
-----------
UserID (fk to User Table)
AnimalId (fk to Animal Table)
Assuming they might own, say 3 dogs, and you want to track the number, you could either have a row per animal or you could modify the table to include a count of the animals of each type:
UserAnimals
-----------
UserID
AnimalID
Count
I'd probably do it that way if I knew that there was a good chance that folks would have multiples of a given animal, otherwise there's a little more work to do whenever retrieval takes place to arrive at a total.
I guess one could make the argument that the ID field isn't absolutely necessary for the animals either. It could just be a lookup table of strings, though that requires a bit more space for storage and complicates things a little bit if you decide that you want to modify animal names for some reason.
I would recommend you many to many relationship table.
Example:
table: users_x_animals
-----------------------
pid | u_id | a_id
1 2 3
2 4 5
3 2 5
4 7 1
5 4 2
This way if you have index (separate) on u_id and a_id you can either query for "animal with id X is owned by users" or the other way around "user with id x owns these animals".
Hope that helps. :)