How to upsert in postgresql - sql

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;

Related

Postgres - UPDATE ON CONFLICT not-null constraint violation

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"

how to delete a value from a column in SQL

How can I delete only the field of a column without deleting the entire row? I have a table that owns the fields:
ID, Name, Mail, Phone, Company and Would like to delete only the email of a person without deleting all the fields.
If I use:
DELETE FROM TEST_TABLE WHERE MAIL = 'myemail#gmail.com'
that way will delete everything and I want to delete just the email
you can use this
Update myTable set MyColumn = NULL where Field = Condition.
References
1- How do I set a column value to NULL in SQL Server Management Studio?
2- UPDATE, DELETE, and INSERT Statements in SQL
try
UPDATE
TEST_TABLE
SET
MAIL = ''
WHERE
id = your_id
or
if you want delete the field
ALTER TABLE TEST_TABLE
DROP COLUMN MAIL;
It is good practice to update the field with a NULL instead of leaving it blank, this indicates that there is a missing value and will later allow you to do queries where something is NOT NULL, which will give better results if it isn't returning bad data. Remember, garbage in, garbage out.
UPDATE TEST_TABLE SET MAIL = NULL WHERE MAIL = 'myemail#gmail.com'

PostgreSQL query for emails with Apostrophe

I have a database table with email addresses with apostrophe such as "some.o'ne#somewhere.com".
I would like to:
Query this table, and check if this email address exist
insert this email address if it doesn't exist in the table
I tried:
SELECT *
FROM EMAILTABLE
WHERE emailaddress LIKE 'some.o''ne#somewhere.com'
it doesn't find "some.o'ne#somewhere.com", so it's treating it as it doesn't exist!
Double-quotes around some text indicates you are looking for an object name, unless you are embedding the double-quotes within single-quotes.
So:
DROP TABLE IF EXISTS emailtable;
CREATE TEMP TABLE emailtable (emailaddress text);
CREATE UNIQUE INDEX idx_emailtable_emailaddress ON emailtable (emailaddress);
INSERT INTO emailtable (emailaddress) VALUES ('some.o''ne#somewhere.com');
SELECT emailaddress
,(emailaddress = 'some.o''ne#somewhere.com')::bool as escaped_apostrophe_single_quotes --true because it matches the value in the table
--,(emailaddress = "some.o'ne#somewhere.com")::bool as double_quotes --ERROR: column "some.o'ne#somewhere.com" does not exist
,(emailaddress = '"some.o''ne#somewhere.com"')::bool as double_quotes --false because the value in the table doesn't include double-quotes
FROM emailtable;
In the second test, it's assuming that "some.o'ne#somewhere.com" is a column, because it's a name inside double-quotes.
You can see from the first and third tests that the presence of the double-quotes inside the string are saying that these strings are different. The first one is the same as the value in the table. The other one isn't.
Anyway, I think the simplest way to handle is with a unique index on the emailaddress. If you then try to insert the same value that already exists, it won't do anything.
INSERT INTO emailtable (emailaddress) VALUES ('some.o''ne#somewhere.com') ON CONFLICT DO NOTHING;
Thanks for the replies (just replacing single quote with two single quotes doesn't work for me as it stores the email addresses with two single quotes)
This query worked fine for me:
SELECT * FROM EMAILTABLE WHERE emailaddress = (E'some.o\'ne#somewhere.com')::text;

How to conditionally insert a record in table B during insert into table A in PostgreSQL?

Given the following structure:
Table A (aliases):
user_id | alias
---------------
1 john
2 peter
user_id references id in users.
Table B (users):
id | password_hash | ...
---------------------------
1 ... ...
2 ... ...
(the idea is that users can have multiple aliases all of which point to the same main user account record)
I would like to do the following operation: given an alias, password, ... record:
if alias exists in aliases, update the corresponding password in users
if alias does not exist, create a new user in users with the given password and insert a row into aliases pointing to this new record.
How can I do that in a single query in Postgres?
Something along the lines of
WITH (
INSERT INTO users(id, password, ...) VALUES(DEFAULT, password, ...) RETURNING id
)
INSERT INTO aliases(user_id, alias) VALUES(id, alias)
ON CONFLICT {delete the temp row in users and update the one with the
known user_id instead}
This assumes that users_id_seq is the sequence used for users.id and that there is a UNIQUE constraint on aliases.alias:
WITH a AS (INSERT INTO aliases (user_id, alias)
VALUES (nextval('users_id_seq'), p_alias)
ON CONFLICT (alias)
/* this does nothing, but is needed for RETURNING */
DO UPDATE
SET user_id = aliases.user_id
RETURNING user_id
)
INSERT INTO users (id, password_hash, ...)
SELECT user_id, p_password, ...
FROM a
ON CONFLICT (id)
DO UPDATE
SET password_hash = EXCLUDED.password_hash;
Note: I'll assume that alias is the primary key (but at least, it's a unique key) of aliases.
Unfortunately, because the unique column (alias) is not on the target table (of the UPSERT), you cannot do this with a single INSERT ... ON CONFLICT ... statement.
First, you'll need to define the foreign key on aliases.user_id (which refers to the users.id column) to be DEFERRABLE (it can be INITIALLY IMMEDIATE though).
After that, these statements should be able to run (despite any concurrent modifications to these tables):
set constraints fk_aliases_user_id deferred;
with params(alias, pwd) as (
values ('john', 'pass3'),
('jane', 'pass4')
),
inserted_alias as (
insert into aliases(alias, user_id)
select alias, coalesce((select user_id
from aliases a
where a.alias = p.alias),
nextval('users_id_seq'))
from params p
on conflict (alias) do nothing
returning *
)
insert into users(id, password_hash)
select coalesce(i.user_id, a.user_id),
crypt(p.pwd, gen_salt('bf'))
from params p
left join inserted_alias i using (alias)
left join aliases a using (alias)
on conflict (id) do update
set password_hash = excluded.password_hash;
set constraints fk_aliases_user_id immediate;
Notes:
I used the crypt() function form the pgcrypto module to generate password_hash from plain passwords. I hope you're doing something similar.
This may cause gaps in the users_id_seq when the concurrency is high, but should always succeed (and I minimized the chances for that with the coalesce() part of the first insert).
You can leave the set constraints statements, if your foreign key is INITIALLY DEFERRED.
http://rextester.com/YDY89070
Your other option is to use PL/pgSQL and a retry loop (what was the official recommendation before the ON CONFLICT support was added).
Edit: it seems immediate constraints are not checked between CTE boundaries (however, I have not found any evidence for this in the docs, yet), so the set constraints statements & to make the foreign key deferrable is not needed.
http://rextester.com/IUSM65192

MySQL transaction query help

This problem is baffling me:
BEGIN;
INSERT INTO sub_users(user_id, email)
SELECT user_id FROM users WHERE email='someemail#email.com', '$email';
COMMIT;
Normally, I have multiple statements in that transactions, but I've removed those for clarity.
I get this error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near ' 'test#email.com'' at line 1
I'm using MySQL version 5.1.36
Two problems with your statement:
You're not selecting email to insert (only id)
You're using '=' instead of IN clause.
Should be something like:
INSERT INTO sub_users(user_id, email)
SELECT user_id, email FROM users
WHERE email IN ('someemail#email.com', 'test#email.com');
instead.
Update (based on comment)
INSERT INTO sub_users(user_id, email)
SELECT user_id, 'test#email.com' FROM users
WHERE email = 'someemail#email.com';
You have at least two errors that I can see:
You are trying to insert two columns worth of data (user_id and email) but only selecting one column (user_id) from the second half of INSERT INTO ... SELECT. You must select the same number of columns as you are trying to insert, and order matters.
You have a syntax error in the predicate of your SELECT. I think you want to use:
WHERE email IN ('email1#foo.com', 'email2#bar.com')
or its equivalent:
WHERE email = 'email1#foo.com'
OR email = 'email2#bar.com'