Multiple Insert in one transaction mysqli php5 - mysql

I have 3 tables:
user, student, studentdetails
User's primary key is the foreign key for a field in table student (userid) and student's primary key is foreign key for a field in table studentdetails (studentid).
I need to insert data from one form to all 3 tables in one submit, following is the SQL script:
$sql = "
INSERT INTO `tbluser`(`username`, `password`, `roleid`, `datecreated`)
VALUES ('$email','$password', '$role', CURRENT_TIMESTAMP);
SELECT #uid := MAX(`userid`) FROM `tbluser`;
INSERT INTO `tblstudent` (`userid`, `scholarnumber`, `firstname`, `middlename`,
`lastname`, `datecreated`)
VALUES ('#uid', '$scholar_no', '$first_name', '$middle_name', '$last_name',
CURRENT_TIMESTAMP);
SELECT #stuID := MAX(`studentid`) FROM `tblstudent`;
INSERT INTO `tblstudentdetails` (`studentid`,`dob`, `studenttype`, `gender`,
`religion`, `category`, `currentsuburbid`, `currentcityid`, `currentaddress1`,
`currentzipcode`, `currentstateid`, `currentcountryid`,`mobile`,`phone1`,`email1`,
`passportnum`, `permasuburbid`, `permaaddress1`, `dateofjoining`,
`admissionreferenceof`, `datecreated`, `dateupdated`)
VALUES ('#stuid', '$dob' ,'$studenttype' ,'$gender','$religion','$category',
'$currentsuburbid', ' $currentcityid', '$currentaddress1', '$currentzipcode',
'$currentstateid', '$currentcountryid', '$mobile',
'$phone1','$email1','$passportnum','$permanentsuburbid', '$permanentaddress1',
'$doj', ' $admissionreference',current_timestamp, current_timestamp);
";
I am not able to figure out the problem, the above script works in mysql (phpmyadmin) but in php it doesn't work. I understand, I need to use multi_query (??) which I am but it doesn't give any error and inserts in two tables, but doesn't in the third one. I feel it might be to do with the SELECT statement in between? At wits end here, I would greatly appreciate any help. Thanks heaps in advance.

It looks like you're trying to run multiple SQL statements separated by semicolons from mysqli. That doesn't work. You need to issue each distinct statement separately.
You can use MySQL's transactions (as long as you're using InnoDB or some other access method for your tables, and not MyISAM: MyISAM doesn't handle transactions).
You'd do that as follows:
$connection->begin_transaction();
/* issue your statements one by one */
$connection->commit();
This will cause all your inserts, etc, to become visible simultaneously.
BUT: you're trying to make use of your most recent autoincrement ID numbers. You're doing this wrong. You need to use MySQL's LAST_INSERT_ID() function in place of your
SELECT #uid := MAX(`userid`) FROM `tbluser`; /*wrong*/
pattern. This works because LAST_INSERT_ID() delivers the value of the ID from your first insert, so the second insert will use it. It's safe even if multiple programs are inserting things to the table because MySQL keeps a separate value for each program connection. It's faster than what you have because it doesn't have to look at the table, and return the value to your program before using it.
So do this and you'll get what you want.
/* do the first insert, using an autoincrementing uid column */
INSERT INTO `tbluser`(whatever, whatever, whatever)
VALUES (whatever, whatever, whatever);
/* now LAST_INSERT_ID() contains the value inserted into tbluser.uid */
/* do the second insert, using the id from the first insert into tblstudent.userid */
INSERT INTO `tblstudent` (`userid`, whatever, whatever, whatever)
VALUES (LAST_INSERT_ID(), whatever, whatever, whatever);
/* now LAST_INSERT_ID() contains the value inserted into tblstudend.studentid */
/* use that value to insert into tblstudentdetails.studentid */
INSERT INTO `tblstudentdetails` (`studentid`, whatever, whatever, whatever)
VALUES (LAST_INSERT_ID(), whatever, whatever, whatever);

Related

