How to avoid mysql primaryKey constraint Error - mysql

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)

Related

UNIQUE Constraint On Combination of Two Column Values

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.

Can't add date to archive

I have duplicated a Table to create an Archive table, and for some reason I can't make to Appending Query to work.
This is the SQL code:
INSERT INTO tblArc
SELECT tblCostumer.*
FROM tblCostumer, tblArc
WHERE (((tblArc.num)=[Enter Client Number you'd like to move to the archive]));
When I enter the costumer number, it says "You are about to append 0 row(s)" instead of appending 1 row.
That FROM clause would give you a cross join, which is probably not what you should really want ...
FROM tblCostumer, tblArc
Instead SELECT only from tblCostumer based on its primary key. For example, if the primary key is tblCostumer.num ...
INSERT INTO tblArc
SELECT tblCostumer.*
FROM tblCostumer
WHERE tblCostumer.num=[Enter Client Number you'd like to move to the archive];
And if the structures of the two tables are not the same, list the specific fields instead of ...
INSERT INTO tblArc
SELECT tblCostumer.*

Ensure combo of values are unique, but entered any number of times - Mysql

How can I enforce constraint checks to ensure that a given combination of values are unique, but can be entered any number of times?
Example: I have two columns: Group_ID and Group_Name. So all data with Group_ID = 1 will always have Group_Name as 'Test1'. What I want to prevent is someone entering 'Test2' into Group_Name where Group_ID=1. This should fail the insert. All this data is loaded directly into the DB without any UI, hence I cannot enforce these checks in application. So what I need is:
A unique constraint over multiple columns, but only for the given combination without checking how many times they have been entered.
Is there anything built in Mysql to do this?
You should normalize your table a little bit. The group_id,group_name pair should be in a separate table that defines your groups and then the table you're working with should only have group_id. Then you could add a foreign key from your table to the group table to ensure that your group_id values reference real groups.
If you can't normalize your tables then you'll probably have to use a before insert and before update trigger to ensure that Group_ID and Group_Name always come together as required.

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.

Scoped/composite surrogate keys in MySQL

Here's an excerpt of my current database (changed the table-names for an easier understanding):
Pet(ownerFK, id, name, age)
Owner(id, name)
Where id is always a surrogate key, created with auto_increment.
I want to have the surrogate key Pet.id to be "scoped" by Pet.ownerFK or in otherwords, have a composite key [ownerFk, id] as my minimum key. I want the table to behave like this:
INSERT Pet(1, ?, "Garfield", 8);
INSERT Pet(1, ?, "Pluto", 12);
INSERT Pet(2, ?, "Mortimer", 1);
SELECT * FROM Pet;
RESULT:
Pet(1, 1, "Garfield", 8)
Pet(1, 2, "Pluto", 12)
Pet(2, 1, "Mortimer", 1)
I am currently using this feature of MyISAM where "you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix. This is useful when you want to put data into ordered groups."
However, due to various (and maybe obvious) reasons, I want to switch from MyISAM to InnoDB, as I need transactions at some places.
Is there any way how to achieve this effect with InnoDB?
I found some posts on this issue, many of them proposed to write-lock the table before insertion. I am not very familiar with this, but wouldn't be a table-write-lock a little-bit of an overhaul for this one? I rather thought of having write-safe transactions (which I never did before) if these are possible - having a Owner.current_pet_counter as an helper field.
So another acceptable Solution would be...
Actually I don't need the "scoped" ID to be part of the actual Key. My actual database design uses a separate "permalink" table which uses this 'feature'. I currently use it as a workaround for the missing transactions. I thought of the following alternative:
Pet(id, ownerFK, scopedId, name, age), KEY(id), UNIQUE(ownerFK, scopedId)
Owner(id, name, current_pet_counter)
START TRANSACTION WITH CONSISTENT SNAPSHOT;
SELECT #new=current_pet_counter FROM Owner WHERE id = :owner_id;
INSERT Pet(?, :owner_id, #new, "Pluto", 21);
UPDATE Owners SET current_pet_counter = #new + 1 WHERE id = :owner_id;
COMMIT;
I haven't worked with transactions/transactionvars in MySQL yet, so I don't know whether there would be serious issues with this one.
Note: I do not want to reuse ids that have been given to a pet once. That's why I don't use MAX(). Does this solution have any caveats?
I don't believe so. If you really had to have that schema, you could use a transaction to SELECT the MAX(id) WHERE ownerFK, then INSERT.
I'm very sceptical there's a good reason for that schema, though; the primary key is now also a fact about the key, which might make the database theorists unhappy.
Normally you'd want ‘id’ to really be a proper primary key on its own, with ownerFK used to group and, if you needed it, a separate ‘rank’ column to put pets in a particular order per owner, and a UNIQUE index over (ownerFK, rank).