mysqli->insert_id on update (PHP) - php

Does it work for anyone? :P
I can properly get insert_id while inserting, but not on update. Of course contactsId column is AUTO_INCREMENT.
Whole code:
<?php
$mysqli = new mysqli('localhost', [USER], [PASSWORD], [DB]);
$mysqli->set_charset("utf8");
$query = 'INSERT INTO contacts (contactsName) VALUES ("Mariola")';
$result = $mysqli->query($query);
echo $mysqli->insert_id . '<br />';
$query = 'UPDATE contacts SET contactsName = "Mariola" WHERE contactsId = 289';
$result = $mysqli->query($query);
echo $mysqli->insert_id;
Output:
1514
0
I HAVE record with id 289, and update works fine.

This behavior is described very clear in the document.
mysqli::$insert_id -- mysqli_insert_id — Returns the auto generated
id used in the last query
If the last query wasn't an INSERT or UPDATE statement or if the
modified table does not have a column with the AUTO_INCREMENT
attribute, this function will return zero.

From MySQL documentation on LAST_INSERT_ID():
If expr is given as an argument to LAST_INSERT_ID(), the value of the argument is returned by the function and is remembered as the next value to be returned by LAST_INSERT_ID(). This can be used to simulate sequences:
Create a table to hold the sequence counter and initialize it:
mysql> CREATE TABLE sequence (id INT NOT NULL);
mysql> INSERT INTO sequence VALUES (0);
Use the table to generate sequence numbers like this:
mysql> UPDATE sequence SET id=LAST_INSERT_ID(id+1);
mysql> SELECT LAST_INSERT_ID();
The UPDATE statement increments the sequence counter and causes the next call to LAST_INSERT_ID() to return the updated value. The SELECT statement retrieves that value. The mysql_insert_id() C API function can also be used to get the value. See Section 20.6.7.37, “mysql_insert_id()”.
Maybe something like this will work:
$query = 'UPDATE contacts SET id = LAST_INSERT_ID(id), contactsName = "Mariola" WHERE contactsId = 289';

Related

Last insert id issue--How to fetch it