Auto generate Primary Key if not present in MySQL INSERT Query

I created a table in MySql
CREATE TABLE newuser(id VARCHAR(10) PRIMARY KEY,sname VARCHAR(20));
When I INSERT record it works fine
INSERT INTO newuser VALUE('abc123','monika');
But sometimes I don't want to supply id in the INSERT query and sometimes I want to supply. In case I don't supply id MySql automatically generate one.
What can I do to get both below query works?
INSERT INTO newuser VALUE('abc123','monika');
INSERT INTO newuser VALUE('nikita');
'I don't understood ANYTHING' - very new then.
Firstly second insert statement is invalid please review https://dev.mysql.com/doc/refman/8.0/en/insert.html -
'If you do not specify a list of column names for INSERT ... VALUES or INSERT ... SELECT, values for every column in the table must be provided by the VALUES list, SELECT statement, or TABLE statement.'
Secondly uuid is a function in which 'A UUID is designed as a number that is globally unique in space and time.' https://dev.mysql.com/doc/refman/8.0/en/insert.html
You can easily select uuid() to see what it produces.
You will need to increase the id size
If you wish to use it in an insert
insert into users values (uuid(),<sname>);

MYSQL innerjoin insert Multiple tables

Hello i'll give you guys an example of the database tables that are being a pain so i got 3 conected tables yeah trying to make a list into a database anyway i'm having trouble writing a insert query for it
vogelsoort
id|naam|idhooftoonder|
now idhoofdtoonder references to the id a connection table to sort of translate a list to a mysql database (the logic of the table will be added underneath)
hoofdtoonder
|pkey|Id|idondersoorten
now idhoofdtoonder references to a the following table its id
ondersoort
id|naam
I'm sorry for asking this also i'm not experienced enough yet in mysql
edit: the question is since i've tried with a simple insert query it overwrote existing data that i'm looking for help with an insert without overwriting existing id's and connections since (idhootoonder references to hooftonder(id) and hooftoonder idontersoorten references to ondersoort(id) but not all data in ondersoort is connected to the same vogelsoort and i need an insert query that doesn't overide existing connections
You need 3 insert-statements, one for each table.
You may use TRANSACTION to do it safely.
You need to use LAST_INSERT_ID() function (your PK fields must been auto_increment for this), docs: http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id
START TRANSACTION;
INSERT INTO ondersoort (id, naam) VALUES (NULL, 'data');
INSERT INTO hoofdtoonder (pkey, Id, idondersoorten) VALUES (NULL, NULL, LAST_INSERT_ID());
INSERT INTO vogelsoort (id, naam, idhooftoonder) VALUES (NULL, 'data', LAST_INSERT_ID());
COMMIT;

how to uniquely concatenate a value in mysql for a non key field

I'm using a third party mysql table (ie I can't change any of its properties) and I have a row that has id (key), name and value.
I want to store unique cache keys into a row with the name cacheKeys.. and this is my sql statement
$query = "INSERT INTO ".$tableName." (name, value) VALUES ('CacheKeys', '".$key."') ON
DUPLICATE KEY UPDATE value = CONCAT_WS (',', $tableName.value, '$key')";
I've already implemented my caching algorithm, so that every time someone adds a cache key, I check to see if it already exists (from the CacheKeys row above), if it does I fetch it from cache.. otherwise I store it.
Problem is it seems that the sql write operation takes time, and it often stores duplicate cacheKeys
ie: currencies,defaultCurrencyId,user19,currency1,currency1,currency1,currency1,currency1
So I need to check to see that I'm not adding a duplicate key into the cacheKeys field.. and I need to do that using SQL (using php, ie regex etc would just be waaaay to expensive).
Try this::
INSERT INTO tb (firstname, lastname) VALUES ('Jack', 'Doe') IF NOT
EXISTS ( SELECT * FROM tb WHERE firstname='Jack' AND lastname='Doe' );

MySql Insert to multiple tables based on the ID generated in the first table

