I have a mysql table which has a trigger attached, that logs changes in this table to a second one
CREATE TRIGGER log_table BEFORE UPDATE ON table1
FOR EACH ROW BEGIN
INSERT INTO log_table(filed) VALUES(NEW.field);
END;
//
Now if I perform an INSERT INTO table1 from PHP an call mysqli_insert_id() afterwards.
Would that return the new ID in table1? Or the new ID in log_table?
LAST_INSERT_ID() called after the INSERT statement will return the new ID in table1 (of the INSERT statement), not the one in the log_table (of the trigger).
This is documented in the manual section of LAST_INSERT_ID:
Within the body of a stored routine (procedure or function) or a
trigger, the value of LAST_INSERT_ID() changes the same way as for
statements executed outside the body of these kinds of objects. The
effect of a stored routine or trigger upon the value of
LAST_INSERT_ID() that is seen by following statements depends on the
kind of routine:
If a stored procedure executes statements that change the value of
LAST_INSERT_ID(), the changed value is seen by statements that follow
the procedure call.
For stored functions and triggers that change the value, the value is
restored when the function or trigger ends, so following statements
will not see a changed value.
Related
Using PHP with MySQL and PDO prepared statements, I would like to mimic an opaque id in a simple, safe and efficient way. The idea is to add a random value to the current value of LAST_INSERT_ID.
INSERT INTO table
SET id = LAST_INSERT_ID( LAST_INSERT_ID() + FLOOR(1 + (RAND() * 99)) ),
text = ?,
...
The outer expression sets the id, the inner call gets the current value and modifies it. This works as expected when I test it with the Sequel Pro client. However, in the real script with a prepared statement the last insert id does not get updated/saved. So, I do get random ids, but they are not added up.
What am I missing?
This really does not work, and here's why:
LAST_INSERT_ID() returns 0 on the first call per connection because there is no last insert. Unfortunately, it does not just return the potential next AUTO_INCREMENT value.
If you would split it up into separate commands, first INSERT and then UPDATE within the same statement, LAST_INSERT_ID() would not get updated in between.
More over, "if you mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the effect is undefined" Manual vs. Manual
UPDATEd ids will be ignored by LAST_INSERT_ID()
I try to insert two values into my database with execute block and return inserting the id.
execute block returns (id integer)
as begin
insert into test (name) values ('test1') returning id into :id;
suspend;
insert into test (name) values ('test2') returning id into :id;
suspend;
end;
If I don't call fetch|fetchAll methods the insert isn't persisted in the database.
I can't call fetchAll after query executing for multiple read of result set.
But if I call there, the insert is persist and I can get returned values.
You must fetch all rows produced by the execute block, for those statements to be executed. An execute block is an anonymous 'stored' procedure, and a suspend statement makes it 'selectable`.
When executed, Firebird does the work up to a suspend statement, waits for the row to be fetched by the client and then proceeds with the next statements until it hits another suspend statement.
If a row is not fetched by the client and the statement is closed / released, then Firebird will roll back all changes until the previous suspend (or the start of stored procedure / execute block if there is no previous suspend).
In other words, retrieving the rows is required for the execute block to have any lasting effect.
Otherwise, it might be better to execute the inserts individually, and simply not use execute block.
I am writing my first sql trigger query.So i am trying to grasp the idea of trigger statement.My plan is to--
1.insert data to a table called 'trigger_table';
2.After insertion ,fetch data from that exact same table and store it to a php variable using PDO;
So i wrote the following trigger statement.And i get this error while executing sql in mysql workbench
ERROR 1415: Not allowed to return a result set from a trigger
SQL Statement:
CREATE TRIGGER `trigger_table_AINS` AFTER INSERT ON trigger_table FOR EACH ROW
-- Edit trigger body code below this line. Do not edit lines above this one
BEGIN
SELECT * FROM trigger_table;
END
It would be a great help if anyone guide me towards the right path to accomplish my task.Thanks !
php code:
$db=new PDO("mysql:host=localhost;dbname=trigger",'root','');
$sql="INSERT INTO trigger_table (name,email) VALUES('zami','alzami#gmail.com')";
$conn=$db->prepare($sql);
if($conn->execute()){
$result=$conn->fetchAll(PDO::FETCH_OBJ);
}
Triggers are meant to react to an event to update, modify or delete content. As the error states, they aren't meant to return data.
Per the documentation:
A trigger is a named database object that is associated with a table, and that activates when a particular event occurs for the table. Some uses for triggers are to perform checks of values to be inserted into a table or to perform calculations on values involved in an update.
You may be after a Stored Routine
Stored routines (procedures and functions) are supported in MySQL 5.7. A stored routine is a set of SQL statements that can be stored in the server. Once this has been done, clients don't need to keep reissuing the individual statements but can refer to the stored routine instead.
Wouldn't there be a problem with it if for example when a user clicks on a link, a new row is automatically inserted and then the php code requests the last inserted id, and at the same time another row is inserted by another user, so the returned id is actually not the one I'm expecting..?
Am I wrong? Is there a way to do the same without that 'security' hole?
(like maybe from within the prepared statement or something...)
P.S the id is automatically generated.
Thank you.
As mentioned in the manual:
LAST_INSERT_ID() (with no argument) returns a BIGINT (64-bit) value representing the first automatically generated value that was set for an AUTO_INCREMENT column by the most recently executed INSERT statement to affect such a column. For example, after inserting a row that generates an AUTO_INCREMENT value, you can get the value like this:
mysql>SELECT LAST_INSERT_ID();
->195
The currently executing statement does not affect the value of
LAST_INSERT_ID(). Suppose that you generate an AUTO_INCREMENT value
with one statement, and then refer to LAST_INSERT_ID() in a
multiple-row INSERT statement that inserts rows into a table with its
own AUTO_INCREMENT column. The value of LAST_INSERT_ID() will remain
stable in the second statement; its value for the second and later
rows is not affected by the earlier row insertions. (However, if you
mix references to LAST_INSERT_ID() and LAST_INSERT_ID(expr), the
effect is undefined.)
If the previous statement returned an error, the value of
LAST_INSERT_ID() is undefined. For transactional tables, if the
statement is rolled back due to an error, the value of
LAST_INSERT_ID() is left undefined. For manual ROLLBACK, the value of
LAST_INSERT_ID() is not restored to that before the transaction; it
remains as it was at the point of the ROLLBACK.
So, LAST_INSERT_ID() is always transaction-safe (even though you don't use transaction).
The MySQL Server transfers the insert ID as part of the OK message after a successful INSERT. This ID is stored in PDO, therefore without a round-trip to the server PDO can return you the correct ID for your connection in a safe way.
Reference: http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#OK_Packet
To counteract this you would use a transaction.
This would essentially isolate your insert from others, so as long as your Insert/lastInsertId() call is within the same transaction, it will work just fine.
If I execute an insert query with a stored procedure with php and the mysqli_* functions, is there a way to retrieve the value of an autoincrement id field?
mysqli->insert_id does not seem to work.
Are you sure the last query you preformed was an INSERT?
mysqli->insert_id seems the proper answer:
Return Values
The value of the AUTO_INCREMENT field that was updated
by the previous query. Returns zero if
there was no previous query on the
connection or if the query did not
update an AUTO_INCREMENT value.
You could try to make a query to MySql like so:
SELECT LAST_INSERT_ID()
Not sure if it works with stored procedures though.
You could add this statement in your stored procedure after the insert:
SET #saved_id = LAST_INSERT_ID()
Then, execute this query after calling the procedure:
SELECT #saved_id
mysqli->insert_id (where mysqli represents your database connection)
must be used directly after the insert. If you run other queries on the same connection
before attempting to read insert_id you get 0 returned.