Get array of Last Inserted Ids mysql php - php

I have a query like
Insert into tbl(str)values('a'),('b'),('c')
if i had a single insert then by using mysqli_insert_id($con) i could get last id inserted but how get all ids inserted in this multiple insert query?

This behavior of last_insert_id() is documented in the MySQL docs:
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 you really need it you can test it using foreach with array_push
<?php
$InsetQueryArray = array(
"Insert into tbl(str) values('a')",
"Insert into tbl(str) values ('b')",
"Insert into tbl(str) values('c')"
);
$allLasIncrementIds = array();
foreach ($InsetQueryArray as $value) {
//execute it mysql
//Then use array_push
array_push($allLastIncrementIds, mysqli_insert_id($con));
}
?>

If it is just for a few rows, you could switch to sending individual inserts with last_insert_id(). Otherwise it will slow down your application notably. You could make a marker for those bulk inserts, which gets set to a number identifying this bulk insert at the bulk insert itself and you can fetch those ids later on:
insert into tbl (stuff, tmp_marker) values ("hi",1715),("ho",1715),("hu",1715);
select group_concat(id) from tbl where tmp_marker = 1715;
update tbl set tmp_marker=0 where tmp_marker=1715;
If those bulk inserts have a meaning, you could also make a table import_tbl with user and time and filename or whatever and keep the 1715 as reference to that import.
EDIT: After discussion, I would go to an import-table
CREATE TABLE import (id int(11) not null auto_increment primary key, user_id int(11), tmstmp timestamp);
When an import starts, insert that:
INSERT INTO import set user_id = $userId;
in php:
$importId = last_insert_id();
$stmt = "insert into tbl (str, import_id) values ('a',$import_id), ('b', $import_id),('c',$importId);
Then you can do whatever you want with the id of your recently imported rows.
I have not made research if a multi-row-insert is guaranteed to lead to a consecutive row of IDs, as in a combination of last_insert_id() and num_rows is presupposed. And if that stays so, even when MySQL increases parallelization. So I would see it as dangerous to depend on it.

Related

PHP MySQL Insert Into & On Duplicate Key Problems

