MySQL set AUTO_INCREMENT value to MAX(id) + 1 shortcut? - mysql

I constantly need to "reset" the AUTO_INCREMENT value of my tables, after I delete a part of my rows. Let me explain with an actual example :
I have a table called CLIENT. Let us say before removing some rows, the auto_increment was set to 11. Then I delete the 4 lasts rows. The auto_increment is still set to 11. So when I will insert some clients again, it will make a hole of id.
I always need to "clean" the auto_increment, e.g. using this function below :
function cleanAutoIncrement($tableName, $columnAutoIncrement, $pdo)
{
$r = false;
try {
$p = $pdo->prepare("SELECT IFNULL(MAX($columnAutoIncrement) + 1, 1) AS 'max' FROM $tableName LIMIT 1;");
$p->execute();
$max = $p->fetch(PDO::FETCH_ASSOC)['max'];
$p = $pdo->prepare("ALTER TABLE $tableName AUTO_INCREMENT = $max;"):
$p->execute();
$r = true;
}
catch(Exception $e) {
$r = false;
}
return $r;
}
What the function do is to get the maximum id in the table, then increments it of 1, and return its value (if there was no rows in table, it return 1). Then I alter the table to reset a "clean" id in order not to let any hole of id.
QUESTION
Is there any MySQL command to perform this task without having to do this manually ?

To close this question, no shortcut exists in MySQL and it is not recommended to perform this task.

Related

Symfony2 DBAL Update method returning 0

I'm getting a strange result, where an update method (http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/data-retrieval-and-manipulation.html#update) call that activates/deactivates site users accounts in my mySQL database sometimes are not updated and returns a 0. Here is the code:
$user_id = '198';
$sqlSetStmnt= [ 'activation_code' => NULL, 'active' => 0 ];
$conn = $this->get( 'database_connection' );
try {
$result = $conn->update( 'users', $sqlSetStmnt,[ 'id' => (int)$user_id ] );
}
catch( Exception $e ) {
$error = $e->getMessage();
throw new \Exception( 'update user account data function -- ' .
'error: ' . $error . ' - ' .
'unable to update your account!' );
} // End of catch( Exception ) block.
if( ( $result === FALSE ) || ( $result !== 1 ) ) {
throw new \Exception( 'update user account data function -- ' .
'update return value: ' . $result . ' - ' .
'unable to update your account!' );
} // if( ( $result === FALSE ) || ( $result !== 1 ) ) ...
The users table has an id int(11) column which is the primary key, an active tinyint(1) column, and activation_code varchar(40) NULL column.
Please note the $user_id variable contains a string value of '198', but it is cast to an int when creating the $sqlSetStmnt value.
Inspecting the users table confirms that there was a row in the users table with the id column value of 198 at the time of the update call.
The account used when running the update call has enough privileges to change the row active and activation_code column values.
There are no other users in the system or accessing the database, so there aren't any locks on the row.
I inspected the code using x-debug, and the values of the $user_id and $sqlSetStmnt variables were properly set to the values that I expected, that $result was set to 0 by the update method, and that no exceptions were thrown by the update method call.
By the way, there is no need to using variable binding because the values in the $user_id and $sqlSetStmnt variables are not input by an user, so no possibility of SQL-Injection, or Buffer-Overrun.
Is there some way to get information from DBAL about why the update method returned 0?
Thank you.
Before solving this issue I switched from:
$result = $conn->update( 'users', $sqlSetStmnt, [ 'id' => (int)$user_id ] );
to:
$sqlUpdateStmnt = 'UPDATE `users` SET field = value ' .
'WHERE `id` = ' . $user_id;
$result = $conn->executeUpdate( $sqlUpdateStmnt );
and got exactly the same result, where some updates would return 0 rows when there definitely was a row in the users table with an id matching the value of $user_id.
I got around this problem by fetching the existing row and then only updating the row when the fields in the set-clause were different than the same columns from the table.
This tells me that the 0 return value wasn't that there were no matching rows, but that the updated didn't have any effect on any rows.
So this issue isn't a bug, so much as a misunderstanding of the result. However, the problem still exists when using the update() method of how to determine when the update failed due to no matching rows and when no changes were made.
The solution that I ended up solves this at the cost of a pre-fetch to verify that the update would affect a row. In my case, with a multi-user database application, pre-fetching isn't actually a problem because the row that is to be updated could have been deleted by another user before the update tries to make its change. But, it would be nice if both update methods explained this more clearly, and returned different values: 0 for no affected rows and FALSE for no rows found.

Perl MySQL DBI - update and get last value

