key violation when inserting mysql - php

I have this SQL code which inserts into a database named daily. I regularly update the daily database like this:
INSERT INTO daily
(SELECT * FROM dailytemp
WHERE trim(`Point`) <>''
AND trim(`Point`) IN (Select trim(`Point`) FROM `data`));
I believe I am getting the error because both tables have id primary columns.
I want the data inserted, but a new id generated for any duplicate found.
I tried to list the columns names, but it's about 20 columns, which might make the process cumbersome.
What is my best option?
Edit
The table defination of daily and daily_temp are the same, all the folumns are varchar(100) with and id of (bigint)
id bigint
col1 varchar(100)
col2 varchar(100)
col3 varchar(100)
etc..

A possible solution might be to use a BEFORE INSERT trigger and a separate table for sequencing (if you don't mind).
Sequence table
CREATE TABLE daily_seq(id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY);
Now the trigger
DELIMITER $$
CREATE TRIGGER tg_daily_insert
BEFORE INSERT ON daily
FOR EACH ROW
BEGIN
INSERT INTO daily_seq VALUES(NULL);
SET NEW.id = LAST_INSERT_ID();
END$$
DELIMITER ;
Now you can insert whatever you want to daily, id value will replaced with unique auto incremented one.
Here is SQLFiddle demo

Indeed, you are certainly trying to insert a row with an id that already exists in daily.
If this does not pose any referential integrity issue, you could regenerate the id's in dailytemp so that they do not overlap with any one in daily. This might do the trick:
SET #offset = (SELECT MAX(id) FROM daily) - (SELECT MIN(id) FROM dalytemp) +1;
UPDATE dailytemp SET id = id + #offset;
Then try your INSERT.
If rows can be inserted into daily by another process than the one you described, or if for any reason a id is skipped in this table (eg. if the column is AUTO_INCREMENT), then the problem will reoccur. You may want to include this preliminary UPDATE in your daily procedure.

i decided to use a stored proceedure to perform the action, listing all colums name instead of this headache
thanks to all those who helped. Another reason i did that was that i needed to insert a date for the daily table for each set of data inserted, so i created a date column for the dail table only
INSERT INTO daily (col1, col2, etc..., created)
SELECT col1, col2, etc..., 'mydate' from daily_temp WHERE
trim(`Point`) <>'' AND trim(`Point`) IN
(Select trim(`Point`) FROM `data`));
thanks all

Related

How would I go about automatically shifting the auto-increment id element in my sql table after I delete an element? [duplicate]

How would I reset the primary key counter on a sql table and update each row with a new primary key?
I would add another column to the table first, populate that with the new PK.
Then I'd use update statements to update the new fk fields in all related tables.
Then you can drop the old PK and old fk fields.
EDIT: Yes, as Ian says you will have to drop and then recreate all foreign key constraints.
Not sure which DBMS you're using but if it happens to be SQL Server:
SET IDENTITY_INSERT [MyTable] ON
allows you to update/insert the primary key column. Then when you are done updating the keys (you could use a CURSOR for this if the logic is complicated)
SET IDENTITY_INSERT [MyTable] OFF
Hope that helps!
This may or not be MS SQL specific, but:
TRUNCATE TABLE resets the identity counter, so one way to do this quick and dirty would be to
1) Do a Backup
2) Copy table contents to temp table:
3) Copy temp table contents back to table (which has the identity column):
SELECT Field1, Field2 INTO #MyTable FROM MyTable
TRUNCATE TABLE MyTable
INSERT INTO MyTable
(Field1, Field2)
SELECT Field1, Field2 FROM #MyTable
SELECT * FROM MyTable
-----------------------------------
ID Field1 Field2
1 Value1 Value2
Why would you even bother? The whole point of counter-based "identity" primary keys is that the numbers are arbitrary and meaningless.
you could do it in the following steps:
create copy of yourTable with extra column new_key
populate copyOfYourTable with the affected rows from yourTable along with desired values of new_key
temporarily disable constraints
update all related tables to point to the value of new_key instead of the old_key
delete affected rows from yourTable
SET IDENTITY_INSERT [yourTable] ON
insert affected rows again with the new proper value of the key (from copy table)
SET IDENTITY_INSERT [yourTable] OFF
reseed identity
re-enable constraints
delete the copyOfYourtable
But as others said all that work is not needed.
I tend to look at the identity type primary keys as if they were equivalent of pointers in C, I use them to reference other objects but never modify of access them explicitly
If this is Microsoft's SQL Server, one thing you could do is use the [dbcc checkident](http://msdn.microsoft.com/en-us/library/ms176057(SQL.90).aspx)
Assume you have a single table that you want to move around data within along with renumbering the primary keys. For the example, the name of the table is ErrorCode. It has two fields, ErrorCodeID (which is the primary key) and a Description.
Example Code Using dbcc checkident
-- Reset the primary key counter
dbcc checkident(ErrorCode, reseed, 7000)
-- Move all rows greater than 8000 to the 7000 range
insert into ErrorCode
select Description from ErrorCode where ErrorCodeID >= 8000
-- Delete the old rows
delete ErrorCode where ErrorCodeID >= 8000
-- Reset the primary key counter
dbcc checkident(ErrorCode, reseed, 8000)
With this example, you'll effectively be moving all rows to a different primary key and then resetting so the next insert takes on an 8000 ID.
Hope this helps a bit!