I have three tables in my database. A users table, StoreA and StoreB
StoreA and StoreB both have a unique key which is the user ID value.
What I want is; When I create a user and insert them into the database, how can I Insert a row into the other two tables without too many additional queries.
I figure I can do this by inserting the user in one query,
then in another return the newly created user ID,
then in another, using said ID, create rows in StoreA and StoreB
Can I cut out the middle query?
Can I cut out the middle query?
YES
START TRANSACTION;
INSERT INTO user (id, name, other)
VALUES (null, 'John','rest of data');
INSERT INTO storeA (id, user_id, other)
VALUES (null, #user_id:= LAST_INSERT_ID(), 'rest of data');
INSERT INTO storeB (id, user_id, other)
VALUES (null, #user_id, 'rest of data');
COMMIT;
See: http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
It's a good idea to do this in a transaction, you you're not stuck with just a user with no other data if something goes wrong.
It's not a DB requirement though.
Yes - there should be a function available to get the last inserted ID (assuming it's an autoincrement field) without another query. In PHP, it's mysql_insert_id(). Just call that after the first query.
YES
Q1: insert into table1 values (...);
Q2: insert into table2 values (last_insert_id(), ...);
last_insert_id is the default mysql build-in function
Most of the mysql libraries in various programming language did support return last insert id.
But You did not mention what sort of language you are using to connect to mysql.,
so cannot provide any example
I just wanted to share a php solution.
If you're using mysqli, first execute your insert query.
Then do
$db_id = $this->db->insert_id;
Why don't you use their username as the primary key instead of creating an arbitrary user_id field thats auto incremented? Their user names are unique, right?

MySQL LAST_INSERT_ID() used with multiple records INSERT statement

If I insert multiple records with a loop that executes a single record insert, the last insert id returned is, as expected, the last one. But if I do a multiple records insert statement:
INSERT INTO people (name,age)
VALUES ('William',25), ('Bart',15), ('Mary',12);
Let's say the three above are the first records inserted in the table. After the insert statement I expected the last insert id to return 3, but it returned 1. The first insert id for the statement in question.
So can someone please confirm if this is the normal behavior of LAST_INSERT_ID() in the context of multiple records INSERT statements. So I can base my code on it.
Yes. This behavior of last_insert_id() is documented in the MySQL docs:
Important
If you insert multiple rows using a single INSERT statement, LAST_INSERT_ID() returns the value generated for the first inserted row only. The reason for this is to make it possible to reproduce easily the same INSERT statement against some other server.
This behavior is mentioned on the man page for MySQL. It's in the comments but is not challenged, so I'm guessing it's the expected behavior.
I think it's possible if your table has unique autoincrement column (ID) and you don't require them to be returned by mysql itself. I would cost you 3 more DB requests and some processing. It would require these steps:
Get "Before MAX(ID)" right before your insert:
SELECT MAX(id) AS before_max_id FROM table_name`
Make multiple INSERT ... VALUES () query with your data and keep them:
INSERT INTO table_name
(col1, col2)
VALUES
("value1-1" , "value1-2"),
("value2-1" , "value2-2"),
("value3-1" , "value3-2"),
ON DUPLICATE KEY UPDATE
Get "After MAX(ID)" right after your insert:
SELECT MAX(id) AS after_max_id FROM table_name`
Get records with IDs between "Before MAX(ID)" and "After MAX(ID)" including:
SELECT * FROM table_name WHERE id>$before_max_id AND id<=$after_max_id`
Do a check of retrieved data with data you inserted to match them and remove any records that were not inserted by you. The remaining records have your IDs:
foreach ($after_collection as $after_item) {
foreach ($input_collection as $input_item) {
if ( $after_item->compare_content($input_item) ) {
$intersection_array[] = $after_item;
}
}
}
This is just how a common person would solve it in a real world, with parts of code. Thanks to autoincrement it should get smallest possible amount of records to check against, so they will not take lot of processing. This is not the final "copy & paste" code - eg. you have to create your own function compare_content() according you your needs.