Trying upsert and foreach in postgresql mybatis - postgresql

<insert id="UPSERT" parameterType="ArrayList">
INSERT INTO
table(column1, column2, column3)
VALUES
<foreach item="paramsList" separator="," collection="list">
('${paramsList.column1}', '${paramsList.column2}', '${paramsList.column3}')
</foreach>
ON CONFLICT (column1, column2)
DO UPDATE SET
column3 = values(column3)
</insert>
I was trying to make simple upsert, and the error says
ERROR: syntax error at or near "("
this part > column3 = values(column3)
So I'm assuming that unlike mysql, I can't use values() to re-use values from foreach.
Then which alternative I can use instead?

Related

INSERT output of a subquery along with an autoincrement in postgres

I am trying to store the output of a WITH statement in an INSERT statement in postgres along with an autoincrement id.
Below is the query:
INSERT
INTO
table
(row_id,(
SELECT
*
FROM
final_dataset
));
However, I am getting a syntax error near "SELECT". I am unabe to figure out the solution to this.
Are you looking for this?
insert into the_table (col1, col2, col3)
select nextval('the_table_id_seq'), x1, x2
from final_dataset;
If col1 is a serial or identitycolumn, I would remove it completely:
insert into the_table (col2, col3)
select x1, x2
from final_dataset;

RETURNING in INSERT INTO statement with a SELECT statement

I am inserting data into a table and want to return the inserted data. The inserted data contains foreign keys. I would like to get the whole data with the joins of the foreign keys.
I have tried putting a SELECT in RETURNING without luck. Is this even possible or do I just have to do another query after inserting the data?
Insert statement:
INSERT INTO someTable (col1, col2, col3, foreign_id)
VALUES ('value1', 'value2', 'value3', 1);
So in this case, could I have a RETURNING that basically would give me:
SELECT someTable.*, foreignTable.*
FROM someTable
JOIN foreignTable ON someTable.foreign_id = foreignTable.id;
demo:db<>fiddle
You can use a CTE for this:
WITH inserting AS (
INSERT INTO...
RETURNING <new data>
)
SELECT i.*, ft.*
FROM inserting i JOIN foreign_table ft ...
In this case the INSERT statement will be executed. The SELECT statement will be executed after that. This can reference the inserted data.
You can use a CTE for that:
with new_row as (
INSERT INTO some_table (col1, col2, col3, foreign_id)
VALUES ('value1', 'value2', 'value3', 1)
returning *
)
SELECT new_row.*, ft.*
FROM new_row
JOIN foreign_table ft ON new_row.foreign_id = ft.id;

UPSERT - INSERT ... ON CONFLICT fails with a function based index as 'lower()' unique constraint

I have a database table with a code column that uses a lowercase index to prevent code values that only differ in case (e.g. 'XYZ' = 'xYZ' = 'xyz'). The typical way in Postgresql is to create a function based index, like this: CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code)).
Now I have a case where I need upsert behaviour on that column:
-- first insert
INSERT INTO mytable (code) VALUES ('abcd');
-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT (code) DO UPDATE
SET code='Abcd';
For the second insert I get a unique key violation: ERROR: duplicate key value violates unique constraint "mytable_lower_code_idx"
(I also tried to use ON CONFLICT ON CONSTRAINT mytable_lower_code_idx but Postgresql tells me that this constraint does not exist so maybe it doesn't treat the index as a constraint.)
My final question: Is there any way to make INSERT ... ON CONFLICT work together with indexes on expressions? Or must I introduce a physical indexed lowercase column to accomplish the task?
Use ON CONFLICT (lower(code)) DO UPDATE:
CREATE TABLE mytable (
code text
);
CREATE UNIQUE INDEX mytable_lower_code_idx ON mytable (lower(code));
INSERT INTO mytable VALUES ('abcd');
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT (lower(code)) DO UPDATE
SET code='Abcd';
SELECT * FROM mytable;
yields
| code |
|------|
| Abcd |
Note that ON CONFLICT syntax
allows for the conflict target to be an index_expression (my emphasis):
ON CONFLICT conflict_target
where conflict_target can be one of:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
ON CONSTRAINT constraint_name
and index_expression:
Similar to index_column_name, but used to infer expressions on
table_name columns appearing within index definitions (not simple
columns). Follows CREATE INDEX format.
Try to add your index as follow:
CREATE UNIQUE INDEX CONCURRENTLY mytable_lower_code_idx
ON mytable (lower(code));
ALTER TABLE mytable
ADD CONSTRAINT unique_mytab_code
UNIQUE USING INDEX mytable_lower_code_idx ;
and then:
INSERT INTO mytable (code) VALUES ('abcd');
-- second insert, with upsert behaviour
INSERT INTO mytable (code) VALUES ('Abcd')
ON CONFLICT ON CONSTRAINT unique_mytab_code DO UPDATE
SET code='Abcd';

How to stop the "insert or update on table ...violates foreign key constraint"?

How to construct an INSERT statement so that it would not generate the error "insert or update on table ... violates foreign key constraint" in case if the foreign key value does not exist in the reference table?
I just need no record created in this case and success response.
Thank you
Use a query as the source for the INSERT statement:
insert into the_table (id, some_data, some_fk_column
select *
from (
values (42, 'foobar', 100)
) as x(id, some_data, some_fk_column)
where exists (select *
from referenced_table rt
where rt.primary_key_column = x.some_fk_column);
This can also be extended to a multi-row insert:
insert into the_table (id, some_data, some_fk_column
select *
from (
values
(42, 'foobar', 100),
(24, 'barfoo', 101)
) as x(id, some_data, some_fk_column)
where exists (select *
from referenced_table rt
where rt.primary_key_column = x.some_fk_column);
You didn't show us your table definitions so I had to make up the table and column names. You will have to translate that to your names.
You could create a function with plpgsql, which inserts a row and catches the exception:
CREATE FUNCTION customInsert(int,varchar) RETURNS VOID
AS $$
BEGIN
INSERT INTO foo VALUES ($1,$2);
EXCEPTION
WHEN foreign_key_violation THEN --do nothing
END;
$$ LANGUAGE plpgsql
You can then call this function by this:
SELECT customInsert(1,'hello');
This function tries to insert the given parameters into the table foo and catches the foreign_key_violation error if occurs.
Of course you can generalise the function more, to be able to insert in more than one table, but your question sounded like this was only needed for one specific table.

IF EXIST before many insertions in PostgreSQL?

I would like to INSERT only if table exists.
Pseudocode
IF EXISTS TABLE file_headers(
INSERT INTO file_headers
(measurement_id, file_header_index_start, file_header_index_end)
VALUES (1, 1, 100);
INSERT INTO file_headers
(measurement_id, file_header_index_start, file_header_index_end)
VALUES (1, 2, 100);
... -- many INSERTs into same table
);
How can you insert only if the table exists in PostgreSQL?
do $$begin
if exists (select * from pg_catalog.pg_tables where tablename = 'mytable') then
insert into mytable (col1) values (1);
end if;
end$$;
This test would make sense if you wanted to pass table names to a function for dynamic execution.
There is a built-in solution for that: if you want to make sure the table exists, cast it to regclass (or use a regclass parameter to your function to begin with). That verifies existence and escapes possible non-standard syntax at the same time:
How to check if a table exists in a given schema
Table name as a PostgreSQL function parameter