I have the following query:
$year = 2019;
$month = 6;
$stmt = $db->prepare('INSERT INTO officeRechNr (jahr,monat,zahl) VALUES (?,?,1) ON DUPLICATE KEY UPDATE zahl = LAST_INSERT_ID(zahl+1)');
$stmt->bind_param('ii', $year, $month);
$stmt->execute();
echo $db->insert_id;
echo '|';
$sql = 'SELECT LAST_INSERT_ID() as number';
$result = $db->query($sql);
$row = $result->fetch_assoc();
echo $row['number'];
echo '<br>';
The table officeRechNr has the unique primary index ['jahr','monat'] and zahl is an index with autoincrement.
If the table officeRechNr is empty, and I execute the code 3 times, then the output is
1|0
2|2
3|3
...
Why is LAST_INSERT_ID() zero after insert, but correct after upgrade?
How do I need to change my query, so that both functions output the same number (1) after insert?
Edit: The purpose of the code is that I need for each invoice that is created in a specific year and month a third unique ascending number. So for example if we have 7 invoices in the year 2015 and month May (3),then I would have the folloing numbers
2015-3-1
2015-3-2
2015-3-3
2015-3-4
2015-3-5
2015-3-6
2015-3-7
So in the row in the database I have stored the current invoice number and with the SQL command presented above I can get the next number. The only reason why the column zahl is an autoincrement field is that the number is returned by insert_id (see https://dev.mysql.com/doc/refman/5.7/en/getting-unique-id.html). Its also necessary to get it through insert_id in case that people create simultaneously invoices.
The problem is that LAST_INSERT_ID(...); with an argument doesn't return the generated ID but instead set the given value in the "memory" of LAST_INSERT_ID() and returns it. So, in your first execution no auto incremented ID was generated (you provided the value by yourself) and LAST_INSERT_ID() return 0. In your following executions you save the value next+1 in the internal storage of LAST_INSERT_ID(), which returns the value. This behavior is described in the MySQL in 12.14 Information Functions:
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().
In fact, you can skip the LAST_INSERT_ID() call and work without it.
INSERT INTO
officeRechNr (jahr,monat,zahl)
VALUES
(?,?,1)
ON DUPLICATE KEY UPDATE zahl = zahl+1
This will insert the row (with the given value) or increase the counter.
If you want the current counter for a given year and month you run a simple SELECT statement. Keep in mind that you might need transactions or locks because a different client could increase the counter before you fetched it with the SELECT statement.
Related
I'm trying to make a tracking system on my website that's very basic, with just the amount of people present recorded.
Unfortunately, the code below doesn't work. I checked the error logs in my server and basically here's the issue : After the first execute there are no entities found, even though there is already an entry with that value, and so the code goes straight to the "else" and then crashes because there is already an entry with that primary key. Can someone help me find why it doesn't find the entity on the first execute?
Here is the code :
$q = "SELECT date, amount FROM tracking WHERE date = ?";
$req = $bdd->prepare($q);
$req->execute(date("Y-m-d"));
$results = $req->fetchAll();
if (count($results) != 0){
$results["amount"] = $results["amount"] + 1;
$track = $bdd->prepare("UPDATE tracking SET amount = ? WHERE DATE(date) = ?");
$track->execute(array($results["amount"], date("Y-m-d")));
exit;
}
else{
$q = 'INSERT INTO tracking (date, amount) VALUES (:val1, :val2)';
$req = $bdd->prepare($q);
$req->execute(
[
"val1" => date(Y-m-d),
"val2" => 1,
]
);
}
Thanks
It looks like your tracking table must only have one row per date. There's a way to handle that directly in MySQL's query language.
First, make your date column the primary key of your table, or create a unique index on it. You create the unique index like this.
CREATE UNIQUE INDEX trackdate ON tracking(date);
Then use this single query to do your insertion / update.
INSERT INTO tracking (date, amount) VALUES (CURDATE(), 1)
ON DUPLICATE KEY UPDATE amount = amount + 1;
Each time you run this query it will either insert the necessary row, or increment the amount column. And it does it "atomically," meaning that if two different php program instances try to do it concurrently, it won't get confused.
Okay so this is my first question and I really have no idea how to ask it so I'm going to try and be as specific as possible. My website is an online game and for user inventories when it inserts a new item into the database
Table name "inventory"
Column names "inv_id", "inv_itemid", "inv_userid", "inv_qty"
and it does not add to the column inv_qty and populate properly instead it creates a new inv_id identifier and row for each item. I was wondering if there was a way for me to create a merge function via php to merge all items with the same inv_itemid and inv_userid while adding to the inv_qty colum and populating the inv_id
In my inventory.php file the inv_id column is used to let the user either equip the item or use it as the main variable.
I have seen this done and have tried many times and I just can't get it to work.
If it were a single key to check then you could have used 'ON DUPLICATE KEY UPDATE' of mysql like the following:
INSERT INTO table(field1, field2, field3, ..)
VALUES (val1, val2, val3, ...)
ON DUPLICATE KEY
UPDATE field3='*'
But in your case there is a combination to consider.
If "inv_id", "inv_itemid", "inv_userid" mathces then UPDATE, otherwise INSERT.
One way to achieve this using only mysql in a single query is to create & use a Stored Procedure.
But using php you can achieve this in 2 query. First query is to determine if the combination exists. Then based on this run the next Insert or Update query.
Please check the following example:
$sql1 = SELECT * FROM inventory WHERE inv_id='$inv_id', inv_itemid='$inv_itemid', inv_userid='$inv_userid'
// Execute $sql1 and get the result.
IF result empty, then INSERT:
$sql2 = INSERT INTO inventory ....
otherwise UPDATE.
$sql2 = UPDATE inventory SET inv_qty=(inv_qty + $update_qty) WHERE inv_id='$inv_id', inv_itemid='$inv_itemid', inv_userid='$inv_userid'
About:
Would there be a way to write a php function at the top of the inventory page for my users to click to merge them
Please check with the following php function.
By calling with param: UserID, it will create a new entry with sum of the inv_qty, for each (inv_itemid + inv_userid) combination and removes the previous duplicate entries of (inv_itemid + inv_userid) leaving the newly enterd: (inv_itemid + inv_userid + (SUM of inv_qty)).
Important, please keep a back up of the DB Table Data before running the function.
Please check the comments in the function and update where necessary based on your system, Like getting the last inserted inv_id.
function merger_fnc($user_id) {
// For Each Combination of: inv_itemid + inv_userid
// This function will Insert a new row in the inventory with the SUM of inv_qty
// And then will remove the previous single rows of: inv_itemid + inv_userid + inv_qty
// First get the distinct Items of the User(by UserID);
$inv_itemids = $db->query("SELECT DISTINCT(inv_itemid) FROM inventory WHERE inv_userid=".$user_id);
// Here $inv_itemids will hold all the distinct ItemIDs for the UserID;
foreach ($inv_itemids as $inv_item) {
// We will Insert A new row which will have the sum of 'inv_qty' for the inv_userid & inv_itemid;
$inv_itemid = $inv_item['inv_itemid'];
// I am not sure what type of result set your $db->query(...) returns. So I assumed it is associative array.
// If the result is an Array of objects, then please use: $inv_itemid = $inv_item->inv_itemid;
$insert_sql = "INSERT INTO inventory (inv_itemid, inv_userid, inv_qty) VALUES ('".$inv_itemid."', '".$user_id."', (SELECT SUM(inv_qty) FROM FROM inventory WHERE inv_userid=".$user_id."))";
$inv_itemids = $db->query($insert_sql);
$inserted_new_inventory_id = $db->insert_id;
// Please check the appropriate method for it in your $db class here.
// In mysqli, it is: mysqli_insert_id($db_conn); In PDO it is: $db_conn->lastInsertId();
// Last we remove the previous data of combination(inv_userid & inv_itemid) but leaving our last inserted row.
$delete_sql = "DELETE FROM inventory WHERE inv_id!='".$inserted_new_inventory_id."' AND inv_userid='".$user_id."' AND inv_itemid='".$inv_itemid."'";
$db->query($delete_sql);
}
}
If getting the last inserted inv_id is troublesome from $db(like inv_id is not defined as key in the table), you can try another approach:
Do another query and save the previous inv_id in an array, before the insertion.
After the insertion of the new entry with sum of qty, run a delete query to delete the previous single qty entries, like the following:
DELETE FROM inventory WHERE inv_id IN (3, 4, 7,...)
Here (3, 4, 7,...) are the previous inv_id for (inv_itemid + inv_userid) combination.
I want to update the same value in DB,
But the database returns 0 when I do that.
is it possible to return 1 or true? or force the DB update?
$sth =$pdo->prepeare("UPDATE table set x = :x where id=:id")
$sth->execute()
$result = $sth->rowCount() // that return 0;
x is the same value in DB and the result is 0 but i want to let the user to approve the form even with the same value
Updating with same values will always return false. You need to add some some distinct value that could be updated .
For example you can have field that stores timestamp (like updatedOn) so every time you pass same values for rest of field, it will be accepted as time stamp field will have unique value.
basically you will need to add a WHERE clause as you said you need to update some values
$sth =$pdo->prepeare("UPDATE table set x = :x where y = :y")
I have a Mysql table where pincode field cant be duplicate daily (Sequential increment id), also i cant apply the unique key on that field using Mysql indexing for some reason.
Using PHP i am trying as below, but my code will become endless if i have to keep increasing by checking them as below.
Is there any better way without Mysql indexing to do it from PHP (zend framework)?
$sql = "SELECT count(*) as total
FROM `sh_av_spform`
WHERE DATE(`createdate`) = CURDATE( )";
$result = $db->fetchAll($sql);
if(count($result)>0) {
$tmp_id = $result[0]['total'] +1;
$new_id = sprintf('%03d',$tmp_id); // 009
try{
$sql1 = "SELECT id,pincode
FROM `sh_av_spform`
WHERE DATE(`createdate`) = CURDATE() and pincode='$new_id' limit 1";
$result1 = $db->fetchAll($sql1);
if(count($result1)>0) {
// 009 already exist make it 010?
$tmp_id = $result[0]['total'] +2;
$new_id = sprintf('%03d', $tmp_id); // 010
}
// Ooopsssss! 010 also exist. now what?
// keep wrting code forever? or there is better way?
$db->insert('sh_av_spform', array('pincode'=>$new_id) );// Pincode cant be duplicated
You can do this entirely in database, using a counter table.
Example:
CREATE TABLE daily_pin (day DATE PRIMARY KEY, pin INT UNSIGNED);
START TRANSACTION;
INSERT INTO daily_pin VALUES (CURDATE(),1) ON DUPLICATE KEY UPDATE pin=LAST_INSERT_ID(pin+1);
INSERT INTO table_requiring_pin (pin) VALUES (LPAD(LAST_INSERT_ID(),3,'0'));
COMMIT;
Notes:
The counter table holds a given day's highest as yet used PIN.
The INSERT .. ON DUPLICATE KEY gets a new pin, either a "1" if it's the first entry for a given day, or the current value plus 1.
LAST_INSERT_ID, when given an argument, returns the argument and remembers it for the next time LAST_INSERT_ID is called without an argument.
Finally, left pad it with LPAD to get the "000" format you're wanting.
As a side benefit of this approach, you get easy metrics on pin usage. Like, "what day of the week consumes the most pin?"
You can create one separate function for checking pin code before you insert.
For example
public function ValidatePinCode($PinCode){
if(isset($PinCode)){
$SQL=$db->prepare("SELECT pincode FROM `sh_av_spform` WHERE pincode='".$PinCode."'");
$SQL=$db->execute($SQL);
if($SQL->fetchColumn()>0){
$ResponseCode='FALSE';
}else {
$ResponseCode='TRUE';
}
return $ResponseCode;
}
}
If you get FALSE response then do not allow to insert new pin code else you can perform INSERT query.
Let me know if you want even more explanation on this.
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';