UNIQUE Constraint On Combination of Two Column Values - mysql

Wasn't sure how to word the question so my bad if it sounds weird.
I have a table in my database called friendRequests with the the following columns: id, sender_id, recipient_id, and status. How can I make sure that no other row has duplicate recipient_id and sender_id values?
So for example, if I had a row in the table with the following values: (1, 4, 6, 0), how can I make sure that no other row has a sender_id of 4 and a recipient_id of 6 AND that no other row has a sender_id of 6 and a recipient_id of 4?

For same relations - use a unique constraint on <sender, receiver>.
The inverse relation <receiver, sender> however will be possible, cause it's different ids for the unique key constraint.
To handle this (using a uniqe key constraint), you have to add another column, let's call it friendship - There, you'll add a unique key constraint, and insert the users ids, concatenated, BUT ORDERED:
I.e. If a user 3 sends a friend request to 10, you'll insert 3-10 to that column. If the invitation goes from 10 to 3, you'll add 3-10 as well.
This way, you can keep track of WHO initiated the friendship (sender_column=3, receiver-column=10) but also ensure that there is no backwards invite (friendship=3-10 already exists)
So that's something like
INSERT INTO friendships(sender, receiver, friendship) VALUES(3,10,"3-10");
or vice versa:
INSERT INTO friendships(sender, receiver, friendship) VALUES(10,3,"3-10");
One of both constraints will avoid the insertion if the friendship has been already requested. (Actually the second constraint would be sufficent for any case, first 2 columns would only allow to determine the active and passive part of the friendship.)

You have to create a unique index on the table.
create unique index sender_recipient
on friendRequest (sender_id, récipient_id)

What I'm hearing is, if user A invites user B, you don't want to create a new record for user B inviting using A.
I don't think it's possible to enforce a constraint like that, except perhaps through the use of triggers, which I would probably not recommend. I would suggest to try and enforce this in your application.

Related

How to re-arrange database primary key

