Concurrent Inserts in mySQL - mysql

I have 3 table - TB1, TB2 and r_tb1_tb2 (innoDB)
TB1 Hold the details of the users (will be inserted)
- id (primary, unique)
- name
TB2 Holds the details of the course the users can take (static table)
- id (primary, unique)
- name of the course
r_tb1_tb2 hold the relation between the 2 tables
- rID
- user_id (from table 1)
- course_id (reference to table 2)
When I insert a new row in TB1, I get the id of the last inserted row.
And use that to insert another row in r_tb1_tb2
I can forsee that this may result to erroneous entries in case of simultaneous instances of inserts in tb1.
Can someone please point to the best practices for such simultaneous updates.
Thanks in advance.

last_insert_id has built in protection for this
The ID that was generated is maintained in the server on a
per-connection basis. This means that the value returned by the
function to a given client is the first AUTO_INCREMENT value generated
for most recent statement affecting an AUTO_INCREMENT column by that
client. This value cannot be affected by other clients, even if they
generate AUTO_INCREMENT values of their own. This behavior ensures
that each client can retrieve its own ID without concern for the
activity of other clients, and without the need for locks or
transactions.
(emphasis theirs)
Thus if two different users are taking action on your site that results in records being inserted into T1, the last_insert_ids for those users will be different because they are using two different connections (clients in the conext above)

last_insert_id return value according the the user:

Related

Transfer records between database with overlap ID (Primary Key)

My current project need to transfer data between databases. The scenario is that I have two different database, they have the similar schema for tables but the information inside table is completely different.
I want to transfer one record from one database to the other, and completely removed it in the old database once transferred. I realize that the primary key for that record may overlap with the one inside the new database.
I think of many solutions: I can add a character in front of the id to distinguish data from different table, then how can I add the character (for example B1100) in front of the id when I transfer? Or how do I automatically increase the ID (meaning generating new ID with the correct order in the new database).
For example: TABLE1 and TABLE2. TABLE1 has the most recent ID is 2000, and I want to transfer a record from TABLE2 with the ID is also 2000. Is there a way that I can transfer that record from table2 with table2.ID is 2000 to a new record of TABLE1 with table1.ID is 2001?
Thank you!
I am planning to use this SQL Query for transferring:
INSERT INTO DATABASE_1.dbo.table1
SELECT *
FROM DATABASE_2.dbo.table2
WHERE DATABASE_2.dbo.table2.id = currentID;

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.

Database design: users and guests

I have two types of people on my site, users and guests. Virtually they are the same, except for creation/auth processes.
At the moment I have two tables:
t_users
userId[PRIMARY, AUTOINC] username[UNIQUE]
t_guests
guestId[PRIMARY, AUTOINC] userId
When somebody enters the site, script does the following:
1) creates new guest record by inserting a new row to t_guests
2) adds new record to t_users, using guestId generated on previous step (guest username = “Guest guestId”)
3) updates guest record setting userId assigned on step 2
I feel this database design to be just awful, because it contains many vulnerabilities. For example, if username "Guest xyz" already exists in t_users table, step 2 will fail and step 3 will assign wrong value to the userId (depending on implementation it’ll be 0 or guestId, assigned on step 1).
Actually I only need t_guests table for its auto increment feature to generate unique usernames for guests. Is there a way to use just one consolidated table and register guests using single query?
UPDATE: I can do the following to insert guests in a single table:
SELECT #mg := IFNULL(MAX(guestId), 0) + 1 FROM t_users;
INSERT INTO t_users (guestId) VALUES(#mg);
But I can't be sure, that nobody inserts a new guest record in t_users between execution of those two statements. And I can't make guestId unique, because real users will have it equal to zero.
If you just have 1 table with"
userID, username, type
for the username you could use your script to generate a Guid and use that as the username, or someother random variable. If you use a GUID it is virtually impossible that you get 2 guids that collide.
Also if you do have 2 usernames that collide if you make sure that the username column has to be unique then the insert will fail and you could just try again.
You definitely aught to just have 1 table here.

INSERTing into mysql table for user relationships

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)

How to handle fragmentation of auto_increment ID column in MySQL

I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this or if not, at the very least, how to write an SQL query that:
Alters the auto_increment value to be the max(current value) + 1
Return the new auto_increment value?
I know how to write part 1 and 2 but can I put them in the same query?
If that is not possible:
How do I "select" (return) the auto_increment value or auto_increment value + 1?
Renumbering will cause confusion. Existing reports will refer to record 99, and yet if the system renumbers it may renumber that record to 98, now all reports (and populated UIs) are wrong. Once you allocate a unique ID it's got to stay fixed.
Using ID fields for anything other than simple unique numbering is going to be problematic. Having a requirement for "no gaps" is simply inconsistent with the requirement to be able to delete. Perhaps you could mark records as deleted rather than delete them. Then there are truly no gaps. Say you are producing numbered invoices: you would have a zero value cancelled invoice with that number rather than delete it.
There is a way to manually insert the id even in an autoinc table. All you would have to do is identify the missing id.
However, don't do this. It can be very dangerous if your database is relational. It is possible that the deleted id was used elsewhere. When removed, it would not present much of an issue, perhaps it would orphan a record. If replaced, it would present a huge issue because the wrong relation would be present.
Consider that I have a table of cars and a table of people
car
carid
ownerid
name
person
personid
name
And that there is some simple data
car
1 1 Van
2 1 Truck
3 2 Car
4 3 Ferrari
5 4 Pinto
person
1 Mike
2 Joe
3 John
4 Steve
and now I delete person John.
person
1 Mike
2 Joe
4 Steve
If I added a new person, Jim, into the table, and he got an id which filled the gap, then he would end up getting id 3
1 Mike
2 Joe
3 Jim
4 Steve
and by relation, would be the owner of the Ferrari.
I generally agree with the wise people on this page (and duplicate questions) advising against reusing auto-incremented id's. It is good advice, but I don't think it's up to us to decide the rights or wrongs of asking the question, let's assume the developer knows what they want to do and why.
The answer is, as mentioned by Travis J, you can reuse an auto-increment id by including the id column in an insert statement and assigning the specific value you want.
Here is a point to put a spanner in the works: MySQL itself (at least 5.6 InnoDB) will reuse an auto-increment ID in the following circumstance:
delete any number rows with the highest auto-increment id
Stop and start MySQL
insert a new row
The inserted row will have an id calculated as max(id)+1, it does not continue from the id that was deleted.
As djna said in her/his answer, it's not a good practice to alter database tables in such a way, also there is no need to that if you have been choosing the right scheme and data types. By the way according to part od your question:
I have a table with an auto_increment field and sometimes rows get deleted so auto_increment leaves gaps. Is there any way to avoid this?
If your table has too many gaps in its auto-increment column, probably as a result of so many test INSERT queries
And if you want to prevent overwhelming id values by removing the gaps
And also if the id column is just a counter and has no relation to any other column in your database
, this may be the thing you ( or any other person looking for such a thing ) are looking for:
SOLUTION
remove the original id column
add it again using auto_increment on
But if you just want to reset the auto_increment to the first available value:
ALTER TABLE `table_name` AUTO_INCREMENT=1
not sure if this will help, but in sql server you can reseed the identity fields. It seems there's an ALTER TABLE statement in mySql to acheive this. Eg to set the id to continue at 59446.
ALTER TABLE table_name AUTO_INCREMENT = 59446;
I'm thinking you should be able to combine a query to get the largest value of auto_increment field, and then use the alter table to update as needed.