I am using the Perl DBI module with MySQL and trying to get the initial value before adding 1 to it when updating a row.
If the current value was 1000 I need to return the value of 1000 and then add 1 to the value.
I use this statement in perl to use one transaction...
update TABLE_NAME set ID = (\#cur_value := ID) + 1
I know I can do a select then an update as two statements or lock the tables manually but transactions happen so fast on our platform that it may cause inconsistencies and this is the fastest way to do it.
However I simply cannot find a way to return the original value before the increment using this statement.
It works fine in ASP as below:
qry = "update V15_TRACKING set TRACKING_ID = (#cur_value := TRACKING_ID) + 1 where TRACKING_TYPE='ABC'"
Set oRS = oConn.Execute(qry)
qry = "select #cur_value"
if not oRS.EOF then
while not oRS.EOF
CurrTrackingID = oRs.Fields("#cur_value")
oRS.movenext
wend
oRS.close
end if
Please can someone advise me what I need to do to return the original value in Perl as I have searched everywhere and tried all sorts of solutions.
A snippet to show what you're actually doing in perl, and your result would help diagnose what is going on in your script.
I tried this trivial example:
The DB:
CREATE DATABASE TEST;
CREATE TABLE foo (
id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
val int(11) NOT NULL
);
INSERT INTO foo (val) VALUES (1);
And the Perl
#!/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use DBI;
my $dbh = DBI->connect('DBI:mysql:database=test', 'dbuser', 'dbpass');
my $select = $dbh->prepare('SELECT * FROM foo WHERE id=?');
my $select_old_val = $dbh->prepare('SELECT #old_val');
my $update = $dbh->prepare('UPDATE foo SET val=(#old_val := val) + 1 WHERE id=?');
$update->execute(1);
$select_old_val->execute();
$select->execute(1);
while (my $row = $select_old_val->fetchrow_hashref) {
print Dumper $row;
}
while (my $row = $select->fetchrow_hashref) {
print Dumper $row;
}
And after a few goes:
$ perl select_and_update.pl
$VAR1 = {
'#old_val' => '10'
};
$VAR1 = {
'id' => '1',
'val' => '11'
};

MYSQL fetch records from table 1 that do not exist in table 2

I created a php function to fetch records from a sql table subscriptions, and I want to add a condition to mysql_query to ignore the records in table subscriptions that exists in table removed_items, here is my code;
function subscriptions_func($user_id, $limit){
$subs = array();
$sub_query = mysql_query("
SELECT `subscriptions`.`fo_id`, `subscriptions`.`for_id`, `picture`.`since`, `picture`.`user_id`, `picture`.`pic_id`
FROM `subscriptions`
LEFT JOIN `picture`
ON `subscriptions`.`fo_id` = `picture`.`user_id`
WHERE `subscriptions`.`for_id` = $user_id
AND `picture`.`since` > `subscriptions`.`timmp`
GROUP BY `subscriptions`.`fo_id`
ORDER BY MAX(`picture`.`since_id`) DESC
$limit
");
while ($sub_row = mysql_fetch_assoc($sub_query)) {
$subs [] = array(
'fo_id' => $sub_row['fo_id'],
'for_id' => $sub_row['for_id'],
'user_id' => $sub_row['user_id'],
'pic_id' => $sub_row['pic_id'],
'since' => $sub_row['since']
);
}
return $subs ;
}
My solution is to create another function to fetch the records from table removed_items and set a php condition where I call subscriptions_func() to skip/unset the records that resemble the records in subscriptions_func(), as the following
$sub = subscriptions_func($user_id);
foreach($sub as $sub){
$rmv_sub = rmv_items_func($sub[‘pic_id’]);
If($rmv_sub[‘pic_id’] != $sub[‘pic_id’]){
echo $sub[‘pic_id’];
}
}
This solution succeeded to skip the items in the table removed_items however this solution makes gaps in the array stored in the variable $sub which makes plank spots in the echoed items.
Is there a condition I can add to the function subscriptions_func() to cut all the additional conditions and checks?
Assuming id is the primary key of subscriptions and subs_id is the foreign key in removed_items, then you just have to add a condition to the WHERE clause. Something like this should work :
...
AND `subscriptions`.id NOT IN (SELECT `removed_items`.subs_id FROM `removed_items`)
...
Not related to your problem :
Your code seems vulnerable to SQL injection : use prepared statement to prevent this.
The original Mysql API is deprecated, it is highly recommended to switch to Mysqli instead.

Do i really need an auto increment id column?

I have a table structure with META_ID | KEY | VALUE | USER_ID where META_ID is auto-increment. Now in my php logic
1 get the result key-value pairs per user
2 delete the key value row per user
3 update or insert the key value pair for a already known USER_ID
4 insert key value pair for a new user
But the META_ID keeps growing, so i was wondering if i could just delete the META_ID column?
Case logic
An registered user or returning registered user can update their form over time if they haven't submit it yet. So overtime an user can select and deselect certain form options and update, insert or delete is triggered.
Now the logic behind "returning user deselects a key (and the row needs to be deleted)" gives me a problem. That's why i just delete all users key-value pairs. But what would be the right way?
So if the key-value exists in the db table but not in $params i need to delete it!
btw here's my function
function user_shopping_meta_data($params) {
global $wpdb;
$shopping_meta_table = 'wp_shopping_metavalues';
$wp_user_id = $params['wp_user_id'];
//1 CHECK IF USER HAS KEY VALUE PAIRS
$checkKeyValues = $wpdb->get_results("SELECT meta_shopping_key FROM $shopping_meta_table WHERE wp_user_id = '$wp_user_id'");
//2 WE DELETE
$qdel = $wpdb->delete($shopping_meta_table, array('wp_user_id' => $wp_user_id));
//3 UPDATE OR INSERT
foreach ($params as $key => $val) {
//variables
if (is_array($val)) {
$val = json_encode($val);
}
$shopping_meta_values = array(
'wp_user_id' => $wp_user_id,
'meta_shopping_key' => $key,
'meta_shopping_value' => $val
);
if (count($checkKeyValues) > 0) {//3 USER IS KNOWN SO UPDATE and/or INSERT new key-value
foreach ($checkKeyValues as $check) {
//UPDATE OR INSERT
if (($key != "wp_user_id")) {
//FOR UPDATE where
$shopping_meta_where = array('meta_shopping_key' => $key, 'wp_user_id' => $wp_user_id);
$result = $wpdb->get_results("SELECT * FROM $shopping_meta_table WHERE meta_shopping_key = '" . $key . "' AND wp_user_id = '$wp_user_id'");
if (count($result) > 0) {//KEY ALREADY EXISTS FOR USER
$return .= $wpdb->update($shopping_meta_table, array('meta_shopping_key' => $key, 'meta_shopping_value' => $val), $shopping_meta_where) . '<br/>';
//$return .= 'UDPATE<br/>';
} else {//KEY IS NEW
$return .= $wpdb->insert($shopping_meta_table, $shopping_meta_values) . '<br/>';
// $return .= 'INSERT for old';
}
}//.end $key
}//.end foreach checkKeyValue
}//.end count
else {//4 INSERT KEY VALUE PAIR FOR NEW USER
if (($key != "wp_user_id")) {
$return .= $wpdb->insert($shopping_meta_table, $shopping_meta_values) . '<br/>';
// $return .= 'INSERT NEW';
}
}
}//.end each
echo 'Test return: ' . $return;
}
You won't gain much by deleting it. You might think that you save some space, but in fact you don't. An auto_increment column is always also (part of) the primary key. If you delete it, MySQL will create an "implicit" primary key, which is not visible but necessary for MySQL to identify rows. Also you will lose some comfort like not being able to use LAST_INSERT_ID().
You can very well delete it. It is just a unique ID. If you can distinguish different rows without the META_ID or you do not need to distinguish rows, then META_ID is redundant.
If i can give you a suggest is better to leave that field as a history.
If you need to want to know what is the last action done for that user you can order by META_ID.
Is usefull to have a primary key in a table. But this is just a suggest
I suggest you have a primary key that you are sure of that it is unique. It is a good idea to use a auto-increment column for this because you will always be sure that it is unique.

How to decrement the sequence on an column with auto increment as a constraint, when records are deleted

create table quiz(E_Id INT NOT NULL AUTO_INCREMENT PRIMAR KEY, E_name VARCHAR(255), E_Salary INT)
now whenever i insert data intop table, auto increment works as expected.
Now when i delete the record the sequence of number doesnot get decrement.
Suppose I have a records with Id 1, 2, 3, 4, 5. When i delete the 2 and 3 rd records, the sequence continues from the number 6. I want that if a records is deleted it numbering should get decrement automatically
There is no way for auto-decrementing. But you can use other simple methods.
DROP the field you are auto_incrementing.
ALTER the table to ADD the field again with the same attributes.
Then the list of auto-incrementing will reset.
NOTE: Take care when you are DROPing the table. It may drop your entire data.
Update all ids that are above the one been deleted by decrementing by 1 (-1) (if there are 3 id's in auto_increment column and you delete id 2, id 3 will be set to be id = 2) and then "alter table set auto_increment = 1 to update auto_increment counter.
code(including deletion of image file from website folder:
$allGood = false;
if(isset($_GET['id']) != ""){
$item = $_GET['id'];
$query = "SELECT * FROM products WHERE id = ?";
if($getImgPath = $sqlConnection->prepare($query)){
$getImgPath->bind_param("i",$item);
$getImgPath->execute();
$result = $getImgPath->get_result();
$row = $result->fetch_array();
$deletedItemId = $row['id'];
if(isset($row['image']) != ""){
$imgPath = "../../../../".$row['image'];
if(unlink($imgPath)){
$query = "DELETE FROM products WHERE id = ?";
if($delete = $sqlConnection->prepare($query)){
$delete->bind_param("i",$item);
if($delete->execute()){
$query = "SELECT * FROM products";
if($getIds = $sqlConnection->query($query)){
while($row = $getIds->fetch_array()){
if($row['id']>$deletedItemId){
$newId = $row['id']-1;
$query = "UPDATE products SET id=? WHERE id=?";
if($updateIds = $sqlConnection->prepare($query)){
$updateIds->bind_param("ii",$newId,$row['id']);
if($updateIds->execute()){
$allGood = true;
}
}
} else {$allGood = true;}
}
}
}
}
}
}
}
}
if($allGood == true){
$query = "ALTER TABLE products AUTO_INCREMENT = 1";
if($sqlConnection->query($query)){
echo "success!";
}else{echo "error.";}
}