Is my code/syntax wrong? I'm not sure what is wrong and am new to this. I have created a table in PHPMyAdmin. It has two columns. One is "id" and is the primary key/auto-increment. The other column is "steamname".
This code is supposed to take a person's online name and enter it into the database. If there is already a record, it should update it anyways with the same/latest name.
The name of the table in phpmyAdmin is names
<?php
// Capture person's name from XML file
$profile = simplexml_load_file('UrlGoesHere.Com/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA);
echo (string)$profile->steamID;
//Enter name into databse and overwrite it if same/duplicate
$con=mysqli_connect("localhost","username","password","databaseName");
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
// Perform queries
mysqli_query($con,"INSERT INTO names (steamname) VALUES ('$profile') ON DUPLICATE KEY UPDATE steamname = VALUES('$profile')");
mysqli_close($con);
?>
I tested this by manually changing the value of a row in "steamname" within PHPMyAdmin to "woogy" and then running this script to see if it would update that database value... nothing is happening. It should update "woogy" to the proper name.
-----------Updates---------------
Hi all. Thank you for the input. I now have my code as follows. Sorry if it's still wrong. I'm learning.
<?php
$profile = simplexml_load_file('http://steamcommunity.com/profiles/76561198006938281/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA);
echo (string)$profile->steamID;
$con=mysqli_connect("localhost","userHere","passwordHERE","DBName");
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
// Perform queries
mysqli_query($con,"INSERT INTO names (steamname) VALUES ('$profile') ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)");
mysqli_close($con);
?>
This is how the database looks before running the script: (woogy should change to Chatyak)
Click image here
Now, when I run the PHP page, this is what happens to my database.
How database looks after running script
Not sure why it didn't update - and also why there is such a huge space?
Right syntax of INSERT ... ON DUPLICATE KEY would be like below, you need to mention the column name in VALUES like VALUES(column_name) instead of VALUES('$profile'). Also, you are missing the PK column ID in case of UPDATE. Cause you want to update the steamname for particular ID value.
INSERT INTO names (steamname)
VALUES ('$profile')
ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)
(OR)
INSERT INTO names (steamname)
VALUES ('$profile')
ON DUPLICATE KEY UPDATE steamname = VALUES(steamname)
Quoted from MySQL documentation
If a table contains an AUTO_INCREMENT column and INSERT ... UPDATE
inserts a row, the LAST_INSERT_ID() function returns the
AUTO_INCREMENT value. If the statement updates a row instead,
LAST_INSERT_ID() is not meaningful. However, you can work around this
by using LAST_INSERT_ID(expr).
You can't use INSERT ON DUPLICATE KEY UPDATE (IODKU) to update instead of inserting a new row unless you try to insert a value that conflicts with an existing primary or unique key column.
Since you're not specifying any value for ID in this INSERT, it will always increment the auto-increment primary key and insert a new row.
If you want the INSERT to replace the steamname for an existing row, you must specify the ID value in your INSERT.
Re your first comment:
I looked at your samples. It's not surprising that it created a new row. It generated a new auto-increment ID value because you didn't specify an ID in your INSERT. So it naturally created a new row. It has no way of telling which row you are trying to replace.
Here's a demo:
mysql> create table names (id serial primary key, steamname text);
mysql> insert into names (steamname) values ('Woogy')
on duplicate key update ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname);
Ignore the ID = LAST_INSERT_ID(ID) part, this has no effect. It's a no-op. #Rahul suggested it, but unfortunately he or she is mistaken. I'm going to take out that term in subsequent tests.
So what happens in this INSERT? You specify a value for steamname, but no value for ID. So MySQL generates a new value for ID. That becomes the primary key value for a new row, and that row is inserted with the steamname 'Woogy'.
mysql> select * from names;
+----+-----------+
| id | steamname |
+----+-----------+
| 1 | Woogy |
+----+-----------+
Next we try to change the name to 'Chatyak':
mysql> insert into names (steamname) values ('Chatyak')
on duplicate key update steamname = VALUES(steamname);
Which row does this apply the change to? The INSERT does not specify an ID value, so MySQL auto-increments another new ID value. That value is the primary key for a new row, and that's the row that gets the steamname 'Chatyak'.
mysql> select * from names;
+----+-----------+
| id | steamname |
+----+-----------+
| 1 | Woogy |
| 2 | Chatyak |
+----+-----------+
What this means is that you can never trigger the ON DUPLICATE KEY part if you let the auto-increment generate a new ID value every time you INSERT. Every INSERT will result in a new row.
As I said above, ON DUPLICATE KEY does nothing unless you try to insert a value that conflicts with a primary or unique key for a row that already exists in the table. But in this case you aren't conflicting, you're always generating a new ID value. So it inserts a new row.
If you had a UNIQUE KEY constraint on steamname, then that would be another opportunity for triggering ON DUPLICATE KEY. But it still won't do what you want.
mysql> alter table names add unique key (steamname(20));
Then if you try to insert the same steamname as one that already exists, it will not insert a new row. It'll update the existing row.
mysql> insert into names (steamname) values ('Chatyak')
on duplicate key update ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname);
Query OK, 0 rows affected (0.02 sec)
Note it says "0 rows" were inserted by that statement.
But this still doesn't allow you to change an existing steamname. If you specify a new name, it'll just insert a new row. If you specify the name that's already in use, it's a duplicate so it won't insert a new row, but it won't change the name either.
How do you expect the INSERT statement to apply to an existing row if you let it auto-increment a new ID value? Which existing row do you think it should conflict with?
Re your second comment:
You don't need a secondary unique key, I'm just trying to show you how IODKU works.
Before thinking about the SQL syntax, you have to think about the logic of the problem. In other words, if you specify a new name, which existing row do you want it to replace?
For example:
mysql> insert into names (id, steamname) values (123, 'Chatyak')
on duplicate key update steamname = VALUES(steamname);
This would work, because if a row exists with ID 123, this INSERT will cause a duplicate key conflict and therefore the ON DUPLICATE KEY clause will be triggered. But where did we get the value 123 in this example? Presumably in your application, you know what id you think you're updating. Is there a variable for that? Does it correspond to the user's session data?
**************************Edit:*************************
In response to the answer provided by #Rahul. This statement:
INSERT INTO names (steamname)
VALUES ('$profile')
ON DUPLICATE KEY UPDATE ID = LAST_INSERT_ID(ID), steamname = VALUES(steamname)
is technically valid but will always affect exactly 0 rows no matter what. Actually there are so many things wrong with it, that it is difficult to enumerate them all (which is the reason for this edit).
The usage of ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id) given in MySQL docs at the bottom of this page is an example of how to capture the id value of a row that has been updated i.e not inserted, since otherwise LAST_INSERT_ID() (with no argument) would return the last inserted id, which in the case of an update would not be meaningful in terms of the row in question.
So the above statement, if it ever worked — though it can't — would be effectively no different from this statement:
INSERT INTO names (steamname)
VALUES ('$profile')
ON DUPLICATE KEY UPDATE steamname = VALUES(steamname)
Except that you would also be updating that row's id value to the value it already was.
Aside from that, the reason the statement can never work is because the ON DUPLICATE KEY UPDATE clause will never be evaluated unless there is a duplicate key. And the same error that would occur without the ON DUPLICATE KEY UPDATE clause, will occur when we try to update steamname to equal the value of steamname i.e. the same value that tripped the ON DUPLICATE KEY UPDATE clause in the first place.
*****************************END OF EDIT***********************************
First: $profile is still an instance of SimpleXMLElement object when you try to use it in your query. So the SimpleXMLElement::__toString method is called, but SimpleXMLElement::__toString method only returns text content from directly inside the first element, and not text from any descendants. So if you have some element <steamname> and some element <steamID> nested within the parent element returned by your call to simplexml_load_file, their values will not be returned. See manual.
Second: when using the VALUES function with an UPDATE clause, the correct syntax is to refer to a column name/s like so DUPLICATE KEY UPDATE colname = VALUES(some_col_name). If you want to use an explicit value the syntax is DUPLICATE KEY UPDATE colname = 'value'. See manual.
Third: since you have id set to AUTO_INCREMENT, any rows inserted into the names table without explicitly inserting an id value or using a WHERE clause to specify an id value will create a new row with a new auto incremented id value, regardless of whether you use DUPLICATE KEY UPDATE or not because AUTO_INCREMENT will ensure that you never generate a duplicate key.
I think you may be able to make use of mysql's REPLACE statement instead. It's the simplest query that works for your described needs.
REPLACE INTO `names` (`id`,`steamname`)
VALUES ($id, $steamname)
where $id is $profile->steamID, and $steamname is the new(or not new) value for that row's steamname column.
The REPLACE statement will simply DELETE and then INSERT where an id value already exists. But since you're inserting the same id the effect is of updating the value of steamname in the row that contains a matching id value. Just be aware that if you have other columns besides id and steamname in the same table and you don't include their values in the REPLACE statement's VALUES then those values will be lost.
Fourth: for debuging these kinds of things its important to a) know what values you're actually passing into your query string. b) know that the basic query syntax you're using is correct, apart from any specific values you are(or aren't) using with it.
And finally: make things easy on yourself; since you're already using object notation with SimpleXMLElement to access element values, why not use it with mysqli also? Try this. It is UNTESTED, but it think it will work for you.
<?php
// Capture person's name and id from XML file
$profile = simplexml_load_file('UrlGoesHere.Com/?xml=1', 'SimpleXMLElement', LIBXML_NOCDATA);
$profile_id = (string)$profile->steamID;
$profile_name = (string)$profile->steamName;
//connect to database
$mysqli = new mysqli("localhost","username","password","databaseName");
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//build query
//IMPROTANT: this will delete other fields(if any)
//> in the row, unless they are also refilled by this statement
$q = ("
REPLACE INTO `names` (`id`,`steamname`)
VALUES (?, ?)
");
//uncomment to debug query
//echo "<pre>$q</pre>";
//uncomment to debug values
//echo "<pre>profile_name: \n $profile_name</pre>";
//echo "<pre>profile_id: \n $profile_id</pre>";
//prepare statement using above query
$stmt = $mysqli->prepare($q);
//fill in '?'s with actual values
//first argument is the data types
//'i' [integer] 's' [string]
//follownig aruments fill in '?'s in order
$stmt->bind_param('is', $profle_id, $profile_name);
// execute statement
$stmt->execute();
//close statement
$stmt->close();
//close connection
$mysqli->close();
?>

Inserting Into a Row and Then Selecting from that Row in One Query

I am working on a web development project that requires PHP and a MYSQL database. I am a bit of a MYSQL newbie. In one php file, I am inserting a few data into a row of a table. This data alone cannot identify the row, but an auto-incremented key is automatically generated which can identify the row. Is there any way to insert this row and select from it in the same query so it doesn't have to be identified with WHERE statement? Any other solutions are very welcome.
Thanks,
Michael
Use this code
// insert a datarow, primary key is auto_increment
// value is a unique key
$query = "INSERT INTO test (value) VALUES ('test')";
mysql_query( $query );
echo 'LAST_INSERT_ID: ',
mysql_query( "SELECT LAST_INSERT_ID()" ),
'<br>mysql_insert_id: ',
mysql_insert_id();
As an alternative you could get the last id with mysql_insert_id php function.
Note that this function is deprecated but you could find alternatives in the link below...
http://www.php.net//manual/en/function.mysql-insert-id.php

How can I query for a primary key immediately after inserting a record?

Using two tables in my database and splitting one HTML form's data into two different queries, I've run into an issue identifying the primary key from the first query for use as a foreign key in the second query. Here is what I'm talking about in pseudocode:
$FormData1, $FormData2, ... INSERT INTO table1 //primary key is auto-generated int
$key = SELECT primaryKey FROM table1 WHERE *** Uh-oh!
$otherFormData1, $otherFormData2, $key ... INSERT INTO table2
Given that the system needs to be designed so that concurrent transactions can occur, how do I solve this problem? If I knew it was only ever going to be run one instance at a time then I could just query for the last record added, but I don't feel like that is a very 'tidy' (or even safe) solution to my problem.
You can query for LAST_INSERT_ID(), but there are the mysqli::insert_id and PDO::lastInsertId methods that you can use. Note that these are per connection, so even if you have many inserts that take place from separate requests simultaneously it is safe to query.
mysql and mysqli after firing an insert-sql have an $insert_id of the record that has just been inserted - you do not need to query your db.
You could use this:
$sql = 'INSERT INTO table1 (field1, field2) VALUES ("aaa", "bbb")';
$mysqli = new mysqli();
$mysqli->query($sql);
$pKey = $mysqli->insert_id;
and now your $pKey has a value of id of the row that you just inserted.
(see here for more: http://pl1.php.net/manual/en/mysqli.insert-id.php)

Check with sql if two tables are unique PHP

How can I check in MYSQL PHP if two columns are unique then not insert again, else if just one column is unique then insert, is that even possible to do in php?
EDIT:
Lets say I have a table like this,
userId | codeId
And I I send a query like this,
$query = $pdo->prepare('insert into table (userId, codeId) values (?,?)');
So now I want to check if userId and codeId are added already once do not insert again, and if just one is added, then do insert the entire query,
I hope its more understanding.
Set up a unique key for those columns, then the mysql query will FAIL when you try to insert.
Use REPLACE INTO instead of INSERT INTO ... ?
Do something like the code below (where TEXT_ID and TEXT_CATEGORY, are keys of table):
INSERT INTO
table_texts
SET
text_id = 174,
text_category = "pam_texto"
ON DUPLICATE KEY UPDATE
text_id = 174,
text_category = "pam_texto";
The above code tries to insert, but if the keys are duplicated performs an update on the line.

Can I avoid to overwrite a column with a NULL value on INSERT query?

I have two different queries in my php page for the same table, the first one is executed if I have two different values for two columns, but in some case, i can use only the first value, can I do it with the same query or should I use two different queries?
// query 1
"INSERT INTO my_table (column_1, column_2) VALUES ('$value_1', '$value_2')";
// second query, used if $value_2 is null
"INSERT INTO my_table (column_1) VALUES ('$value_1')";
// can I do something like this without use a SELECT for column_2 before the INSERT?
$value_2 = null;
"INSERT INTO my_table (column_1, column_2) VALUES ('$value_1', '$value_2')";
// ======================================= new value === ^ | ^ === mantain old value because it's null
so can I execute an INSERT statement with new vals without overwrite the old vals with a null value?
An INSERT will never overwrite an old record (though it could fail if you try to insert a record that breaks a unique key constraint). So you can just use the first query, even if $value_2 is null, and get the same results.
If you want to overwrite records, you will need to use an UPDATE statement. In that case you could overwrite only a single column, if you wanted to. See http://dev.mysql.com/doc/refman/5.0/en/update.html for the UPDATE syntax.
There is also REPLACE to overwrite old rows in the case of unique keys, but it sounds like this is the opposite of what you want to do.

Categories