Postgres - UPDATE ON CONFLICT not-null constraint violation - sql

I am trying to use UPDATE .. ON CONFLICT available from Postgres 9.5 but I have a problem with violating null constraint on id.
I get:
ERROR: null value in column "id" of relation "feed" violates not-null constraint
Basically when id = null (feedId) I would like to perform INSERT and if id is returned to run an UPDATE.
Also I have WHERE clause in that query that it should be used only when there is an ID and not use when not because when adding it I also got an error that column reference \"id\" is ambiguous. It there a fix to my query or there is an alternative?
Without WHERE clause UPDATE is working and INSERT is not. But in the original case I posted below I have trouble with both errors.
I am new with writing sql queries and solutions I found online did not helped me with my case.
"INSERT INTO feed (id, team_id, name, url, auto_approve, last_pulled_at, source, evergreen)
VALUES (:feedId, :teamId, :name, :url, :autoApprove, :lastPulledAt, :source, :evergreen)
ON CONFLICT (id)
DO UPDATE SET
id = :feedId,
team_id = :teamId,
name = EXCLUDED.name,
url = EXCLUDED.url,
auto_approve = EXCLUDED.auto_approve,
last_pulled_at = EXCLUDED.last_pulled_at,
source = EXCLUDED.source,
evergreen = EXCLUDED.evergreen
WHERE id = :feedId
AND team_id = :teamId"

Related

How to upsert in postgresql