How can I reset id to 0 in PHP MYSQL? [duplicate]

How can I reset the AUTO_INCREMENT of a field?
I want it to start counting from 1 again.
You can reset the counter with:
ALTER TABLE tablename AUTO_INCREMENT = 1
For InnoDB you cannot set the auto_increment value lower or equal to the highest current index. (quote from ViralPatel):
Note that you cannot reset the counter to a value less than or equal
to any that have already been used. For MyISAM, if the value is less
than or equal to the maximum value currently in the AUTO_INCREMENT
column, the value is reset to the current maximum plus one. For
InnoDB, if the value is less than the current maximum value in the
column, no error occurs and the current sequence value is not changed.
See How can I reset an MySQL AutoIncrement using a MAX value from another table? on how to dynamically get an acceptable value.
SET #num := 0;
UPDATE your_table SET id = #num := (#num+1);
ALTER TABLE your_table AUTO_INCREMENT =1;
Simply like this:
ALTER TABLE tablename AUTO_INCREMENT = value;
Reference: 13.1.9 ALTER TABLE Statement
There is a very easy way with phpMyAdmin under the "operations" tab. In the table options you can set autoincrement to the number you want.
The best solution that worked for me:
ALTER TABLE my_table MODIFY COLUMN ID INT(10) UNSIGNED;
COMMIT;
ALTER TABLE my_table MODIFY COLUMN ID INT(10) UNSIGNED AUTO_INCREMENT;
COMMIT;
It's fast, works with InnoDB, and I don't need to know the current maximum value!
This way. the auto increment counter will reset and it will start automatically from the maximum value exists.
The highest rated answers to this question all recommend "ALTER yourtable AUTO_INCREMENT= value". However, this only works when value in the alter is greater than the current max value of the autoincrement column. According to the MySQL 8 documentation:
You cannot reset the counter to a value less than or equal to the value that is currently in use. For both InnoDB and MyISAM, if the value is less than or equal to the maximum value currently in the AUTO_INCREMENT column, the value is reset to the current maximum AUTO_INCREMENT column value plus one.
In essence, you can only alter AUTO_INCREMENT to increase the value of the autoincrement column, not reset it to 1, as the OP asks in the second part of the question. For options that actually allow you set the AUTO_INCREMENT downward from its current max, take a look at Reorder / reset auto increment primary key.
As of MySQL 5.6 you can use the simple ALTER TABLE with InnoDB:
ALTER TABLE tablename AUTO_INCREMENT = 1;
The documentation are updated to reflect this:
13.1.7 ALTER TABLE Statement
My testing also shows that the table is not copied. The value is simply changed.
Beware! TRUNCATE TABLE your_table will delete everything in your your_table.
You can also use the syntax TRUNCATE table like this:
TRUNCATE TABLE table_name
ALTER TABLE news_feed DROP id
ALTER TABLE news_feed ADD id BIGINT( 200 ) NOT NULL AUTO_INCREMENT FIRST ,ADD PRIMARY KEY (id)
I used this in some of my scripts. The id field is dropped and then added back with previous settings. All the existent fields within the database table are filled in with the new auto increment values. This should also work with InnoDB.
Note that all the fields within the table will be recounted and will have other ids!!!.
It is for an empty table:
ALTER TABLE `table_name` AUTO_INCREMENT = 1;
If you have data, but you want to tidy up it, I recommend to use this:
ALTER TABLE `table_name` DROP `auto_colmn`;
ALTER TABLE `table_name` ADD `auto_colmn` INT( {many you want} ) NOT NULL AUTO_INCREMENT FIRST ,ADD PRIMARY KEY (`auto_colmn`);
To update to the latest plus one id:
ALTER TABLE table_name AUTO_INCREMENT =
(SELECT (id+1) id FROM table_name order by id desc limit 1);
Edit:
SET #latestId = SELECT MAX(id) FROM table_name;
SET #nextId = #latestId + 1;
ALTER TABLE table_name AUTO_INCREMENT = #nextId;
Not tested please test before you run*
Warning: If your column has constraints or is connected as a foreign key to other tables this will have bad effects.
First, drop the column:
ALTER TABLE tbl_name DROP COLUMN column_id
Next, recreate the column and set it as FIRST (if you want it as the first column I assume):
ALTER TABLE tbl_access ADD COLUMN `access_id` int(10) NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST
As of MySQL 5.6 the approach below works faster due to online DDL (note algorithm=inplace):
alter table tablename auto_increment=1, algorithm=inplace;
SET #num := 0;
UPDATE your_table SET id = #num := (#num+1);
ALTER TABLE your_table AUTO_INCREMENT =1;
ALTER TABLE tablename AUTO_INCREMENT = 1
Try to run this query:
ALTER TABLE tablename AUTO_INCREMENT = value;
Or try this query for the reset auto increment
ALTER TABLE `tablename` CHANGE `id` `id` INT(10) UNSIGNED NOT NULL;
And set auto increment and then run this query:
ALTER TABLE `tablename` CHANGE `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT;
The auto-increment counter for a table can be (re)set in two ways:
By executing a query, like others already explained:
ALTER TABLE <table_name> AUTO_INCREMENT=<table_id>;
Using Workbench or another visual database design tool. I am going to show in Workbench how it is done - but it shouldn't be much different in other tools as well. By right clicking over the desired table and choosing Alter table from the context menu. On the bottom you can see all the available options for altering a table. Choose Options and you will get this form:
Then just set the desired value in the field Auto increment as shown in the image. This will basically execute the query shown in the first option.
If you're using PHPStorm's database tool you have to enter this in the database console:
ALTER TABLE <table_name> AUTO_INCREMENT = 0;
I tried to alter the table and set auto_increment to 1 but it did not work. I resolved to delete the column name I was incrementing, then create a new column with your preferred name and set that new column to increment from the onset.
I googled and found this question, but the answer I am really looking for fulfils two criteria:
using purely MySQL queries
reset an existing table auto-increment to max(id) + 1
Since I couldn't find exactly what I want here, I have cobbled the answer from various answers and sharing it here.
Few things to note:
the table in question is InnoDB
the table uses the field id with type as int as primary key
the only way to do this purely in MySQL is to use stored procedure
my images below are using SequelPro as the GUI. You should be able to adapt it based on your preferred MySQL editor
I have tested this on MySQL Ver 14.14 Distrib 5.5.61, for debian-linux-gnu
Step 1: Create Stored Procedure
create a stored procedure like this:
DELIMITER //
CREATE PROCEDURE reset_autoincrement(IN tablename varchar(200))
BEGIN
SET #get_next_inc = CONCAT('SELECT #next_inc := max(id) + 1 FROM ',tablename,';');
PREPARE stmt FROM #get_next_inc;
EXECUTE stmt;
SELECT #next_inc AS result;
DEALLOCATE PREPARE stmt;
set #alter_statement = concat('ALTER TABLE ', tablename, ' AUTO_INCREMENT = ', #next_inc, ';');
PREPARE stmt FROM #alter_statement;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
Then run it.
Before run, it looks like this when you look under Stored Procedures in your database.
When I run, I simply select the stored procedure and press Run Selection
Note: the delimiters part are crucial. Hence if you copy and paste from the top selected answers in this question, they tend not to work for this reason.
After I run, I should see the stored procedure
If you need to change the stored procedure, you need to delete the stored procedure, then select to run again.
Step 2: Call the stored procedure
This time you can simply use normal MySQL queries.
call reset_autoincrement('products');
Originally from my own SQL queries notes in https://simkimsia.com/reset-mysql-autoincrement-to-max-id-plus-1/ and adapted for Stack Overflow.
delete from url_rewrite where 1=1;
ALTER TABLE url_rewrite AUTO_INCREMENT = 1;
and then reindex
ALTER TABLE `table_name` DROP `id`;
ALTER TABLE `table_name` ADD `id` INT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`id`) ;
Shortly,First we deleted id column then added it with primary key id again...
The best way is remove the field with AI and add it again with AI. It works for all tables.
You need to follow the advice from Miles M's comment and here is some PHP code that fixes the range in MySQL. Also you need to open up the my.ini file (MySQL) and change max_execution_time=60 to max_execution_time=6000; for large databases.
Don’t use "ALTER TABLE tablename AUTO_INCREMENT = 1". It will delete everything in your database.
$con = mysqli_connect($dbhost, $dbuser, $dbpass, $database);
$res = mysqli_query($con, "select * FROM data WHERE id LIKE id ORDER BY id ASC");
$count = 0;
while ($row = mysqli_fetch_array($res)){
$count++;
mysqli_query($con, "UPDATE data SET id='".$count."' WHERE id='".$row['id']."'");
}
echo 'Done reseting id';
mysqli_close($con);
I suggest you to go to Query Browser and do the following:
Go to schemata and find the table you want to alter.
Right click and select copy create statement.
Open a result tab and paste the create statement their.
Go to the last line of the create statement and look for the Auto_Increment=N,
(Where N is a current number for auto_increment field.)
Replace N with 1.
Press Ctrl + Enter.
Auto_increment should reset to one once you enter a new row in the table.
I don't know what will happen if you try to add a row where an auto_increment field value already exist.

mysql trigger or php code

I have MySQL database that has 21 tables, in each table I have 'created' column(timestamp).
this field holds insert time of row.
However:
1- I can write a trigger for fill the column for each table like this:
CREATE TRIGGER `created` BEFORE INSERT ON `some_table`
FOR EACH ROW SET NEW.`created`=NOW()
2- I can handle it by PHP:
$result = mysqli_query($con,"INSERT INTO table_name (column1, column2, column3,created)
VALUES (value1, value2, value3,now()) ");
I concerned about writing trigger for each table has overhead. Is it right?
which one is better specific on performance?
which one is better at all?
You should not need to fill this column yourself if you configure the TIMESTAMP properties for it...
http://dev.mysql.com/doc/refman/5.5/en/timestamp-initialization.html
To clarify from the documentation here is a working example of how subsequent updates do not change a timestamp field when correctly configured for the desired behaviour
create temporary table example (apID int auto_increment,
audit timestamp DEFAULT CURRENT_TIMESTAMP,
name varchar(50),
primary key(apID));
insert into example(name) values('test');
select * from example;
update example set name='tes' where apID=1;
select * from example;

Max 3 rows in table for each user

Is it possible to make a script without else/if that deletes the oldest row for a user if the new row is the 4 row for that specific user?
I have a table called points_history. Fields are:
date(datetime),
fk_player_id(int),
points(int)
Here is my insert:
mysqli_query($mysqli,"INSERT INTO points_history (date,fk_player_id,points) VALUES (NOW(),$player,$points)");
The reason for this taht I want to be able to go back in the players history and check points, but only the last 3 points and don't want a table with a million of rows.
Can it be done in one sql query?
Hoping for help and thanks in advance :-)
This is quite easy to do if you add a primary key to your table points_history.
Part 1:
Use the following script to add a primary key called points_history_id to your table:
ALTER TABLE points_history RENAME TO points_history_old;
CREATE TABLE points_history
(
`points_history_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`date` datetime NOT NULL,
`fk_player_id` int(11) NOT NULL,
`points` int(11) NOT NULL,
PRIMARY KEY (`points_history_id`)
);
INSERT INTO points_history (date, fk_player_id, points)
SELECT date, fk_player_id, points
FROM points_history_old;
-- Drop table if migration succeeded (up to you)
-- DROP TABLE points_history_old;
This needs to be run only once!
Part 2:
Now you can use the following SQL script to add a new record and delete the outdated:
-- First insert the new record
INSERT INTO points_history (date,fk_player_id,points)
VALUES (NOW(),:player,:points);
-- Create temporary table with records to keep
CREATE TEMPORARY TABLE to_keep AS
(
SELECT points_history_id
FROM points_history
WHERE fk_player_id = :player
ORDER BY date DESC
LIMIT 3
);
SET SQL_SAFE_UPDATES = 0;
-- Delete all records not in table to_keep
DELETE FROM points_history
WHERE points_history_id NOT IN (SELECT points_history_id FROM to_keep);
SET SQL_SAFE_UPDATES = 1;
-- Drop temporary table
DROP TEMPORARY TABLE to_keep;
If you use a database supporting transactions, I strongly recommend to wrap this script in a transaction. I tested it on MySQL 5.5.29 and it runs fine.

