I ran in to this problem in one of my live web apps. It seems that if you issue a multi-statement query to MySQL via PHP PDO, and the first statement is an insert statement, and the second statement is an update statement, the PDO::nextRowset() function doesn't return the right number of result sets. (Note that PDO supposedly supports multiple statements per MySQL query since PHP 5.3.)
Here's an example:
SQL:
create database `test`character set utf8 collate utf8_general_ci;
create table `test`.`testtable`( `id` int );
PHP:
<?php
$link = new \PDO('mysql:host=localhost;dbname=test', 'username', 'password');
//Run one of the 4 $handle assignments at a time (comment out all but one).
//Run #4 on an empty table to compare the results of #1 and #4.
//WORKS: INSERT, followed by SELECT, followed UPDATE
//Output:
//Rowset 1
//Rowset 2
//Results detected
$handle = $link->prepare(' insert into testtable(id) values(1);
select * from testtable where id = ?;
update testtable set id = 2 where id = ?;');
//WORKS: SELECT, followed by UPDATE
//Output:
//Rowset 1
//Results detected
$handle = $link->prepare('select * from testtable where id = ?;
update testtable set id = 2 where id = ?;');
//WORKS: UPDATE, followed by SELECT
//Output:
//Rowset 1
//Rowset 2
//Results detected
$handle = $link->prepare('select * from testtable where id = ?;
update testtable set id = 2 where id = ?;');
//DOESN'T WORK: INSERT, followed by UPDATE, followed by SELECT
//Output:
//Rowset 1
//Expected output: same as examples 1 and 3
$handle = $link->prepare('insert into testtable(id) values(1);
update testtable set id = 2 where id = ?;
select * from testtable where id = ?;');
$handle->bindValue('1', '1');
$handle->bindValue('2', '2');
$handle->execute();
$i = 1;
do{
print('Rowset ' . $i++ . "\n");
if($handle->columnCount() > 0)
print("Results detected\n");
}while($handle->nextRowset());
?>
Does anyone have any idea as to what I'm doing wrong? Why can't I put my select statement at the end?
PHP 5.3.5
MySQL 5.1.54
I submitted a PHP bug report about this problem and a possible patch has been submitted. So it looks like this was a PHP bug.
First , you have to figure out what will nextRowset() returns regarding of a non-select operation . I have tried to var_dump it after an update ,and I got bool(false), which can explain why you only get one output with :
$handle = $link->prepare('insert into testtable(id) values(1);
update testtable set id = 2 where id = ?;
select * from testtable where id = ?;');
Second, I replaced the following codes:
var_dump($h->nextRowset());
var_dump($h->nextRowset());
with your do-while segment,I got bool(false) bool(true), witch means that it just goes once in the loop.
I don't know whether it is right to explain your question as above, may it will help you.
Related
I am trying to create a query inside a PDO script that checks if a record exists if it does the query should update the record and if it doesn't exist it should create a new one.
The column that should only exist once in the table is not an INDEX key (cannot make it unique right now) so it is not set as unique and I cannot use the ON DUPLICATE KEY UPDATE
I would like to use this queries logic below to make it work:
$stmt = $conn->prepare('IF EXISTS (SELECT * FROM `Table1` WHERE `code`= :code )
UPDATE `Table1`
SET `code_stat` = 2
WHERE code = :code
ELSE
INSERT INTO `Table1` (`code`,`code_stat`)
VALUES (:code, 2 ) ' );
$stmt->execute([
'code' => $_POST['code']
]);
The problem is when executing the query I get the following error saying there is a syntax problem:
SQL syntax; check the manual that corresponds
to your MySQL server version for the right syntax to use near
'IF EXISTS (SELECT * FROM Table1 WHERE code= ? ) UPDATE Table1' at line 1
If you can't add a unique key to the table, you can attempt an update first, and if that doesn't update any rows, do an insert. Something like this:
$stmt = $conn->prepare('UPDATE `Table1` SET `code_stat` = 2 WHERE code = :code');
$stmt->execute(array(':code' => $_POST['code']));
if (!$stmt->rowCount()) {
// no rows updated, so insert
$stmt = $conn->prepare('INSERT INTO `Table1` (`code_stat`, `code`) VALUES (2, :code)');
$stmt->execute(array(':code' => $_POST['code']));
}
Note that you may need to set the PDO::MYSQL_ATTR_FOUND_ROWS attribute to ensure that the UPDATE query returns 1 if it finds the row but the value doesn't change. You must set that attribute when you make the connection e.g.
$conn = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_FOUND_ROWS => true));
Why not write a stored procedure to handle this, similar to the below:
DROP PROCEDURE IF EXISTS db.SP_NEW_CODE;
CREATE PROCEDURE db.`SP_NEW_CODE`(IN `in_code` INT)
BEGIN
DECLARE numFound INT DEFAULT 0;
SET numFound=(SELECT * FROM `Table1` WHERE `code`= in_code);
IF (numFound=0) THEN
INSERT INTO `Table1` (`code`,`code_stat`) VALUES (in_code, 2 );
ELSE
UPDATE `Table1` SET `code_stat` = 2 WHERE code = in_code
END IF;
END;
From your code, simple execute CALL SP_NEWCODE(3); (for example, where 3 is the appropriate code value).
I have updated my records based on specific condition after that I want to know the ids from the affected rows.
$sql = mysqli_query("update table set xxx='".$x."' where yyy='".$y."'");
Now after executing this query I want to know the affected rows.
Simple yet effective
$last_id = mysqli_insert_id($conn);
http://www.w3schools.com/php/php_mysql_insert_lastid.asp
You must first fetch the IDs, and then perform the update. If concurrency is a concern, you can use a locking read (provided that your table is stored in a transactional engine, such as InnoDB):
$mysqli->autocommit(FALSE);
$select = $mysqli->prepare('SELECT id FROM table WHERE yyy = ? FOR UPDATE');
$select->bind_param('s', $y);
$select->execute();
$update = $mysqli->prepare('UPDATE table SET xxx = ? WHERE yyy = ?');
$update->bind_param('ss', $x, $y);
$update->execute();
$mysqli->commit();
// here are the IDs that were updated
$select->bind_result($id);
while ($select->fetch()) printf('Updated id: %s\n', $id);
The only way I can think of is to first sleect rows that would be updated with the update statement, those are:
$updatableIds = mysqli_query("SELECT id FROM table WHERE xxx !='".$x."' AND yyy='".$y."'");
we add xxx !='".$x."' because if value of xxx already was $x those rows would not be affected.
Next you run the update
$sql = mysqli_query("update table set xxx='".$x."' where yyy='".$y."'");
UPDATE users
SET type = '3'
WHERE type = '2';
To find out the last affected row right after the statement, it should be slightly updated as follows:
UPDATE users
SET type = '3',
user_id=LAST_INSERT_ID(user_id)
WHERE type = '2';
// use function
function updateAndGetId($value)
{
$query ="UPDATE users
SET type = '$value',
user_id=LAST_INSERT_ID(user_id)
WHERE type = '2'";
mysql_query($query)
return mysql_insert_id();
}
$lastUpdatedRow = updateAndGetId(3);
In case you want to update only really changed row, add a conditional update of the user_id through the LAST_INSERT_ID and check if the data is going to change in the row.
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.
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';
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
MySQL trigger to update a field to the value of id
Please I have a table with three fields :
Id (auto increment)
GroupById
Text
And I want when inserting a new row : If I left the field groupById blank, it must get by default the same value of the field Id.
Please have you any Idea ? Thanks in advance.
Edit : My code :
mysql_query("INSERT INTO group SET GroupById = (must get the current Id), Text = 'bla bla bla' ");
How about a trigger:
DELIMITER $$
CREATE TRIGGER trg_yourtable
BEFORE INSERT ON yourtable
FOR EACH ROW
BEGIN
IF NEW.GroupById IS NULL THEN
SET NEW.GroupById = (
SELECT AUTO_INCREMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'yourtable'
);
END IF;
END $$
I'm not sure how safe this is... or what happens when you insert multiple rows from one query, or when two connections attempt to insert at the same time... but it works.
This simple SQL should do what you want:
INSERT INTO myTable (GroupById, Text) VALUES (NULL, 'your text');
SET #lastID = LAST_INSERT_ID();
UPDATE myTable SET GroupById = #lastID WHERE Id = #lastID;
Use a stored procedure
Get the MAX id
Add 1 to it and add insert it into both rows
You get the desired result in a single transaction.
Hope this helps.
Use this function | Tested
Example of use:
function get_current_insert_id($table)
{
$q = "SELECT LAST_INSERT_ID() FROM $table";
return mysql_num_rows(mysql_query($q)) + 1;
}
$txt = "text";
$groupID = '';
if ( empty($groupID) ) { $groupID = get_current_insert_id(test); }
$query = mysql_query("INSERT INTO test VALUES ('', '$groupID', '$txt') ");
if ( $query ) { echo 'Saved using ID:' . $groupID; } else { echo 'Oh noes!' . $query; }
Looks like you might need to run 2 queries:
insert into 'table' ('GroupById', 'Text') VALUES ('{group id}', '{sometext}')
update 'table' set GroupById = id where GroupById = null - it mightbe 0 instead of null depending on what you insert in db.
You can always optimize it through indexes, limits, order.