I have three columns in my table: id, email, and OAuthEmail. When a user, (user1#example.com) signs up with an email I'd like to create a new record if the email is not in the table. If the user previously signed up with OAuthEmail (if OAuthEmail = user1#example.com), I'd like to add user1#example.com to the email column. I have tried the following -
INSERT INTO users ("email")
VALUES (user1#example.org)
ON CONFLICT (user."OAuthEmail" = users1#example.org)
DO UPDATE SET (users.email = user1#example.org)
WHERE users."OAuthEmail" = user1#example.org;
I am getting a syntax error at "=" on the line - ON CONFLICT (users.email = user."OAuthEmail").
You have syntax problems
The email address has to be enclosed in quotes
The ON CONFLICT should specify the conflict column or the name of the constraint
There should be no WHERE clause
If you are going to set the email to the value that it already has, then change it to DO NOTHING
I hope you do have a UNIQUE constraint on the email column.
Also, You should not use mixed-case identifiers. That will give you headaches down the road.
with do_update as (
update users
set email = "OAuthEmail"
from invars
where "OAuthEmail" = 'user1#example.org'
and email is null
)
insert into users (email)
select new_email
from invars
on conflict (email) do nothing;

UNIQUE inside CHECK constraint

This question is new twist of An IF inside a check constraint SQL. I want to do something similar to the following check (which throws an ORA-00936: missing expression exception):
ALTER TABLE t_table
ADD CONSTRAINT chk_unique_active CHECK
(
( tb_active = 0 ) OR
( tb_active = -1 AND UNIQUE(tb_active, tb_img, tb_objid))
);
The objetive is to be sure (at DBMS level) that only one row with the same objid is active although inactive rows can be duplicated (an historical view of the rows).
It can be done in a trigger but it seems to be better using the check as explained in UNIQUE constraint vs checking before INSERT question.
is this possible?
Do this with a unique index:
create unique index unq_table_active
on ( (case when tb_active = -1 then tb_img end),
(case when tb_active = -1 then tb_objid end)
)
Oracle allows multiple rows with NULL values in a unique index.

SQL/DB2 SQLSTATE=23505 error when executing an UPDATE statement

I am getting a SQLSTATE=23505 error when I execute the following DB2 statement:
update SEOURLKEYWORD
set URLKEYWORD = REPLACE(URLKEYWORD, '/', '-')
where STOREENT_ID = 10701
and URLKEYWORD like '%/%';
After a quick search, a SQL state 23505 error is defined as follows:
AN INSERTED OR UPDATED VALUE IS INVALID BECAUSE THE INDEX IN INDEX SPACE CONSTRAINS COLUMNS OF THE TABLE SO NO TWO ROWS CAN CONTAIN DUPLICATE VALUES IN THOSE COLUMNS RID OF EXISTING ROW IS X
The full error I am seeing is:
The full error I am seeing is:
DB2 Database Error: ERROR [23505] [IBM][DB2/LINUXX8664] SQL0803N One or more values in the INSERT statement, UPDATE statement, or foreign key update caused by a DELETE statement are not valid because the primary key, unique constraint or unique index identified by "2" constrains table "WSCOMUSR.SEOURLKEYWORD" from having duplicate values for the index key. SQLSTATE=23505
1 0
I'm not sure what the "index identified by '2'" means, but it could be significant.
The properties of the columns for the SEOURLKEYWORD table are as follows:
Based on my understanding of this information, the only column that is forced to be unique is SEOURLKEYWORD_ID, the primary key column. This makes it sound like the update statement I'm trying to run is attempting to insert a row that has a SEOURLKEYWORD_ID that already exists in the table.
If I run a select * statement on the rows I'm trying to update, here's what I get:
select * from SEOURLKEYWORD
where storeent_id = 10701
and lower(URLKEYWORD) like '%/%';
I don't understand how executing the UPDATE statement is resulting in an error here. There are only 4 rows this statement should even be looking at, and I'm not manually updating the primary key at all. It kind of seems like it's reinserting a duplicate row with the updated column value before deleting the existing row.
Why am I seeing this error when I try to update the URLKEYWORD column of these four rows? How can I resolve this issue?
IMPORTANT: As I wrote this question, I have narrowed down the problem to the last of the four rows in the table above, SEOURLKEYWORD_ID = 3074457345616973668. I can update the other three rows just fine, but the 4th row is causing the error, I have no idea why. If I run a select * from SEOURLKEYWORD where SEOURLKEYWORD_ID = 3074457345616973668;, I see only the 1 row.
The error is pretty clear. You have a unique index/constraint in the table. Say you have two rows like this:
STOREENT_ID
URLKEYWORD
10701
A/B
10701
A-B
When the first version is replaced by 'A-B', the result would violate a unique constraint on (STOREENT_ID, URLKEYWORD) or (URLKEYWORD) (do note that other columns could possibly be included in the unique constraint/index as well).
You could avoid these situations by not updating them. I don't know what columns the unique constraint is on, but let's say only on URLKEYWORD. Then:
update SEOURLKEYWORD
set URLKEYWORD = REPLACE(URLKEYWORD, '/', '-')
where STOREENT_ID = 10701 and
URLKEYWORD like '%/%' and
not exists (select 1 from SEOURLKEYWORD s2 where replace(s2.urlkeyword, '/', '-') = REPLACE(SEOURLKEYWORD.URLKEYWORD, '/', '-')
);
Note the replace() is required for both columns because you might have:
A-B/C
A/B-C
These only conflict after the replacement in both values.
To complement the answer given by #GordonLinoff, here is a query that can be used to find a table's unique constraints, with their IDs, and the columns included in them:
SELECT c.tabschema, c.tabname, i.iid AS index_id, i.indname, ck.colname
FROM syscat.tabconst c
INNER JOIN syscat.indexes i
ON i.indname = c.constname -- unique index name matches constraint name
AND i.tabschema = c.tabschema AND i.tabname = c.tabname
INNER JOIN syscat.keycoluse ck
ON ck.constname = c.constname
AND ck.tabschema = c.tabschema c.tabname = ck.tabname AND
WHERE c.type = 'U' -- constraint type: unique
AND (c.tabschema, c.tabname) = ('YOURSCHEMA', 'YOURTABLE') -- replace schema/table
ORDER BY i.iid, ck.colseq

Insert into ignore duplicates that are not the primary key

I'm trying to find a query for sql that will just insert values but not do it should the value exist. Now ive seen alot of examples but they all rely on primary keys or table to table moves. I just want to add a new row in the table and assuming that one of the collumns doesnt have the same value add it. I know the following wont work but its as close to I think it would be and might just clear it up if my writting is not enough.
INSERT INTO table (txtLastName,txtEmail,txtZip)
Values ('Tester','test#test.com','12345')
WHERE txtLastName <> 'Tester'
or WHERE txtEmail <> 'test#test.com'
or WHERE txtZip <> '12345'
Using MS SQL Server.
You should create a Unique Constraint composed by the three fields (txtLastName, txtEmail, txtZip).
The links directs you to SQL Server docs, but the concept of unique constraint is RDBMS universal.
Just beware that when you create a Unique Constraint, your duplicate insert will not just fail silently: it will throw an error saying the insert tried to violate the unique constraint. And, of course, it should do that! Make sure your code handles that exception.
Try this:
INSERT INTO table (txtLastName,txtEmail,txtZip)
SELECT 'Tester','test#test.com','12345'
WHERE NOT EXISTS (SELECT *
FROM table
WHERE txtLastName = 'Tester'
AND txtEmail = 'test#test.com'
AND txtZip = '12345'
)

Doing UPSERT when row is referenced by a FK

Let's say that I have a table of items, and for each item, there can be additional information stored for it, which goes into a second table. The additional information is referenced by a FK in the first table, which can be NULL (if the item doesn't have additional info).
TABLE item (
...
item_addtl_info_id INTEGER
)
CONSTRAINT fk_item_addtl_info FOREIGN KEY (item_addtl_info)
REFERENCES addtl_info (addtl_info_id)
TABLE addtl_info (
addtl_info_id INTEGER NOT NULL
GENERATED BY DEFAULT
AS IDENTITY (
INCREMENT BY 1
NO CACHE
),
addtl_info_text VARCHAR(100)
...
CONSTRAINT pk_addtl_info PRIMARY KEY (addtl_info_id)
)
What is the "best practice" to update an item's additional info (in IBM DB2 SQL, preferably)?
It should be an UPSERT operation, meaning that if additional info does not yet exist then a new record is created in the second table, but if it does, then it is only updated, and the FK in the first table does not change.
So imperatively, this is the logic:
UPSERT(item, item_info):
CASE WHEN item.item_addtl_info_id IS NULL THEN
INSERT INTO addtl_info (item_info)
UPDATE item.item_addtl_info_id (addtl_info.addtl_info_id)
^^^^^^^^^^^^^
ELSE
UPDATE addtl_info (item_info)
END
My main problem is how to get the newly inserted addtl_info row's id (underlined above). In a stored proc I can request the id from a sequence and store it in a variable, but maybe there is a more straightforward way. Isn't it something that comes up all the time when programming databases?
I mean, I'm really not interested in what the id of the addtl_info record is as long as it remains unique and is referenced properly. So using sequences seems a bit of an overkill to me in this case.
As a matter of fact, this UPSERT operation should be part of the SQL language as a standard operation (maybe it is, and I just don't know about it?)...
The syntax I was looking for is:
SELECT * FROM NEW TABLE ( INSERT INTO phone_book VALUES ( 'Peter Doe','555-2323' ) )
from Wikipedia (http://en.wikipedia.org/wiki/Insert_%28SQL%29)
This is how to refer to the record that was just inserted in the table.
My colleague called this construct an "in-place trigger", which what it really is...
Here is the first version that I put together as a compound SQL statement:
begin atomic
declare addtl_id integer;
set addtl_id = (select item_addtl_info_id from item where item.item_id = XXX);
if addtl_id is null
then
set addtl_id = (select addtl_info_id from new table
(insert into addtl_info
(addtl_info_text)
values ('My brand new additional info')
)
);
update item set item.item_addtl_info_id = addtl_id
where item.item_id = XXX;
else
update addtl_info set addtl_info_text = 'My updated additional info'
where addtl_info.addtl_info_id = addtl_id;
end if;
end
XXX being equal to the item id to be updated - this code can now be easily inserted into a sproc, and XXX can be converted to an input parameter.
I also tried using MERGE INTO, but I couldn't figure out a syntax for updating a table different from what was specified as the target.