Change Column to Auto_Increment

I asked this question a little earlier today but am not sure as to how clear I was.
I have a MySQL column filled with ordered numbers 1-56. These numbers were generated by my PHP script, not by auto_increment.
What I'd like to do is make this column auto_incrementing after the PHP script sets the proper numbers. The PHP script works hand in hand with a jQuery interface that allows me to reorder a list of items using jQuery's UI plugin.
Once I decide what order I'd like the entries in, I'd like for the column to be set to auto increment, such that if i were to insert a new entry, it would recognize the highest number already existing in the column and set its own id number to be one higher than what's already existing.
Does anyone have any suggestions on how to approach this scenario?
I'd suggest creating the table with your auto_increment already in place. You can specify a value for the auto_inc column, and mysql will use it, and still the next insert to specify a NULL or 0 value for the auto_inc column will magically get $highest + 1 assigned to it.
example:
mysql> create table foobar (i int auto_increment primary key);
mysql> insert into foobar values (10),(25);
mysql> insert into foobar values (null);
mysql> select * from foobar;
# returns 10,25,26
You can switch it to MySQL's auto_increment implementation, but it'll take 3 queries to do it:
a) ALTER TABLE to add the auto_increment to the field in question
b) SELECT MAX(id) + 1 to find out what you need to set the ID to
c) ALTER TABLE table AUTO_INCREMENT =result from (b)
MySQL considers altering the AUTO_INCREMENT value a table-level action, so you can't do it in (a), and it doesn't allow you to do MAX(id) in (c), so 3 queries.
You can change that with a query, issued through php, using the mysql console interface or (easiest) using phpmyadmin.
ALTER TABLE table_name CHANGE old_column_name new_column_name column_definition;
ALTER TABLE table_name AUTO_INCREMENT = highest_current_index + 1
column_definiton:
old_column_definition AUTO_INCREMENT
More info:
http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
http://dev.mysql.com/doc/refman/5.1/en/create-table.html
EDIT
Always use mysql_insert_id or the appropiate function of your abstraction layer to get the last created id, as LAST_INSERT_ID may lead to wrong results.
No, stop it. This isn't the point of auto_increment. If you aren't going to make them ordered by the id then don't make them auto_increment, just add a column onto the end of the table for ordering and enjoy the added flexibility it gives you. It seems like you're trying to pack two different sets of information into one column and it's really only going to bite you in the ass despite all the well-meaning people in this thread telling you how to go about shooting yourself in the foot.
In MySQL you can set a custom value for an auto_increment field. MySQL will then use the highest auto_increment column value for new rows, essentially MAX(id)+1. This means you can effectively reserve a range of IDs for custom use. For instance:
CREATE TABLE mytable (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
col1 VARCHAR(256)
);
ALTER TABLE mytable AUTO_INCREMENT = 5001;
In this schema all ids < 5001 are reserved for use by your system. So, your PHP script can auto-generate values:
for ($i=1; $i<=56; $i++)
mysql_query("INSERT INTO mytable SET id = $i, col1= 'whatevers'");
New entries will use the non-reserved range by not specifying id or setting it to null:
INSERT INTO mytable SET id = NULL, col1 = 'whatevers2';
-- The id of the new row will be 5001
Reserving a range like this is key - in case you need more than 56 special/system rows in the future.
ALTER TABLE <table name> <column name> NOT NULL AUTO_INCREMENT
More info:
AUTO_INCREMENT Handling in InnoDB
Server SQL Modes

Categories