Do i really need an auto increment id column? - mysql

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.

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.

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

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.

Yii relation — MySQL foreign key

Tables in MySQL:
field:
id pk
field_option
id pk
feild_id int(11)
ALTER TABLE `field_option` ADD CONSTRAINT `option_field` FOREIGN KEY ( `feild_id` ) REFERENCES `agahi_fixed`.`field` (
`id`
) ON DELETE CASCADE ON UPDATE RESTRICT;
Relation Field model:
return array(
'fieldOption' => array(self::HAS_MANY, 'FieldOption', 'feild_id'),
);
Relation FieldOption model:
return array(
'feild' => array(self::BELONGS_TO, 'Field', 'feild_id'),
);
In controller:
if(Field::model()->exists('cat_id = :catId', array(":catId"=>$_POST['catid']))){
$criteria=new CDbCriteria;
//$criteria->select='*';
$criteria->condition='cat_id=:catId';
$criteria->params=array(':catId'=>$_POST['catid']);
$criteria->with = 'fieldOption';
$field=Field::model()->findAll($criteria);
header('Content-type: application /json');
$jsonRows=CJSON::encode($field);
echo $jsonRows;
}
but it does not work with just select records in field table.
Why?
this way you won't achive what your looking for,
when you fetch your records using with it will fetch associated records, meaning : eager loading not lazy, but when you json encode your model, It will get attributes of your main model, not any relations, If you want any related data to get encoded with the model, you have to explicitly say so. I suggest make an empty array :
$result = array();
make a loop over your model and append to this result, from model to related model
foreach($field as $model)
{
$record = $model->attributes; // get main model attributes
foreach($model->fieldOption as $relation)
$record['fieldOption'][] = $relation->attributes; // any related records, must be explicitly declared
$result[] = $record;
}
now you have exactly what you need, then echo it
echo CJSON::encode($result);

MySql - Inserting array values (matching and unmatching) into db matching columns

So I have a list of optional clothing items as checkboxes, there may be a greater number than the 5 below.
shoes, pants, skirt, socks, jacket //list of possible choices
A comma separated array is created in jquery of the chosen item. Let's say the following are chosen:
shoes, socks, jacket //array posted as $_POST['clothes']
In the db, each customer has these options in the clothes table with 'yes' or 'no' under the clothing items. However, the clothing item are named a bit differently but map out to the same options:
'clothes' table before insert
customer_id dress_shoes elegant_pants long_skirt ankle_socks biker_jacket
1 no yes no no no
With the $_POST['clothes'], I'm trying to loop through the array, updating the corresponding fields to 'yes', and the non corresponding fields to 'no' in the db. I have a hard time doing that.
'clothes' table after insert
customer_id dress_shoes elegant_pants long_skirt ankle_socks biker_jacket
1 yes no no yes yes
I did an array_intersect to get the items to mark as 'yes':
$clothesArray = array("shoes", "socks", "jacket"); // Posted clothes
$clothesArrayAll = array("shoes", "pants", "skirt", "socks", "jacket"); // All clothes
$common = array_intersect($clothesArrayAll,$clothesArray);
print_r($common);
Array ( [0] => shoes [3] => socks [4] => jacket )
I'm trying to somehow loop through the $clothesArrayAll, give a 'yes' to common clothes, and a 'no' to all others in the array. Then, I'm trying to update the 'clothes' table via PDO, setting each corresponding field to a 'yes' or 'no' in the most efficient way. I'm stuck after getting the common clothes array above and not sure how to proceed.
Can someone help me please?
Thank you!
I think you are on the right track. I would just add one additional array that contains the mappings of your fields, e.g.
$mappingArray = array('shoes' => 'dress_shoes', 'socks' => 'ankle_socks', ...);
With this array and the previous you can loop through and set your SQL accordingly based on the value of the $common field with the key in the $mappingArray
Edit with example (probably not the most optimized):
$finalArray = array();
foreach ($mappingArray as $key => $value) {
$finalArray[$value] = in_array($key, $common) ? 'yes' : 'no';
}
$finalArray will now have an yes/no statement for each value that matches your db table.
Edit to include PDO: I would actually update the above loop as follows:
$finalArray = array();
$sql = "INSERT INTO cloths (" . implode(",", array_values($mappingArray)) . ") VALUES (:" . implode(",:", array_values($mappingArray)) . ")";;
foreach ($mappingArray as $key => $value) {
$finalArray[":" . $value] = in_array($key, $common) ? 'yes' : 'no';
}
$q = $conn->prepare($sql);
$q->execute($finalArray);
Going on the fly with this one, so something like that...
Why not change your HTML field names to match your database names, set a default of 'no' in the atabase then...
$cols='INSERT INTO clothes ';
$values=' VALUES ';
$join='(';
foreach ($_POST as $key=>$val) {
$cols.=$join . $key;
$values=$join . ':' . $key;
$join=',';
}
$qry=$cols . ')' . $values . ')';
$stmt = $dbh->prepare($qry);
foreach ($_POST as $key=>$val) {
$stmt->bindParam(':' . $key, $_POST[$key]);
}
But you might want to check the posted names are valid column names - you can get the column names and types from the table using
DESC clothes;

MYSQL copy row to another identical table with AUTO_INCREMENT id field

I have a movie database where movies are inserted into a table named titles with an AUTO_INCREMENT primary key named titles_id. Users can submit movies anonymously which are inserted into a separate identical table named titles_anon. After reviewing entries in titles_anon I want to insert them into titles but the id column is causing problems
I tried this:
INSERT INTO titles SELECT * FROM titles_anon WHERE
title_id='$title_id';
I either get a duplicate key error, or if the title_id does not already exist in titles it inserts OK but uses the titles_anon id instead of a new AUTO_INCREMENT value which I want.
How do I copy a row between tables when both tables have an AUTO_INCREMENT primary key?
INSERT INTO titles
(column_name1, column_name2, column_name3, column_name4,...)
SELECT title_id, col2, col3, col4,..
FROM titles_anon
WHERE title_id = '$title_id';
You define your fields in SELECT, but omit the PK and add the same fields to INSERT!
You can omit the id column completely, let mysql generate it for you. This need a little longer SQL to specify the exact columns you want to insert.
INSERT INTO titles (columns-other-than-the-primary-key)
SELECT columns-of-the-same-order FROM titles_anon
In PHP you can do something similar to:
$rs = mysql_query("select * from table_orig where RowID=$IDToCopy",$db_conn);
$row = mysql_fetch_assoc($rs);
$sql = '';
$fields = '';
foreach($row as $k => $v){
if($k == "RowID") continue;
$sql .= ",'$v'";
$fields .= ",$k";
}
$sql = "insert into table_copy (".substr($fields,1).") values (".substr($sql,1).")";
mysql_query($sql,$db_conn);
/* copy table with primary key to another table */
$table_old='colaboradores'; // your table old
$table_new='colaboradores2'; //your table new
// sql all columns withou primary key (column_key <> 'pri'
$rs=mysql_query("select column_name from INFORMATION_SCHEMA.COLUMNS
where table_name = '$table_old' and column_key <> 'pri' AND
table_schema = 'your database'");
// get all columns
$rows = mysql_fetch_assoc($rs);
$fields = '';
// mount fields in line
foreach ($rows as $k => $v) {
$fields .= ",$v[0]";
}
// remove first comma
$fields = substr($fields,1);
echo "Sintaxe for Create table in Mysql";
echo "CREATE TABLE $table_new SELECT $fields FROM $table_old ";
echo "Sintaxe for Insert from old table to new table Mysql";
echo "INSERT INTO $table_new $fields SELECT $fields FROM $table_old";