INSERTing into mysql table for user relationships - mysql

I have a table for many-to-many relationship of users (three columns: relationship_id, user_id, user_id). How can I keep the relationships unique when the table accepts any entry? When I have a row of
22 11 43
How can I prevent INSERT of next_id 11 43 and more importantly next_id 43 11? When user 11 requested relationship with user 43, user 43 must not be able to request relationship with user 11.
How can I check two columns before INSERT?
And my problem is even more serious as I have two tables (request and relationships). A row from request table will be deleted and inserted into relationships upon user approval. The reason for using two tables is that many pending requests make the table so long, which should be used regularly for displaying user friends.
When INSERTing request from user 11 to user 43, what is the fastest and efficient method to check for possible existence of 11 43 and 43 11 rows in tables: requests and relationships?

Anytime I have used a "Linking Table" to keep track of many to many relationships I always DELETE any existing relationships before INSERTING the new relationships. However, this may not work in your case because your linking table contains the surrogate key relationship_id. If you drop the surrogate key from the linking table and use a stored procedure you can do every thing you listed.
Identifying DuplicatesCreate a View using CASE logic
CREATE VIEW vFriendRequests
AS
SELECT
CASE
WHEN ID_1 < ID_2 THEN ID_1
ELSE ID_2
END CASE as RequestId_1,
CASE
WHEN ID_1 < ID_2 THEN ID_2
ELSE ID_1
END CASE as RequestId_2
FROM Friend_Requests_Table
Then you can do a select distinct from this view to get only the unique sets of requests.

Here are some options to achieve what you want (which to choose or combine depends mainly on your datamodel, client architecture and storage engine):
create a unique composite index on both user_id columns
revoke INSERT into relationships and implement a Stored Procedure for INSERT (this can do any checks etc. you want) OR implement an ON BEFORE INSERT trigger which does what you want
IF the the order of the user_ids is not relevant change the INSERT code to always sort both IDs before INSERTing (for example via the Stored Procedure approach)
This way you don't need to check explicitely but the index will do all work for you
create a fourth column idcomb in relationships with a UNIQUE INDEX and an ON BEFORE INSERT TRIGGER which just takes both user_id sorts them and concatenates them with a - inbetween and assign that to idcomb column as value... this way all work is done by the index and no change on the client-side is needed (when some duplicate is inserted is just comes back with an error)

Related

Best way to create Unique index in MYSQL on two columns when one column can contain empty values

I would like to create a unique index on two columns, one of which always has a value but another where the value is often 0 by default.
In the following example, userid 22 liked 2 TV shows and 3 movies. I want to prevent a case where the user would like the same tv show or movie twice. However, I can't make it UNIQUE to userid and movieid or userid and tvshowid, as there are numerous cases where the userid is paired with 0 when the like is for a TV show.
Likes
id|userid|movieid|tvshowid
1|22|0|33
2|22|0|34
3|22|66|0
4|22|67|0
5|22|68|0
I could change all the 0s to NULLs but NULLS tend to create problems on the client.
What is the best way to handle creating a unique index on two columns when one has multiple empty values in it?
If it requires changing all the 0s to NULLs, is there an efficient MYSQL statement to do this?
Change zeroes to NULLs:
UPDATE Likes SET movieid = NULL WHERE movieid = 0;
UPDATE Likes SET tvshowid = NULL WHERE tvshowid = 0;
Create two unique keys, one for movies and one for tvshows.
ALTER TABLE Likes
ADD UNIQUE KEY (userid, movieid),
ADD UNIQUE KEY (userid, tvshowid);
I don't know what you mean by NULLs causing problems on the client. Any client that can use SQL should be able to handle NULLs.
Plan B: 2 tables, each with 3 columns. One talks about TV shows; one for movies.

How to store a set/array of unique numbers in a MySQL cell

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.

Is a bidirectional self-join relationship in Filemaker possible?

I have a table of transactions. For any given record, I want to be able to include a portal which shows all related transactions. Since the related records are in the same table as the parent record, I have created a second table occurrence (TO). I have linked these two TOs with a join table, into which I enter the foreign keys of the two TOs to indicate which records relate to which other records.
On the layout for a given transaction, I've added a portal which displays related records from the second table occurrence. So far, so good.
So, let's say in the join table I said transaction 100 is linked to transactions 105 and 110. In the portal for transaction 100, I can see records 105 & 110.
However, I would also like to see transaction 100 in the portal for transaction 105, but can't figure out how to do this, without having to manually enter the same relationship, but in reverse.
NB. I'm using Filemaker Pro 12.
You need to create two records for each relationship in the intermediate table to do this.
For example, you have TO_1 and TO_2 and an intermediate table. The intermediate table has two fields, id_1 and id_2. When you create a relationship between TO_1 and TO_2, create two records in the intermediate table. One record stores the id of TO_1 in id_1 and TO_2 in id_2. The second record stores the id of TO_1 in id_2 and TO_2 in id_1.
It sounds like you want all the foreign keys to be present in a single field that you use a multi-key. In your example, the multi-key would look like this:
100
105
110
If you do not want to display the present record in a portal of related records, you can omit it be defining the relationship to exclude itself by adding the predicate something like this:
fk = fk AND
pk ≠ pk
Would that work?

Syncronizing entries in a table according to a user array in MySQL

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.

MySQL Insert Race Condition

I have a webapp that currently stores all of a user's searches into a search_log table. I now want to create another table called results_log that stores all the results we supply to the user. The search_log table contains a primary key called id_search and the results log table has the foreign key id_search, and one other field id_result. The id_searched field is an auto_incrementing field in both tables.
In my web app I would do the inserts in this sequential order:
insert into search_log table
insert into result_log table
I am worried this may cause a race condition. If user A and user B both finish the webapp and reach this part of the code at about the same time, is it possible that the order would go:
User A -> Insert into search_log
User B -> Insert into search_log
User B -> Insert into result_log
User A -> Insert into result_log
Since both tables are auto_incrementing on the id_search field, I'm worried User A and User B will have their data swapped. I also thought about querying for the id_search, but it seems like a even worse solution.
My question is:
-Is there a way to fix this race condition?
-Would one solution be inserting into two tables with one SQL query? Is this possible?
If those tables are related, then you should include the auto increment ID with when inserting. After inserting into search_log, get the last insert ID, no lookup needed. Then include that in the result_log search as another field.
Never rely on auto increment IDs being the same in different tables.