Good day
I create database at localhost for website. and put some info, than i delete and re-enter info from database. and now for 'id' primary key i have more than 200 rows. I want to re-arrange primary key.
for example
id |name
1 |Samuel
2 |Smith
4 |Gorge
15 |Adam
19 |David
i want to have
id |name
1 |Samuel
2 |Smith
3 |Gorge
4 |Adam
5 |David
Is it possible to do with any command?
You could drop the primary key column and re-create it. All the ids will then be reassigned, I assume in the order in which the rows were inserted.
alter table your_table drop column id;
then to create it
ALTER TABLE `your_table_name` ADD `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
The purpose of a primary key is to uniquely identify each row, so rows in one table can be related to rows in another table. Remember, this is a relational database and part of the meaning of "relational" is that entities are related to each other.
In other words, you don't want to change the primary key of rows, because that will break links from other tables. MySQL does not guarantee that auto incremented values are inserted without holes. In fact, as you have discovered, deletions and re-inserts cause problems.
Your interpretation of the "primary key" as a sequential number with no gaps assigned to each row maintained by the database is simply not correct.
Even though you don't want to do this, you can. I advise against it, but you can:
declare #rn := 0;
update t
set id = (#rn := #rn + 1)
order by id;
If you want to enforce this over time, you will need to learn about triggers.
Consider this scenario: Gorge sends some offensive emails, and people complain and his account (#4) is denylisted.
Then you reorder your primary key values, and Adam is now assigned id 4. Suddenly, he finds himself banned! And lots of people mistrust him without cause.
Primary keys are not required to be consecutive -- they're only required to be unique. It's normal for there to be gaps, if you sometimes ROLLBACK transactions, or DELETE rows.
Most likely the primary key is being auto generated from some sort of auto increment sequence. In that case you can take the following steps:
1) update all the primary keys to the next value of the sequence: this will collapse all of the values into a contiguous range. In your case those ids will be 20, 21, 22, 23, 24. Postgres example:
UPDATE my_table SET id = nextval(my_table_id_sequence)
2) reset the sequence to start at 1: In Postgres this would look like the following:
ALTER SEQUENCE my_table_id_sequence RESTART WITH 1
3) update the values to the next value of the sequence again: Now can move all the rows back "down" to start at 1, and in your case they will be 1, 2, 3, 4, 5. It is important to first consolidate all the values at the "top" of the sequence before resetting, because that way we guarantee that there wont be any primary key collisions at the "bottom"
UPDATE my_table SET id = nextval(my_table_id_sequence)
NOTE: this approach only works if there are no foriegn keys which are referring to the primary key of the table. If there are foreign keys you can still take the same approach, but first do these 3 steps:
1) find all of the related tables/columns that are referencing this primary key column
2) create a function that will cascade updates to the pk out to all fks
3) create a trigger that will execute the above function whenever the pk is updated: at this point, when we update the primary key column, all of the related foreign keys will also be updated. Depending on the database, you might need to explicitly defer constraint validation, or do the whole thing in one transaction.
For an example of what the above might look like in Postgres you can take a look at my answer here How Do I Deep Copy a Set of Data, and Change FK References to Point to All the Copies?

Database design: auto-increment key & update inconsistencies

Two tables share a unique identifier 'id'. Both tables are meant to be joined by using 'id'.
Defining 'id' as an auto incrementing primary key in both tables may risk update inconsistencies.
Is there some general pattern to avoid such a situation or do I have to deal with updating table1 first and table2 by utilizing the last inserted id after (therefore not declaring id as auto inc in table2)?
First, if you use InnoDB table engine in MySQL you could use both transactions and foreign keys for data consistency.
Second, after the insert in the first table, you could get the last insert id (depending on the way you access the db) and use it as foreign key.
Eg
Table 1: Users: user_id, username
Table 2: User_Profiles: user_id, name, phone
In User_Profiles you don't need to define user_id as auto increment, but first insert a record in Users table and use the user_id for the User_Profiles record. If you do this in transaction, the Users record won't be seen outside of the transaction connection until it's completed, this way you guarantee that even if something bad happens after you insert the user, but before you have inserted the profile - there won't be messed up data.
You could also define that the user_id column in User_Profiles table is foreign key of Users table thus if someone deletes a record from the Users table, the database would automatically delete the one in User_Profiles. There are many other options - read more about that.
There is no problem with same column name 'id' in any number of tables.
Several persistence layer frameworks do it same way.
Just use aliases in your SQL to distinct your tables accordingly.
do I have to deal with updating table1 first and table2 by utilizing the last inserted id after (therefore not declaring id as auto inc in table2)?
Yes. And make id a foreign key so it can only exist in table2 if it already exists in table1.
Yes you do, and remember to wrap the operation in a transaction.

How to avoid mysql primaryKey constraint Error

I have a Mysql table.
Requests - Request_id, Request_message
After user filling the Request_message in HTML form user clicks on submit button. I will generate a request ID from the requests table, and then add the row. (There are two hits to the table, one for getting the last request_id and one for add the row to the table). I want to avoid this.
Also, say at the same time 7-8 users hit the submit button, they will get the same last request_id from the table. they will add 1 to the last request_id, and tries to add the row. except one all are getting primary key constraint error .. How to avoid this ??
Please provide your suggestions.
Thanks
Devesh
Use AUTO_INCREMENT for the primary key.
Edit:
If you don't use integer type for the column, then another solution is using row level lock.
For the first question, you can do something like that
INSERT INTO request (Request_id,Request_message) VALUES (
(SELECT MAX(Request_id) FROM request), yourMessage)
or better, you can use the autoincrement for the p.k.
Moreover, if you demand all the increment/inserction problem to your DBMS, you can avoid directly problems related to p.k.
Use sp to insert the record in table.
or
Use auto number field for primary key in table.
or
use
INSERTINTO request
(Request_id,
Request_message)
VALUES ( (SELECT 'REQ' + Lpad(Ifnull(Max(Request_id) + 1, 0), 4, 0)
FROM request),
yourMessage)

Composite primary key comprising two foreign keys referencing same table: SQL Server vs. MySQL

I've read over a number of posts regarding DB table design for a common one-to-many / users-to-friends scenario. One post included the following:
USERS
* user_id (primary key)
* username
FRIENDS
* user_id (primary key, foreign key to USERS(user_id))
* friend_id (primary key, foreign key to USERS(user_id))
> This will stop duplicates (IE: 1, 2)
from happening, but won't stop
reversals because (2, 1) is valid.
You'd need a trigger to enforce that
there's only one instance of the
relationship...
The bold portion motivated me to post my question: is there a difference between how SQL Server and MySQL handle these types of composite keys? Do both require this trigger that the poster mentions, in order to ensure uniqueness?
I ask, because up until this point I've been using a similar table structure in SQL Server, without any such triggers. Have I just luckily not run into this data duplication snake that's lurking in the grass?
Yes, all DBMS will treat this the same. The reason is that the DBMS assumes that the column has meaning. I.e., the tuple is not comprised of meaningless numbers. Each attribute has meaning. user_id is assumed to have different meaning than friend_id. Thus, it is incumbent upon the designer to build a rule that claims that 1,2 is equivalent to 2,1.
You could just use a check constraint that friend_id > user_id to prevent "reversals". This would enforce that it was not possible to enter a pair such as (2, 1) such a relationship would have to be entered as (1, 2).
If you friendship relationship is symmetrical, you need to add a CHECK(user_id < friend_id) into the table definition and insert the data like this:
INSERT
INTO friends
VALUES (
(CASE user_id < friend_id THEN user_id ELSE friend_id END),
(CASE user_id > friend_id THEN user_id ELSE friend_id END)
)
In SQL Server, you can build a UNIQUE index on a pair of computed columns:
CREATE TABLE friends (orestes INT, pylades INT, me AS CASE WHEN orestes < pylades THEN orestes ELSE pylades END, friend AS CASE WHEN orestes > pylades THEN orestes ELSE pylades END)
CREATE UNIQUE INDEX ux_friends_me_friend ON friends (me, friend)
INSERT
INTO friends
VALUES (1, 2)
INSERT
INTO friends
VALUES (2, 1)
-- Fails
To fetch all friends for a given user, you need to run this query:
SELECT friend_id
FROM friends
WHERE user_id = #myuser
UNION ALL
SELECT user_id
FROM friends
WHERE friend_id = #myuser
However, in MySQL, it may be more efficient to always keep each both copies of each pair.
You may find these article interesting:
Selecting friends
Six degrees of separation
If relationship is symmetrical, then one alternative is to "define" the relationship as asymetrical in the database, but just always add both tuples every time you add either one.
You are basically saying "Nature of friendship is in DB assymetrical, A can be friend to B while B is not friend to A, but application will always add (or remove) BOTH records (a,B) and (B, A) anytime I add (remove) either. That simplifies the query logic as well since you don't have to look in both columns anymore. One extra insert / delete each time you modify data, but fewer reads when querying...

Best Primary Key for a friendship table

I have read a number of solutions for a mysql Facebook friendship table and have decided on a fairly simple table with two fields user_a and user_b. I would then using a query with a UNION to get a list of all of a users friends (as they could be in user_a or user_b). My question now is... is it better to have a auto incrementing unique id or a compound id?
table 1)
user_a, user_b
table 2)
unique_id, user_a, user_b
My comments:
either approach for the key is fine. I would prefer a compound key over surrogate key to save space and avoid additional indexes
you may require a surrogate key though - some DALs do not work with compound keys
Update:
You may consider that friendship is a two-way street. Just because UserA has friended UserB does not mean that UserB has friended UserA. If you track both sides, it makes your queries easier. In that case you do:
Friend
-------
UserID
FriendUserID
So, you are only matching on the UserID column to get the list of the user's friends. If two users friend each other, you put two rows in the table. If one user unfriends another, you remove that one row.
While it is true that the compound key solution seems to be more elegant from a design perspective and less space-consuming at first glance, there are circumstances in which I'd personnaly go for an auto incremented numeric id instead.
If the friendship is referenced elsewhere, it will save more space on the long run to have a single numeric ID as a foreign key in the referencing table than a compound ID. Plus, an index on a single id will be (slightly) shorter and faster than a composite index if you query often on the friendship ID.