I'm using $id = mysqli_insert_id($connection); to get the last inserted id, but in case if it updates any record in the table, it returns 0 as last inserted id.
Is there any way to handle this?
I want to get id each time weather it's inserting or updating.
Thanks
Edit
I need this id to be used for inserting data into table2
id from tab1
put data into tab2 where id from tab1 is FK
and most important, I'm not using the update with where clause
Here is my code that I'm using
$val = utf8_encode($val);
mysqli_set_charset($connection, 'utf8');
mysqli_query($connection, "SET NAMES 'utf8'");
mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 0;");
$sql = "INSERT INTO leaks($insert) VALUES($val)";
$sql .= " ON DUPLICATE KEY UPDATE `url` = '".mysqli_real_escape_string($connection,$data['url'])."';";
mysqli_query($connection, ($sql))or die(mysqli_error($connection)."<br />".print($sql));
$id = mysqli_insert_id($connection);
$proofs['leaks_id'] = $id;
mysqli_query($connection, "SET FOREIGN_KEY_CHECKS = 0;");
print_r($id);
$this->insertProofs($connection, $proofs);
connection::close_connection($connection);
Please note down that $this->insertProofs($connection, $proofs); inserts data to table2 on the base of key passed to it
On INSERT
After executing an INSERT query, using mysqli_insert_id() is absolutely fine.
On UPDATE
Depending on your update, you;
Would know the id's you're updating
Know the criteria to search for the id's from the update.
For example, if your UPDATE was something like;
UPDATE `foo` SET `x`='y' WHERE `a`='b'
You can then run
SELECT `id` FROM `foo` WHERE `a`='b'
to fetch the updated id's.
Edit
I see you're using ON DUPLICATE KEY UPDATE.
You can modify your query to become (assuming id is the primary auto_increment key)
ON DUPLICATE KEY UPDATE
`url` = '".mysqli_real_escape_string($connection,$data['url'])."',
id = LAST_INSERT_ID(id)
Then you can use mysqli_insert_id() regardless of if it was an UPDATE or INSERT
For example, if I run (with a record of id=2 exists; so we'll update);
INSERT INTO foobar (id, foo) VALUES (2, 'bar') ON DUPLICATE KEY UPDATE foo = 'baz', id = LAST_INSERT_ID(id);
SELECT LAST_INSERT_ID();
The output is 2, as that was the last insert id.

PHP check if MYSQL query was an insert or update

I have a simple MYSQL query:
INSERT INTO table (col1,col2) VALUES ('1','2')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2'
I use PHP PDO statements to query the database. Is there a way to know if the query executed resulted into a new inserted row or an existing was updated?
One way to do so is to get the number of rows before executing the query, then get the number of rows after executing the query, if they're not equal, it means a new row was inserted and if they are equal, it means a row was updated.
$sql = "SHOW TABLE STATUS LIKE 'TABLE_NAME'";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_before = $row['Rows'];
// Do your query here, afterwards
$stmt = $pdo->prepare($sql);
$stmt->execute();
$row = $stmt->fetch();
$number_of_rows_after = $row['Rows'];
// If condition
if($number_of_rows_before == $number_of_rows_after) // Update was executed
else // a new row was inserted.
Just use mysqli_affected_rows,it returns the number of rows affected by the last INSERT, UPDATE, REPLACE or DELETE query.
From PHP documentation:
In the case of "INSERT ... ON DUPLICATE KEY UPDATE" queries, the return value will be 1 if an insert was performed, or 2 for an update of an existing row.
see https://www.php.net/manual/en/function.mysql-affected-rows.php
From Mysql manual:
"With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if
the row is inserted as a new row and 2 if an existing row is updated."
See: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
This is the most reliable way to do it.
maybe you put the answer right into the query like:
INSERT INTO table (col1,col2, col_type) VALUES ('1','2','inserted')
ON DUPLICATE KEY UPDATE col1 = '1', col2 = '2', col_type = 'updated'

How to get currently inserted AUTO_INCREMENT id?

I have following table structure.
I want to insert the same value for item_image_name as the value of item_id(this is AUTO_INCREMENT) at the time of inserting a new record.
EDIT
I want this at the time of insertion
INSERT INTO items(user_id, item_image_name) VALUES ('','LAST_INCREMENT_VALUE + 1')
You can achieve this by defining a before insert trigger on the table.
In the body, first you can find next auto increment value to be assigned to primary key field and use the same for image_name field.
Example:
delimiter //
drop trigger if exists bi_on_items //
create trigger bi_on_items before insert on items
for each row
begin
if NEW.item_image_name is null OR length( trim( NEW.item_image_name ) ) = 0 then
set NEW.item_image_name := ( SELECT AUTO_INCREMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'items'
AND TABLE_SCHEMA = DATABASE() );
end if;
end;
//
delimiter ;
You can use mysql_insert_id() to get the last inserted id on the current connection.
mysql_insert_id — Get the ID generated in the last query
$id = mysqli_insert_id();
This should be used instead of mysql_insert_id() since on PHP 5.5 native MySQL driver has been deprecated.
Short answer: You can't. The auto increment ID is generated only upon successful insert. So you won't have access to it during the insert process.
The alternative would be to make an insert first and then get the last id by using mysql_insert_id() and then update the same row using the retrieved id.
PS: You should stop using mysql_* functions as are officially deprecated post v5.5. Switch to PDO or mysqli as soon as you can.
You can try like this:
first insert then get last inserted id and update value of item_image_name
$mysqli = new mysqli("localhost", "my_user", "my_password", "db");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "your insert query";// insert with all fields and set item_image_name to empty
if( $mysqli->query($query)){
$id= $mysqli->insert_id;
$query = "UPDATE your_table set item_image_name= '$id' where item_id = $id LIMIT 1 ";//now update the value of item_image_name
$mysqli->query($query);
}
USE MYSQLI_* or PDO do not use mysql_* since mysql_* is deprecated.
use
$sid=mysql_insert_id();
I you insert the first row the $sid should be 1
To insert next use $sid+1
Edit
For your question: write a query to get the last item_id and use that id + 1 to fill the current item_image_name.
Note: for the new item item_id and item_image_name should same.
You can use
SHOW TABLE STATUS LIKE 'tablename' to get the next auto incremented id.
$result = mysql_query("SHOW TABLE STATUS LIKE 'table_name'");
$row = mysql_fetch_array($result);
$nextId = $row['Auto_increment'];
and then use the variable $nextId in your insert query
INSERT INTO items(user_id, item_image_name) VALUES ('',$nextId)
What mysql driver are you using?
If you are using mysql driver then you will get the last inserted id with mysql_insert_id();
for PDO driver: PDO::lastInsertId();
Note: mysql driver is deprecated from PHP 5.5 (http://php.net/manual/en/migration55.deprecated.php)
Get last inserted id first.
select item_id as lastId from items order by id desc limit 0,1
Now use lastId in your query.
INSERT INTO items(user_id, item_image_name) VALUES ('','lastId+1')
Hope this helps.
You can use an insert into select query
INSERT INTO items(user_id, item_image_name) VALUES ('','SELECT MAX(item_id)+1 FROM items')

How do I get all the ids of the row created by one multiple row insert statement

I'm new to php. So, please forgive me if this seems like a dumb question.
Say i have a MySQL insert statement insert into table (a,b) values (1,2),(3,4),(5,6). table 'table' has a auto increment field called 'id'.
how can I retrieve all the ids created by the insert statement above?
It will be great if i get an example that uses mysqli.
You can't. I would suggest that you maintain your own ids (using guid or your own auto-increment table) and use it when you insert into the table.
But it's possible to get the auto-increment value for the last inserted using LAST_INSERT_ID():
http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
AngeDeLaMort's answer is almost right. Certainly, the most appropriate way to deal with the problem is to insert one row at a time and poll the insert_id or generate the sequence elsewhere (which has additional benefits in terms of scalability).
I'd advise strongly against trying to determine the last insert_id and comparing this the most recent insert_id after the insert - there's just too may ways this will fail.
But...an alternative approach would be:
....
"INSERT INTO destn (id, data, other, trans_ref)
SELECT id, data, other, connection_id() FROM source";
....
"SELECT id FROM destn WHERE trans_ref=connection_id()";
....
"UPDATE destn SET trans_ref=NULL where trans_ref=connection_id()";
The second query will return the ids generated (note that this assumes that you use the same connection for all 3 queries). The third query is necessary because connection ids to go back into the pool when you disconnect (i.e. are reused).
C.
In some cases, if you have another identifier of sort such as a UserID, you could filter your query by UniqueID's greater than or equal to mysql_insert_id(), limit by the number of affected rows and only display those by the user. This would really only work inside of a transaction.
$SQL = "INSERT INTO Table
(UserID, Data)
VALUES
(1,'Foo'),
(1,'Bar'),
(1,'FooBar')";
$Result = mysql_query($SQL);
$LastID = mysql_insert_id();
$RowsAffected = mysql_affected_rows();
$IDSQL = "SELECT RecordID
FROM Table
WHERE UserID = 1
AND RecordID >= '$LastID'
LIMIT '$RowsAffected'";
$IDResult = mysql_query($IDSQL);
as a follow up to AngeDeLaMort:
You could seperate your inserts and do it something like this:
$data = array (
array(1,2),
array(3,4),
array(5,6)
);
$ids = array();
foreach ($data as $item) {
$sql = 'insert into table (a,b) values ('.$item[0].','.$item[1].')';
mysql_query ($sql);
$id[] = mysql_insert_id();
}
Now all your new id's are in the $id array.
Maybe I can do this
$insert = "insert into table (a,b) values (1,2),(3,4),(5,6)";
$mysqli->query($insert);
$rows_to_be_inserted=3;
$inserted_id = $mysqli->insert_id // gives me the id of the first row in my list
$last_row_id = ($inserted_id+$rows_to_be_inserted)-1;
$mysql->query("select * from table where id between $inserted_id and $last_row_id");
what to you guys say?

Getting the final value to this MySQL query

I've got my database set up with three tables - code, tags, and code_tags for tagging posts.
This will be the SQL query processed when a post is submitted. Each tag is sliced up by PHP and individually inserted using these queries.
INSERT IGNORE INTO tags (tag) VALUES ('$tags[1]');
SELECT tags.id FROM tags WHERE tag = '$tags[1]' ORDER BY id DESC LIMIT 1;
INSERT INTO code_tags (code_id, tag_id) VALUES ($codeid, WHAT_GOES_HERE?)
The WHAT_GOES_HERE? value at the end is what I need to know. It needs to be the ID of the tag that the second query fetched. How can I put that ID into the third query?
I hope I explained that correctly. I'll rephrase if necessary.
Edit: Thanks for your help so far but I'm still struggling a bit in regards to what was pointed out - if it's already there I can't get the inserted ID...?
If you use INSERT IGNORE and a new record is ignored (because of a unique key violation) mysql_insert_id() and LAST_INSERT_ID() don't have a meaningful value.
But you can use INSERT ... ON DUPLICATE KEY UPDATE and LAST_INSERT_ID(expr) to set the data you expect LAST_INSERT_ID() to return in case of a doublet.
Step-by-step:
Let's assume you have a table tags like
CREATE TABLE tags (
id int auto_increment,
tag varchar(32),
dummy int NOT NULL DEFAULT 0, /* for demo purposes only */
primary key(id),
unique key(tag)
)
Inserting a tag twice results in a duplicate key violation because of unique key(tag). That's probably the reason why you've used INSERT IGNORE. In that case MySQL ignores the violation but the new record is ignored as well. The problem is that you want the id of the record having tag='xyz' regardless of whether it has been newly created or it was already in the database. But right now mysql_insert_id()/LAST_INSERT_ID() can oly provide the id of a new record, not an ignored one.
With INSERT ...ON DUPLICATE you can react on such duplicate key violations. If the new record can be inserted (no violation) it behaves like a "normal" INSERT. But in case of a duplicate key violation the part after ON DUPLICATE KEY is executed like an UPDATE statement for the record with that particular index value already existing in the table. E.g. (with an empty table tags)
INSERT INTO tags (tag) VALUES ('tag A') ON DUPLICATE KEY UPDATE dummy=dummy+1
This will simply insert the record as if there was no ON DUPLICATE ... clause. id gets the next auto-increment value, dummy the default value of 0 and tag='tag A'. Let's assume the newly create auto_increment value was 1. The resulting record stored in MySQL is (id=1, tag='tag A', dummy=0) and LAST_INSERT_ID() will return 1 right after this query. So far so good.
Now if you insert the same record again with the same query a violation occurs because of the first record (id=1, 'tag=tag A', dummy=0). For this already exisitng record the UPDATE statement after ON DUPLICATE KEY is executed, i.e. the record becomes (id=1, tag='tag A', dummy=1). But since no new record has been created there was also no new auto_increment value and LAST_INSERT_ID() becomes meaningless. So still the same problem as with INSERT IGNORE.
But there is a "special" construct that allows you to set the value LAST_INSERT_ID() is supposed to return after the ON DUPLICATE KEY UPDATE statement has been executed.
id=LAST_INSERT_ID(id)
Looks strange but it really only sets the value LAST_INSERT_ID() will return.
If you use the statement
INSERT INTO
tags
(tag)
VALUES
('xyz')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
LAST_INSERT_ID() will always return the id of the record having tag='xyz' no matter if it was added by the INSERT part or "updated" by the ON DUPLICATE KEY part.
I.e. if your next query is
INSERT INTO
code_tags
(code_id, tag_id)
VALUES
(4711, LAST_INSERT_ID())
the tags.id for the tag 'xyz' is used.
The self-contained example script uses PDO and prepared statements. It should do more or less what you want to achieve.
$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// set up temporary table and demo data
$pdo->exec('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))');
$pdo->exec('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)');
$pdo->exec("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')");
// prepare the statements
// set id=LAST_INSERT_ID(id), so LAST_INSERT_ID() gets a value even if the record is "ignored"
$stmtTags = $pdo->prepare('
INSERT INTO
tmpTags
(tag)
VALUES
(:tag)
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
');
$stmtTags->bindParam(':tag', $tag);
$stmtCodeTags = $pdo->prepare('INSERT INTO tmpCode_tags (code_id, tag_id) VALUES (:codeid, LAST_INSERT_ID())');
$stmtCodeTags->bindParam(':codeid', $codeid);
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// process (test)data
foreach($testdata as $data) {
// the parameter :codeid of $stmtCodeTags is bound to $codeid; assign it the "current" value
$codeid = $data['codeid'];
// split the tags
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// the parameter :tag is bound to $tag
// nothing more to do than to execute the statement
$stmtTags->execute();
// the parameter :codeid is bound to $codeid which was set to $codeid=$data['codeid']
// again nothing more to do than to execute the statement
$stmtCodeTags->execute();
}
}
unset($stmtTags);
unset($stmtCodeTags);
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
foreach( $pdo->query($query, PDO::FETCH_NUM) as $row ) {
echo join(', ', $row), "\n";
}
prints
1, tagA
1, tagC
2, tagC
2, tagD
2, tagE
edit2: In case the PDO-part of the script and the prepared statements are intimidating, here's the same thing using the old php-mysql module. But I urge you to use parametrized prepared statements. Doesn't have to be PDO but I happen to like it. E.g. the mysqli module provides prepared statements as well, the old mysql module doesn't.
$mysql = mysql_connect('localhost', 'localonly', 'localonly') or die(mysql_error());
mysql_select_db('test', $mysql) or die(mysql_error());
// set up temporary table and demo data
mysql_query('CREATE TEMPORARY TABLE tmpTags (id int auto_increment, tag varchar(32), primary key(id), unique key(tag))', $mysql) or die(mysql_error());
mysql_query('CREATE TEMPORARY TABLE tmpCode_tags (code_id int, tag_id int)', $mysql) or die(mysql_error());
mysql_query("INSERT INTO tmpTags (tag) VALUES ('tagA'), ('tagB')", $mysql) or die(mysql_error());
// and some new records we want to insert
$testdata = array(
array('codeid'=>1, 'tags'=>'tagA tagC'), // tagA is already in the table "tags", tagC is a "new" tag
array('codeid'=>2, 'tags'=>'tagC tagD tagE') // tagC will already be inserted, tagD and atgE are "new"
);
// "prepare" the statements.
// This is nothing like the server-side prepared statements mysqli and pdo offer.
// we have to insert the parameters into the query string, i.e. the parameters must
// be escaped so that they cannot mess up the statement.
// see mysql_real_escape_string() for string literals within the sql statement.
$qsTags = "
INSERT INTO
tmpTags
(tag)
VALUES
('%s')
ON DUPLICATE KEY UPDATE
id=LAST_INSERT_ID(id)
";
$qsCodeTags = "
INSERT INTO
tmpCode_tags
(code_id, tag_id)
VALUES
('%s', LAST_INSERT_ID())
";
foreach($testdata as $data) {
// in this example codeid is a simple number
// let's treat it as a string literal in the statement anyway
$codeid = mysql_real_escape_string($data['codeid'], $mysql);
$tags = explode(' ', $data['tags']);
foreach($tags as $tag) {
// now $tag is certainly a string parameter
$tag = mysql_real_escape_string($tag, $mysql);
$query = sprintf($qsTags, $tag);
mysql_query($query, $mysql) or die(mysql_error());
$query = sprintf($qsCodeTags, $codeid);
mysql_query($query, $mysql) or die(mysql_error());
}
}
// let's see what we've got
$query = '
SELECT
ct.code_id, t.tag
FROM
tmpCode_tags as ct
JOIN
tmpTags as t
ON
ct.tag_id=t.id
';
$result = mysql_query($query, $mysql) or die(mysql_error());
while ( false!==($row=mysql_fetch_row($result)) ) {
echo join(', ', $row), "\n";
}
If I understand what you're attempting to achieve correctly, the second query is un-necessary - use mysql_insert_id to obtain the ID of previously inserted row, which is I presume what you need for "WHAT_GOES_HERE".

Categories