I want my the id field in my table to be a bit more " random" then consecutive numbers.
Is there a way to insert something into the id field, like a +9, which will tell the db to take the current auto_increment value and add 9 to it?
Though this is generally used to solve replication issues, you can set an increment value for auto_increment:
auto_increment_increment
Since that is both a session and a global setting, you could simply set the session variable just prior to the insert.
Besides that, you can manually do it by getting the current value with MAX() then add any number you want and insert that value. MySQL will let you know if you try to insert a duplicate value.
You have a design flaw. Leave the auto increment alone and shuffle your query result (when you fetch your data)
As far as i know, it's not possible to 'shuffle' your current IDs. If you wanted though, you could pursue non-linear IDs in the future.
The following is written in PDO, there are mysqli equivalents.
This is just an arbitrary INSERT statement
$name = "Jack";
$conn = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass);
$sql = "INSERT INTO tableName (name) VALUES(:name)";
$q = $conn->prepare($sql);
$q->execute(':name' => $name);
Next, we use lastInsertId() to return the ID of the last inserted row, then we concatenate the result to rand()
$lastID = $conn->lastInsertId();
$randomizer = $lastID.rand();
Finally, we use our 'shuffled' ID and UPDATE the previously inserted record.
$sql = "UPDATE tableName SET ID = :randomizer WHERE ID=:lastID ";
$q = $conn->prepare($sql);
$q->execute(array(':lastID' => $lastID , ':randomizer' => $randomizer));
An idea.. (Not tested)
CREATE TRIGGER 'updateMyAutoIncrement'
BEFORE INSERT
ON 'DatabaseName'.'TableName'
FOR EACH ROW
BEGIN
DECLARE aTmpValueHolder INT DEFAULT 0;
SELECT AUTO_INCREMENT INTO aTmpValueHolder
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'DatabaseName'
AND TABLE_NAME = 'TableName';
SET NEW.idColumnName =aTmpValueHolder + 9;
END;
Edit : If the above trigger doesn't work try to update AUTO_INCREMENT value directly into the system's schema. But as noted by Eric, your design seems to be flawed. I don't see the point of having an auto-increment here.
Edit 2 : For a more 'random' and less linear number.
SET NEW.idColumnName =aTmpValueHolder + RAND(10);
Edit 3 : As pointed out by Jack Williams, Rand() produces a float value between 0 and 1.
So instead, to produce an integer, we need to use a floor function to transform the 'random' float into an integer.
SET NEW.idColumnName =aTmpValueHolder + FLOOR(a + RAND() * (b - a));
where a and b are the range of the random number.
Related
I want to update a table by getting the last inserted id but it is giving no results.
here is the query :
$quer = mysql_query("UPDATE date
SET d_startdate = '$start', d_enddate = '$end'
WHERE d_id = LAST_INSERT_ID() AND d_sid = $id");
d_id is the primary key and d_sid is a foreign key of another table
I have used INSERT as well as UPDATE operation on my same table and its working fine. You can change this query as per your need.
<?php
$con = mysql_connect("localhost","root","") or die("Could not connect");
mysql_selectdb("test", $con);
$query = 'INSERT INTO item (`name`) VALUES ("DELTaaaA")';
$res = mysql_query($query, $con) or die(mysql_error());
echo "<pre>";
print_r($res);
$query = 'UPDATE item set name="DELTaaaA1" WHERE id = LAST_INSERT_ID()';
$res = mysql_query($query, $con) or die(mysql_error());
print_r($res);
?>
It should return 1 1
use code like this before that
SELECT LAST_INSERT_ID() and assign this to a variable, then use that variable in your code
I don't know the normal syntax, but PDO syntax is quiet simple, you can get last inserted id by the function PDO::lastInsertId() use it as $myPDOobject->lastInsertId() . More information here : http://php.net/manual/en/pdo.lastinsertid.php
LAST_INSERT_ID gives you the id of the most recent insert.
Suppose that you added a row which has d_id=10 (d_id set by auto increment) and d_sid=20 and then another one with d_id=11 (again, auto increment) and d_sid=30.
You then want to look for the most recent insert with d_sid=20, but doing this with LAST_INSERT_ID is not possible, since LAST_INSERT_ID has value 11, and no row matches with that d_id and d_sid=20. You have to keep track by yourself of the most recent d_id for each category, most likely when you insert the new rows.
Do the INSERT
SELECT the LAST_INSERT_ID (name it L_ID)
Store the tuple <d_sid, L_ID> somewhere, so you know that for d_sid your most recent INSERT has value L_ID
UPDATE your table where the d_sid is the one you want and d_id is taken from the tuple
As a side note, mysql_* is deprecated, so you should switch to something else like mysqli or PDO.
"BIG" UPDATE:
Ok I was getting the whole
auto-increment point wrong. I though
this would be an easier way to target
the first, second, third and so row,
but it is just the wrong approach.
You should instead care about that the
auto_increments are unique and well...
that they increment. You should use
the for that.
I wont delete this question because I
think it might be helpful for someone
else with the same wrong idea, BUT
BE WARNED! :)
I have a very simple MySQL table which went like this:
id comment user
1 hello name1
2 bye name2
3 hola name3
Then I deleted the two first comments, the result:
id comment user
3 hola name3
So now when I add comments:
id comment user
3 hola name3
5 chau name4
6 xxx name5
My problem is that I would need that whenever a row gets deleted it should "start over" and look like this.
id comment user
1 hola name3
2 chau name4
3 xxx name5
I would like to know how is it possible to some how "restart" the table so that it is "always" indexed 1, 2, 3 and so on.
Thanks in advance!!
I hope I have explained myself clear enough, I'm sorry for all my "plain english", feel free to edit if you think a word might be confusing :) and please ask for any clarification needed!
BTW: I did not add any of my code because this is a simplified situation and I though it be more confusing and less helpful to others, but I you think it would help (or is necessary) tell me about it!
Assuming there are no foreign key issues to deal with, this code will do it:
set #id:=0;
update mytable
set id = (#id := #id + 1)
order by id;
If there are foreign key issues, make sure your constraints are defined like this before you execute the update:
ALTER CHILD_TABLE ADD CONSTRAINT
FOREIGN KEY MYTABLE_ID REFERENCES MYTABLE
ON UPDATE CASCADE; -- This is the important bit
When it's all done, execute this to fix up the auto_increment value:
SELECT MAX(ID) + 1 FROM MYTABLE; -- note the output
ALTER TABLE MYTABLE AUTO_INCREMENT = <result from above>;
Disclaimer: I can't think of one valid reason to do this, and it can break stuff very bad. However, I'm adding this for the sake of completeness and demonstration purposes.
You could use this really ugly solution, please only do this if you're at gunpoint or your dog is held hostage!
-- Create a new veriable.
SET #newId:=0;
-- Set all id's in the table to a new one and
-- also increment the counter in the same step.
-- It's basically just setting id to ++id.
UPDATE
yourTableHere
SET
id=#newId:=#newId+1;
-- Now prepare and execute an ALTER TABLE statement
-- which sets the next auto-increment value.
SET #query:=CONCAT("ALTER TABLE yourTableHere AUTO_INCREMENT=", #newId+1);
PREPARE sttmnt FROM #query;
EXECUTE sttmnt;
DEALLOCATE PREPARE sttmnt;
This will reset all of the Ids to the position of the row in the table. Please be aware that this will reorder the rows to how MySQL gets them from the storage engine, so there's no guarantee on the order in any way.
If you have a system which is based on the Ids (like relationships between tables) then you'll be...well, let's say I hope you have a backup.
Can't be done using MySQL's autoincrement feature. You could roll your own solution, e.g. a mix between application logic and database triggers. BUT, seriosly, your design is heavily broken if it requires you to recycle UNIQUE IDs.
Couldn't you just create another table where you'd save references like that (this could be done by querying the minimum) and let your main table point to that auxilliary table?
EDIT
Here's a blog I've googled that deals with your problem: see here.
ALTER TABLE event AUTO_INCREMENT = 1;
That's not the purpose of AUTO_INCREMENT. It exists to generate unique identifiers, not to maintain a gapless sequence.
If you have a valid reason for wanting this, then generate the identifiers yourself in your code. AUTO_INCREMENT won't provide this for you.
Accentually, auto increment is made for that to increase, no matter, how much rows are there.
ALTER TABLE table AUTO_INCREMENT = 1 does reseting, but you may get some bad things, if ID's starts to repeating.
So, my advise would be - leave it alone :)
function backup_tables($host, $user, $pass, $dbname, $tables = '*'){
$connect = mysqli_connect($host, $user, $pass , $dbname);
mysqli_query($connect, "SET NAMES 'utf8'");
//get all of the tables
if($tables == '*'){
$tables = array();
$result = mysqli_query($connect, 'SHOW TABLES');
while($row = mysqli_fetch_row($result))
{
$tables[] = $row[0];
}
}
else
{
$tables = is_array($tables) ? $tables : explode(',',$tables);
}
foreach($tables as $table){
$table = trim($table);
// getting all table fields
$tblDetails = mysqli_query($connect,"SHOW FULL COLUMNS FROM $table");
// we may need to know how to create our table
$tblCreate = mysqli_fetch_row(mysqli_query($connect, 'SHOW CREATE TABLE '.$table));
// getting last line from table creation script in order to get info about engine ->suffix1
$suffix1 = end(explode(PHP_EOL,$tblCreate[1]));
// if there is auto increment we have to remove
if (strpos($suffix1,"AUTO_INCREMENT")){
$tmpArr = explode(" ",$suffix1);
$newStr = '';
foreach ($tmpArr as $term){
if (!is_int(strpos($term, "AUTO_INCREMENT"))) $newStr .= $term . ' '; else $suffix4 = $term; // suffix4 stores next value of auto_increment
}
$suffix1 = $newStr;
} // now if there is auto_increment we removed from the last line of creation table script
$return .= "DROP TABLE IF EXISTS `".$table."` CASCADE;\n\n";
// starting creation table with our rules
$kgbReturn = "CREATE TABLE `$table` (\n";
while($cols = mysqli_fetch_row($tblDetails )){
if ($cols[2]) $cols[2] = " COLLATE " . $cols[2]; //if a charset defined add to line
if ($cols[3]=='NO') $cols[3] = " NOT NULL"; // if the field may be null
$kgbReturn .= "`".$cols[0]."` ".$cols[1]. $cols[2] . $cols[3]. ",\n"; //field creation line ready
}
$kgbReturn = rtrim($kgbReturn,",\n") . "\n" .trim($suffix1," ") . ";\n\n"; // table creation without auto_increment
$tblDetails = mysqli_query($connect,"SHOW FULL COLUMNS FROM $table WHERE (`Key` LIKE 'PRI%')");
$suffix2 = '';
while($cols = mysqli_fetch_row($tblDetails )){
$suffix2 .= "ALTER TABLE `". $table ."` \n ADD PRIMARY KEY (`".$cols[0]."`);\n\n";
}
$tblDetails = mysqli_query($connect,"SHOW FULL COLUMNS FROM $table WHERE (Extra LIKE 'auto_increment%')");
$suffix3 = '';
while($cols = mysqli_fetch_row($tblDetails )){
$suffix3 = "ALTER TABLE `". $table ."` \n ADD PRIMARY KEY (`".$cols[0]."`);\n\n";
$suffix3 = "ALTER TABLE `".$table."` \n MODIFY `".$cols[0]."` ".$cols[1]." NOT NULL AUTO_INCREMENT, ".$suffix4.";";
}
$return .= $kgbReturn;
$result = mysqli_query($connect, 'SELECT * FROM '.$table);
$num_fields = mysqli_num_fields($result);
// insert into all values
for ($i = 0; $i < $num_fields; $i++){
while($row = mysqli_fetch_row($result)){
$return .= 'INSERT INTO '.$table.' VALUES(';
for($j=0; $j < $num_fields; $j++){
$row[$j] = addslashes($row[$j]);
$row[$j] = str_replace (array("\r\n", "\n", "\r", PHP_EOL), '\r', $row[$j])
;
if (isset($row[$j])) { $return.= '"'.$row[$j].'"' ; } else { $return .= '""'; }
if ($j < ($num_fields-1)) { $return .= ','; };
}
$return .= ");\n";
}
}
$return .= "\n\n"; // insert values completed.
// now add primary key and auto increment statements if exist
$return .= $suffix2 . $suffix3 . "\n\n\n";
echo "<pre>".$return ."</pre>"; // debug line. comment if you don't like.
}
// we need to write to a file that coded as utf-8
$bkTime = date('Y_m_j_H_i_s');
$fileName = 'backup-db-'.$bkTime.'.sql';
$f=fopen($fileName,"w");
# Now UTF-8 - Add byte order mark
fwrite($f, pack("CCC",0xef,0xbb,0xbf));
fwrite($f,$return);
fclose($f);
}
You shouldn't really be worrying about this - the only thing an id should be is unique; its actual value should be irrelevant.
That said, here is a way (see the top comment) to do exactly what you want to do.
For those that are looking to "reset" the auto_increment, say on a list that has had a few deletions and you want to renumber everything, you can do the following.
DROP the field you are auto_incrementing.
ALTER the table to ADD the field again with the same attributes.
You will notice that all existing rows are renumbered and the next auto_increment number will be equal to the row count plus 1.
(Keep in mind that DROPping that column will remove all existing data, so if you have exterior resources that rely on that data, or the numbers that are already there, you may break the link. Also, as with any major structure change, it's a good idea to backup your table BEFORE you make the change.)
Maybe it is your approach to the solution you're trying to achieve that is not correct as what you're trying to achieve it's not possible "automatically" and doing by hand when you have thousands of rows will make your system lag.
Is it really necessary that it the system adjusts at every delete?
update table_name set id =NULL; alter table table_name change column
`id` `id` int auto_increment;
I have a table with 27 varchar fields. I want to make all fields lowercase, but i want to do it in one short mysql call.
This does a single field:
UPDATE table
SET field = LOWER(field)
How do I do the equivalent of this (which doesn't work):
UPDATE table
SET * = LOWER(*)
You can't do it with your creative attempt SET * = LOWER(*) etc.
You can however do it like this:
UPDATE table SET
column1 = LOWER(column1),
column2 = LOWER(column2),
-- etc, listing all text type columns
columnN = LOWER(columnN);
The reason there's no "shortcut" is probably because this pattern is so infrequently needed.
The consensus is that this cannot be done in a single mysql query.
Here is a super quick PHP script that does this for N fields (thanks for the idea #alex):
$sql = "SHOW COLUMNS
FROM table";
$results = mysqli_query($dbcon,$sql);
while($column = mysqli_fetch_assoc($results))
{
$column = $column["Field"];
$sql = "UPDATE table
SET $column = LOWER($column)";
$success = mysqli_query($dbcon,$sql);
}
In my table I have an userID that is auto-incremented. In the same row I have an idHash. Is it possible to generate the idHash (simply an MD5 sum) from it directly with the same INSERT statement so that I don't have to SELECT the id, and then UPDATE the idHash again?
Problem is: I do not know the userID before it is being generated (auto-incremented) by MySQL.
Thanks
Frank
PS: I'm using PHP.
PPS: This question is all about a SINGLE INSERT. I know that I can use PHP or other languages to manually select the data and then update it.
I don't believe you can do it within a single INSERT statement.
What you probably could do is use an INSERT trigger, that both determines the new ID, hashes it, and then updates the record.
One solution I can recommend is using the last insert ID instead of re-querying the table. Here is a simplified example:
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "INSERT INTO users VALUES (....)";
$mysqli->query($query);
$newUserID = $mysqli->insert_id;
$query = "UPDATE users SET idHash = MD5(userID) WHERE userID = $newUserID";
$mysqli->query($query);
/* close connection */
$mysqli->close();
?>
AFAIK there's no "secure" way for doing this in the same query if you're using auto_increment.
However, if rows are never deleted in your table, you can use this little trick :
insert into mytable (col1, col2, col3, idhash)
values ('', '', '', md5(select max(id) from mytable))
I don't understand why you need to hash the id though, why not use the id directly ?
This seems to work for me:
CREATE TABLE tbl (id INT PRIMARY KEY AUTO_INCREMENT, idHash TEXT);
INSERT INTO tbl (idHash) VALUES (MD5(LAST_INSERT_ID() + 1));
SELECT *, MD5(id) FROM tbl;
Note this will only work on single-row inserts as LAST_INSERT_ID returns the insert ID of the first row inserted.
Performing MD5(column_name) on an auto_increment value does not work as the value has not been generated yet, so it is essentially calling MD5(0).
PHP snippet
<?
$tablename = "tablename";
$next_increment = 0;
$qShowStatus = "SHOW TABLE STATUS LIKE '$tablename'";
$qShowStatusResult = mysql_query($qShowStatus) or die ( "Query failed: " . mysql_error() . "<br/>" . $qShowStatus );
$row = mysql_fetch_assoc($qShowStatusResult);
$next_increment = $row['Auto_increment'];
echo "next increment number: [$next_increment]";
?>
This will get you the next auto-increment and then you can use this in your insert.
Note: This is not perfect (Your method is imperfect as you will effectively have 2 primary keys)
From: http://blog.jamiedoris.com/geek/560/
I have two tables with identical structure except for one column... Table 2 has an additional column in which I would insert the CURRENT_DATE()
I would like to copy all the values from table1 to table2.
If I use
INSERT INTO dues_storage SELECT * FROM dues WHERE id=5;
it throws an error pointing to the difference in the number of columns.
I have two questions:
How do I get around this?
How do I add the value for the additional date column (CURRENT_DATE()) in table2 within this same statement?
To refine the answer from Zed, and to answer your comment:
INSERT INTO dues_storage
SELECT d.*, CURRENT_DATE()
FROM dues d
WHERE id = 5;
See T.J. Crowder's comment
The safest way to do it is to fully specify the columns both for insertion and extraction. There's no guarantee (to the application) that either of these will be the order you think they may be.
insert into dues_storage (f1, f2, f3, cd)
select f1, f2, f3, current_date() from dues where id = 5;
If you're worried about having to change many multiple PHP pages that do this (as you seem to indicate in the comment to another answer), this is ripe for a stored procedure. That way, all your PHP pages simply call the stored procedure with (for example) just the ID to copy and it controls the actual copy process. That way, there's only one place where you need to maintain the code, and, in my opinion, the DBMS is the right place to do it.
INSERT INTO dues_storage
SELECT field1, field2, ..., fieldN, CURRENT_DATE()
FROM dues
WHERE id = 5;
Hope this will help someone... Here's a little PHP script I wrote in case you need to copy some columns but not others, and/or the columns are not in the same order on both tables. As long as the columns are named the same, this will work. So if table A has [userid, handle, something] and tableB has [userID, handle, timestamp], then you'd "SELECT userID, handle, NOW() as timestamp FROM tableA", then get the result of that, and pass the result as the first parameter to this function ($z). $toTable is a string name for the table you're copying to, and $link_identifier is the db you're copying to. This is relatively fast for small sets of data. Not suggested that you try to move more than a few thousand rows at a time this way in a production setting. I use this primarily to back up data collected during a session when a user logs out, and then immediately clear the data from the live db to keep it slim.
function mysql_multirow_copy($z,$toTable,$link_identifier) {
$fields = "";
for ($i=0;$i<mysql_num_fields($z);$i++) {
if ($i>0) {
$fields .= ",";
}
$fields .= mysql_field_name($z,$i);
}
$q = "INSERT INTO $toTable ($fields) VALUES";
$c = 0;
mysql_data_seek($z,0); //critical reset in case $z has been parsed beforehand. !
while ($a = mysql_fetch_assoc($z)) {
foreach ($a as $key=>$as) {
$a[$key] = addslashes($as);
next ($a);
}
if ($c>0) {
$q .= ",";
}
$q .= "('".implode(array_values($a),"','")."')";
$c++;
}
$q .= ";";
$z = mysql_query($q,$link_identifier);
return ($q);
}
Alternatively, you can use Inner Queries to do so.
SQL> INSERT INTO <NEW_TABLE> SELECT * FROM CUSTOMERS WHERE ID IN (SELECT ID FROM <OLD_TABLE>);
Hope this helps!
SET #sql =
CONCAT( 'INSERT INTO <table_name> (',
(
SELECT GROUP_CONCAT( CONCAT('`',COLUMN_NAME,'`') )
FROM information_schema.columns
WHERE table_schema = <database_name>
AND table_name = <table_name>
AND column_name NOT IN ('id')
), ') SELECT ',
(
SELECT GROUP_CONCAT(CONCAT('`',COLUMN_NAME,'`'))
FROM information_schema.columns
WHERE table_schema = <database_name>
AND table_name = <table_source_name>
AND column_name NOT IN ('id')
),' from <table_source_name> WHERE <testcolumn> = <testvalue>' );
PREPARE stmt1 FROM #sql;
execute stmt1;
Of course replace <> values with real values, and watch your quotes.
Just wanted to add this little snippet which works beautifully for me.
INSERT INTO your_target_table
SELECT *
FROM your_rescource_table
WHERE id = 18;
And while I'm at it give a big shout out to Sequel Pro, if you're not using it I highly recommend downloading it...makes life so much easier