I'm running into the following error after trying to delete a bunch of records and then insert new ones:
Error: SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "routes_pkey" DETAIL: Key (id)=(1328) already exists.
SQL Query:
INSERT INTO routes (agency_id, route_identifier, short_name, long_name, description, route_color, text_color) VALUES (:c0, :c1, :c2, :c3, :c4, :c5, :c6) RETURNING *
Here's a simplified version of the code:
$routes = TableRegistry::get('Routes');
$routes->deleteAll(['agency_id' => $agency->id]);
# Data to be updated
$patchData = [
'stops' => $stopData,
'static_data_modified' => Time::now()
];
$patchOptions = [
'associated' => ['Stops.Routes']
];
# If: The stops have already been added to the DB
# Then: Remove them from the patch data
if (isset($stopData['_ids'])) {
unset($patchData['stops']);
# Change settings for this implementation type
$patchOptions = [];
$stopCount = count($stopData['_ids']);
}
$agency = $this->Agencies->patchEntity($agency, $patchData, $patchOptions);
$this->Agencies->save($agency);
It seems like for some reason Postgres thinks I'm inserting a record with a duplicate primary key. But I can't see how that would be possible from my code.
Here's what I see at the end of the SQL Log:
DELETE FROM
routes
WHERE
agency_id = 51
BEGIN
SELECT
1 AS "existing"
FROM
agencies Agencies
WHERE
Agencies.id = 51
LIMIT
1
INSERT INTO routes (
agency_id, route_identifier, short_name,
long_name, description, route_color,
text_color
)
VALUES
(
51, '100001', '1', '', 'Kinnear - Downtown Seattle',
'', ''
) RETURNING *
ROLLBACK
Any ideas why I'm seeing this error?
I'm on CakePHP v3.1 with Postgresql 9.4
I tried to add this but it didn't change anything:
$connection = ConnectionManager::get('default');
$results = $connection->execute('SET CONSTRAINT = routes_pkey DEFERRED');
Here are similar questions I've read without finding a solution:
ERROR: duplicate key value violates unique constraint in postgres
cakephp duplicate key value violates unique constraint
Error: duplicate key value violates unique constraint
ERROR: duplicate key value violates unique constraint "xak1fact_dim_relationship"
postgresql: error duplicate key value violates unique constraint
https://stackoverflow.com/questions/33416321/postgresql-bdr-error-duplicate-key-value-violates-unique-constraint-bdr-node
UPDATE
Following muistooshort's comment, I deleted all records from the routes table and re-ran the code and it worked fine. It also worked fine when I ran it a second time after that. So I think this supports mu's theory that something is wrong with the existing records in the db (not my code). I think the better question now is what exactly are the circumstances in the DB that are causing this and how do I fix them?
The serial type in PostgreSQL is pretty simple: it is essentially an integer column whose default value comes from a sequence. But the sequence doesn't know what you're doing to the table so things can get confused if you specify a value for the serial without using or updating the sequence.
For example:
create table t (
id serial not null primary key
);
insert into t (id) values (1);
insert into t (id) values (DEFAULT);
will produce a uniqueness violation because 1 was explicitly used for id but the sequence had no way of knowing that it was used.
Demo: http://sqlfiddle.com/#!15/17534/1
Presumably somewhere at sometime something added a row with id = 1328 without that id coming from the sequence. Or perhaps the sequence used for your PK's default was adjusted using setval to start returning values that were already in the table.
In any case, the easiest thing to do is adjust the sequence to match the table's current content using setval:
select setval('routes_id_seq', (select max(id) from routes));
The sequence should be called routes_id_seq but if it isn't, you can use \d routes inside psql to find out what its name is.
So if we update the previous example to this:
create table t (
id serial not null primary key
);
insert into t (id) values (1);
select setval('t_id_seq', (select max(id) from t));
insert into t (id) values (DEFAULT);
then we'll get 1 and 2 in our table instead of 1 and an error.
Demo: http://sqlfiddle.com/#!15/17534/7
Related
Read some relevant questions here, here and here. A simple query still triggers an error
SQLSTATE[HY000]: General error: 25 bind or column index out of range
The $query
INSERT OR IGNORE INTO `menu` (`id`,`name`,`name_clean`,`display`) VALUES (:idInsert,:nameInsert,:name_cleanInsert,:displayInsert);
UPDATE `menu` SET id=:idUpdate,name=:nameUpdate,name_clean=:name_cleanUpdate,display=:displayUpdate WHERE id = 1;
';
The $values
[:idInsert] => 1
[:idUpdate] => 1
[:nameInsert] => 2
[:nameUpdate] => 2
[:name_cleanInsert] => 3
[:name_cleanUpdate] => 3
[:displayInsert] => 1
[:displayUpdate] => 1
The snippet. $this->db->handle is the DB handle. As stated in one of the references above, I have implemented the setAttribute(\PDO::ATTR_EMULATE_PREPARES, true) to be able to execute multiple queries
$statement = $this->db->handle->prepare($query);
$statement->execute($values);
Fighting with this one for hours and feels like I am running circles. What am I missing here?
Update
Table definition as required
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (`id` INTEGER PRIMARY KEY NOT NULL ,`name` VARCHAR,`name_clean` VARCHAR,`sequence` INTEGER, `display` INTEGER);
I think you're running into this:
The keys from input_parameters must match the ones declared in the SQL. Before PHP 5.2.0 this was silently ignored.
Reference: PHP: PDOStatement::execute
My guess is that PHP tries to match the parameters on each of the two queries.
Your input and update parameters look the same, so I don't think there is a requirement to have the two sets. Try collapsing them into one set
[:id] => 1
[:name] => 2
[:name_clean] => 3
[:display] => 1
and referencing them in both queries.
Another note: are you sure you want
WHERE id = 1
This should probably also be
WHERE id=:id
The issue is that PDO wants to bind 8 values to 4 params in the first query split by the ";".
Seeing you SQL code (and your added database structure) I would change it to something like:
INSERT INTO menu (id, name, name_clean, display) VALUES
(
:idInsert, :nameInsert, :name_cleanInsert, :displayInsert
)
ON DUPLICATE KEY UPDATE
id = VALUES(:idUpdate),
name = VALUES(:nameUpdate),
name_clean = VALUES(:name_cleanUpdate),
display = VALUES(:displayUpdate);
This is written by hand, might contain syntax errors.
I have been trying to get the following SQL to work however it seems to skip the insert function. Essentially updating should take priority as most of the time it should fire.
UPDATE `teams-tasks`
SET status=(:s), name=(:n), description=(:d), importance=(:i), applies=(:a)
WHERE teamId =(:t) AND date=(:da) AND playerId =(:p) AND creatorId =(:c);
IF (SELECT ROW_COUNT() = 0);
INSERT INTO `teams-tasks`
( status, date, creatorId, teamId, playerId, name, description, importance, applies )
VALUES
( (:s), (:da), (:c), (:t), (:p), (:n), (:d), (:i), (:a) ))
what am i doing wrong?
i am using php pdo for my database connection if it matters
thanks
User replace into query which makes sure if the row exists it will update the data, if row does not exists it will insert the date.
to check the duplicate entry it compares the primary key internally
e.g.
REPLACE INTO table_name(column_name1,column_name2,…)
VALUES(value1,value2,…)
e.g.
REPLACE INTO offices(officecode,city)
VALUES(8,'San Jose')
Thanks
Amit
You should use INSERT ... ON DUPLICATE KEY UPDATE
For example
INSERT INTO AggregatedData (datenum,Timestamp)
VALUES ("734152.979166667","2010-01-14 23:30:00.000")
ON DUPLICATE KEY UPDATE
Timestamp=VALUES(Timestamp)
DELETE FROM RelationsAuthors WHERE MainId = :MainId AND AuthorId NOT IN (:authorarray)
The above code will delete anything that is not in authorarry and where MainId equals a specific value.
After the deletion I would like to insert the values of authoarray into the database if they do not exist without getting any errors.
*with using foreach $_POST['AuthorId']:
INSERT INTO RelationsAuthors (Id,MainId,AuthorId) VALUES('',:MainId,:AuthorId)
However I would like to add to my code that I need to INSERT only WHERE (MainId = :MainId AND AuthorId = :AuthorID) does not exist. How can I do that?
First, create a unique index on the two fields so the database will prevent duplicates for you:
create unique index idx_RelationsAuthors_MainId_AuthorId on RelationsAuthors(MainId, AuthorId);
Then the insert will fail with an error if you have duplicates. You can have this error ignored in a few ways. My preferred way is:
INSERT INTO RelationsAuthors (MainId, AuthorId)
VALUES(:MainId, :AuthorId)
ON DUPLICATE KEY UPDATE MainId = VALUES(MainId);
This will specifically ignore duplicate key errors, but other problems will still generate an error (if appropriate). Note I removed the first column. I'm guessing from the syntax that it is an auto-incremented id, so you don't need to include it in the insert statement at all.
I'm using a sequence and trigger to essentially auto increment a column in a table, however I'm getting an error - ORA-24344: success with compilation error.
I was using this post: How to create id with AUTO_INCREMENT on Oracle? and it worked successfully for two other tables w/ auto increment I made, but there must be something in here I'm not familiar with causing an error.
More edits: Thanks to Polppan we've established that this likely isn't an Oracle issue, rather an OCI with PHP issue. I'm using:
oci_execute($sql);
And as mentioned here (again, thanks Polppan for that link), there's a bit of an issue between EOL characters and oci_execute. It was 11 years ago, so I don't know if that's been patched or not, and I did try his solution but it didn't help. Does anyone know if there are other issues with oci_execute and creating triggers?
Creating the table: (works)
CREATE TABLE RT_documents (
documentID INT NOT NULL,
reviewID varchar2(20) NOT NULL,
file_location CLOB NOT NULL,
version NUMBER(*,3) NOT NULL,
CONSTRAINT RT_documents_pk PRIMARY KEY (documentID)
)
Creating the sequence: (works)
CREATE SEQUENCE rt_documents_seq
Creating/replacing trigger: (doesn't work)
CREATE OR REPLACE TRIGGER rt_documents_bir
BEFORE INSERT ON RT_documents
FOR EACH ROW
BEGIN
SELECT RT_documents_seq.NEXTVAL
INTO :new.documentID
FROM dual;
END;
EDIT: Exact error message as requested - (Note, I'm executing these query-by-query using OCI/Oracle in PHP. PHP tag added just in case, but pretty sure this is an oracle syntax error or something).
Error:
Notice: oci_execute(): OCI_SUCCESS_WITH_INFO: ORA-24344: success with
compilation error in (...)
-I can successfully execute the first two queries, and double checked and the table is there so it worked properly.
Trigger doesn't understand new.id as id doesn't exist in RT_documents table.
Your trigger should be
CREATE OR REPLACE TRIGGER rt_documents_bir
BEFORE INSERT
ON RT_documents
FOR EACH ROW
BEGIN
SELECT RT_documents_seq.NEXTVAL INTO :new.documentID FROM DUAL;
END;
Update
SELECT * FROM v$version;
Oracle Database 10g Enterprise Edition
CREATE TABLE RT_documents
(
documentID INT NOT NULL,
reviewID VARCHAR2 (20) NOT NULL,
file_location CLOB NOT NULL,
version NUMBER (*, 3) NOT NULL,
CONSTRAINT RT_documents_pk PRIMARY KEY (documentID)
);
Table created.
CREATE SEQUENCE rt_documents_seq;
Sequence created.
CREATE OR REPLACE TRIGGER rt_documents_bir
BEFORE INSERT
ON RT_documents
FOR EACH ROW
BEGIN
SELECT RT_documents_seq.NEXTVAL INTO :new.documentID FROM DUAL;
END;
/
Trigger created.
INSERT INTO RT_documents (reviewID, file_location, version)
VALUES ('test', 'test', 1);
1 row created.
SELECT * FROM RT_documents;
DOCUMENTID REVIEWID FILE_LOCATION
VERSION
---------- -------------------- -------------------------------------------
-------------------------------- ----------
1 test test
1
Thanks to Polppan.
The solution was removing the EOL characters. (I did try this but had, without realising, removed the semi-colons, which caused the same error code)
This was a PHP error after all. Using oci_execute, you must remove EOL characters in triggers:
$sql = "CREATE OR REPLACE TRIGGER ......."; //shortened for easy reading
$sql = str_replace(chr(13),'',$sql);
$sql = str_replace(chr(10),'',$sql);
oci_execute($sql);
$blanknumber = $_POST["blankstartnumber"];
while ($blanknumber <= ($_POST["blankendnumber"] ))
{
echo "$blanknumber";
$blankid = $blanknumber;
$query = "INSERT INTO blank (Blank_ID) VALUES ('$blankid')";
mysql_query($query,$con);
$blanknumber++;
}
So the values are added into the database. Lets say if I have the starting number at 1 and ending at 5. It will all the those values, but it's still trying to add more into the database. I also tried adding an IF statement aswell. if ($blanknumber != $_POST["blankendnumber"])
12345 Error: Duplicate entry '5' for
key 'PRIMARY'
Make sure your $POST value is an integer; by default, I believe it will be cast as a string.
$_POST['varName'] = (int) $_POST['varName'];
edit:
$blanknumber = $_POST["blankstartnumber"];
while ($blanknumber <= ($_POST["blankendnumber"] ))
This should only execute once, since you're setting both comparison variables equal. Definitely 2x check your code.
The database error indicates that Blank_ID is your primary key for that table, and you'd already inserted a 5 into the row. A primary key's values can exist only once in the entire table - duplicates are forbidden (if they were allowed, it wouldn't be a primary key anymore).
If your while loop isn't ending, I'd suggest dumping out both the blankendnumber and blankstartnumber before the loop starts, making sure you've got the right values in there.
It looks like it's actually functioning properly, but you might not have tidy'ed up your db table prior to running. If your output was:
123455 Error: Duplicate entry '5'...
Then, you'd have a programming error, as 5 is getting run twice. Instead, I think you already have data in the blank table that causes a conflict.
Edit: to automatically have MySQL handle the duplicate key error gracefully, you can use the ON DUPLICATE KEY clause to update the row.
INSERT INTO blank (Blank_ID) VALUES (5) ON DUPLICATE KEY UPDATE mod_date = NOW();