I have a simple table (id and name column, both unique), which I am importing a tab delimited CSV file.
I am running psql 9.5, and wanted to try out the new ON CONFLICT
feature to update the name column if the ID already exists.
CREATE TEMP TABLE tmp_x AS SELECT * FROM repos LIMIT 0;
COPY tmp_x FROM '/Users/George/git-parser/repo_file' (format csv, delimiter E'\t');
INSERT INTO repos SELECT * FROM tmp_x
ON CONFLICT(name) DO UPDATE SET name = tmp_x.name;
DROP TABLE tmp_x;
I am getting this error:
SELECT 0
COPY 1
ERROR: missing FROM-clause entry for table "tmp_x"
LINE 4: ON CONFLICT(name) DO UPDATE SET name = tmp_x.name;
^
Query failed
PostgreSQL said: missing FROM-clause entry for table "tmp_x"
Not too sure whats going wrong here.
You must have INSERT privilege on a table in order to insert into it. If ON CONFLICT DO UPDATE is present, UPDATE privilege on the table is also required. If a column list is specified, you only need INSERT privilege on the listed columns.
The INSERT ON CONFLICT statement allows you to update an existing row that contains a primary key when you execute the INSERT statement to insert a new row that contains the same primary key. This feature is also known as UPSERT or INSERT OVERWRITE. It is similar to the REPLACE INTO statement of MySQL.
The actual implementation within PostgreSQL uses the INSERT command with a special ON CONFLICT clause to specify what to do if the record already exists within the table. You can specify whether you want the record to be updated if it's found in the table already or silently skipped.
Introduction to PostgreSQL EXCLUDE. PostgreSQL excludes statements in PostgreSQL is used to compare any two rows from the specified column or expression by using the operator specified in PostgreSQL. At the time of excluding the column, the comparison operator will return the null or false value as output.
The ON CONFLICT clause is a non-standard extension specific to SQLite that can appear in many other SQL commands. It is given its own section in this document because it is not part of standard SQL and therefore might not be familiar. The ON CONFLICT clause described here has been a part of SQLite since before version 3.0.0 (2004-06-18).
The ON CONFLICT clause applies to UNIQUE, NOT NULL, CHECK, and PRIMARY KEYconstraints. The ON CONFLICT algorithm does not apply to FOREIGN KEY constraints. There are five conflict resolution algorithm choices: ROLLBACK, ABORT, FAIL, IGNORE, and REPLACE.
PostgreSQL ON CONFLICT | How ON CONFLICT Statement Works? PostgreSQL on conflict is used to insert the data in the same row twice, which the constraint or column in PostgreSQL identifies values. If we want to insert data into the same column twice at the same time, we have to use on the conflict by using insert statement in PostgreSQL.
If a NOT NULLconstraint violation occurs, the REPLACE conflict resolution replaces the NULL value with the default value for that column, or if the column has no default value, then the ABORT algorithm is used. If a CHECK constraintor foreign key constraintviolation occurs, the REPLACE conflict resolution algorithm works like ABORT.
If you look at the documentation of the ON CONFLICT
clause, it says this about the "conflict action":
The SET and WHERE clauses in ON CONFLICT DO UPDATE have access to the existing row using the table's name (or an alias)
In your query, the target table is repos
.
tmp_x
, on the other hand, is the source of the data you are trying to insert, but the ON CONFLICT
clause cannot "see" that - it is looking at a particular row that has been calculated and failed. Consider if you'd written something like this:
INSERT INTO repos SELECT max(foo_id) FROM tmp_x
Clearly, it wouldn't make sense for a row which failed to insert into repos
to have access to any one row from tmp_x
.
If there was no way of seeing the rejected data, the whole feature would be pretty useless, but if we read on:
... and to rows proposed for insertion using the special excluded table.
So instead, you need to access the magic table alias excluded
, which contains the values which you tried to insert but got a conflict on, giving you this:
INSERT INTO repos SELECT * FROM tmp_x
ON CONFLICT(name) DO UPDATE SET name = excluded.name;
If it seems weird that an imaginary table name pops up for this purpose, consider that a similar thing happens when writing triggers, where you get OLD
and NEW
(depending on the kind of trigger you're writing).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With