SQL check if exists and insert [duplicate] - sql

This question already has answers here:
Is SELECT or INSERT in a function prone to race conditions?
(3 answers)
Closed last year.
I'm using a table 'Customer' with the following schema
id INTEGER NOT NULL UNIQUE,
name TEXT NOT NULL,
auth BOOLEAN DEFAULT FALSE
Now, I want to add a record if does not exist, I can do the following
IF NOT EXISTS (SELECT name from Customer where id=220)
BEGIN
INSERT into Customer (name,id) values ('Jon', 220)
END;
But at the same time, I also want to know if the id really did not exist along with the insertion i.e. True/False result of the select query. I can split it into two queries, from the first I can know if it exists and if id did not then I can insert it. But how can I do this in a single query?

You need to use INSERT with the RETURNING clause (PostgreSQL INSERT).

'on conflict' clause used with INSERT can be customized to serve your purpose.
INSERT INTO <table_name>(<column_name_list)) values(<column_values>) ON CONFLICT(<constraint_column>) DO NOTHING;
ref: https://www.postgresqltutorial.com/postgresql-upsert/
Set up
Step 1: Create the table:
create table test
(
id INTEGER NOT NULL UNIQUE,
name TEXT NOT NULL,
auth BOOLEAN DEFAULT FALSE
);
Step 2: Load the table with some sample rows:
insert into test(id,name) values(1,'vincent'),(2,'gabriel'),(3,'sebastian');
Step 3: Test with an INSERT of a row with existing id i.e 1 , the insert does not go through as the ID already exists:
INSERT INTO test(id,name) values(1,'xavier') ON CONFLICT(id) DO NOTHING;
Step 4: Now test with a row with ID that does not exist.i.e 4. It gets through.
INSERT INTO test(id,name) values(4,'xavier') ON CONFLICT(id) DO NOTHING;
Demo:
postgres=# select * from test;
id | name | auth
----+-----------+------
1 | vincent | f
2 | gabriel | f
3 | sebastian | f
(3 rows)
postgres=# INSERT INTO test(id,name) values(1,'xavier') ON CONFLICT(id) DO NOTHING;
INSERT 0 0
postgres=#
postgres=# select * from test;
id | name | auth
----+-----------+------
1 | vincent | f
2 | gabriel | f
3 | sebastian | f
(3 rows)
--- NOTE: no row inserted as ID 1 already exists.
postgres=# INSERT INTO test(id,name) values(4,'xavier') ON CONFLICT(id) DO NOTHING;
INSERT 0 1
postgres=# select * from test;
id | name | auth
----+-----------+------
1 | vincent | f
2 | gabriel | f
3 | sebastian | f
4 | xavier | f -------> new row inserted.
(4 rows)

you can use the following :
INSERT into Customer SELECT 'Jon', 220
Where Not EXISTS (SELECT 1
from Customer
where id=220);
Select Cast(##ROWCOUNT as bit);

Related

missing FROM-clause entry for table when trying to update / insert

Try to update a table with a new set of values,
if the values already exist then it should update the fields, if it doesn't then it should insert the fields.
WITH f AS (
SELECT 1 as MarketId,
'dros#test.com' as userName,
28 as age,
1 as isPremiumMember,
1 as isSubscribed,
'2021-03-12T17:07:30' as LastModifiedOn
from members
)
INSERT INTO members
(age,isPremiumMember,isSubscribed, lastModifiedOn)
VALUES (f.age,f.isPremiumMember,f.isSubscribed,f.lastModifiedOn)
ON CONFLICT (age,isPremiumMember,isSubscribed,lastModifiedOn)
DO UPDATE SET age= EXCLUDED.age,isPremiumMember = EXCLUDED.isPremiumMember,isSubscribed= EXCLUDED.isSubscribed,lastModifiedOn= EXCLUDED.lastModifiedOn;
however when I run the query I get this error:
missing FROM-clause entry for table "f"
trying to write this in plain SQL
INSERT INTO members
(age,isPremiumMember,isSubscribed, lastModifiedOn)
select age,isPremiumMember,isSubscribed,lastModifiedOn from f
ON CONFLICT (age,isPremiumMember,isSubscribed,lastModifiedOn)
DO UPDATE SET age= EXCLUDED.age,isPremiumMember = EXCLUDED.isPremiumMember,isSubscribed= EXCLUDED.isSubscribed,lastModifiedOn= EXCLUDED.lastModifiedOn;
tried adding the unique constraint as select as recommend below. this is my current statement.
Try using select instead of f.
INSERT INTO members
(age,isPremiumMember,isSubscribed, lastModifiedOn)
select age,isPremiumMember,isSubscribed,lastModifiedOn from f
ON CONFLICT (age,isPremiumMember,isSubscribed,lastModifiedOn)
DO UPDATE SET age= EXCLUDED.age,isPremiumMember = EXCLUDED.isPremiumMember,isSubscribed= EXCLUDED.isSubscribed,lastModifiedOn= EXCLUDED.lastModifiedOn;
Simplified example for an upsert query (avoiding the CTE):
\i tmp.sql
create table members (
member_id integer primary key
, name text
, eyes text
, hair text
);
INSERT INTO members(member_id, name, eyes, hair) VALUES
(1, 'Lisa', 'brown', 'brown'),
(2, 'Bob', 'brown', 'brown');
\echo Initial
SELECT * FROM members order by 1;
INSERT INTO members(member_id,name, eyes, hair)
VALUES(1, 'Alice', 'blue', 'blonde' )
ON conflict (member_id) DO
UPDATE SET name = EXCLUDED.name, eyes = EXCLUDED.eyes, hair = EXCLUDED.hair
;
\echo After the update
SELECT * FROM members order by 1;
Results:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 2
Initial
member_id | name | eyes | hair
-----------+------+-------+-------
1 | Lisa | brown | brown
2 | Bob | brown | brown
(2 rows)
INSERT 0 1
After the update
member_id | name | eyes | hair
-----------+-------+-------+--------
1 | Alice | blue | blonde
2 | Bob | brown | brown
(2 rows)

How to use a new serial ID for each new batch of inserted rows?

Is it possible to use a sequence for a batch of rows, versus getting a new ID on each insert? I'm keeping track of a set of details, and I want the sequence to apply for the set, not each individual row. So my data should look like so:
id batch_id name dept
1 99 John Engineering
2 99 Amy Humanities
3 99 Bill Science
4 99 Jack English
It's the batch_id that I want Postgres to issue as a sequence. Is this possible?
Define batch_id as batch_id bigint not null default currval('seqname') and call nextval('seqname') manually before inserting batch of rows.
Or, for the full automation:
1) Create sequence for the batch id:
create sequence mytable_batch_id;
2) Create your table, declare batch id field as below:
create table mytable (
id bigserial not null primary key,
batch_id bigint not null default currval('mytable_batch_id'),
name text not null);
3) Create statement level trigger to increment the batch id sequence:
create function tgf_mytable_batch_id() returns trigger language plpgsql
as $$
begin
perform nextval('mytable_batch_id');
return null;
end $$;
create trigger tg_mytablebatch_id
before insert on mytable
for each statement execute procedure tgf_mytable_batch_id();
Now each single statement when you inserting data into the table will be interpreted as next single batch.
Example:
postgres=# insert into mytable (name) values('John'), ('Amy'), ('Bill');
INSERT 0 3
postgres=# insert into mytable (name) values('Jack');
INSERT 0 1
postgres=# insert into mytable (name) values('Jimmmy'), ('Abigail');
INSERT 0 2
postgres=# table mytable;
id | batch_id | name
----+----------+-------------
1 | 1 | John
2 | 1 | Amy
3 | 1 | Bill
4 | 2 | Jack
5 | 3 | Jimmy
6 | 3 | Abigail
(6 rows)

SQL Server: Insert row into table, for all id's not existing yet

I have three tables in MS SQL Server, one with addresses, one with addresstypes and one with assignments of addresstypes:
Address:
IdAddress | Name | ...
1 | xyz
2 | abc |
...
AddressTypes
IdAddresstype | Caption
1 | Customer
2 | Supplier
...
Address2AddressType
IdAddress2AddressType | IdAddress | IdAddressType
1 | 1 | 2
3 | 3 | 2
Now I want to insert a row into Address2AddressType for each address, which is not assigned yet / not emerging in this table with the Addresstype Customer.
So to select those addresses, I use this query:
SELECT adresses.IdAddress
FROM [dbo].[Address] AS adresses
WHERE adresses.IdAddress NOT IN (SELECT adresstypeassignment.IdAddress
FROM [dbo].[Address2AddressType] AS adresstypeassignment)
Now I need to find a way to loop through all those results to insert like this:
INSERT INTO (Address2AddressType (IdAddress, IdAddresstype)
VALUES (<IdAddress from result>, 1)
Can anybody help, please?
Thanks in advance.
Regards
Lars
Use insert . . . select:
INSERT INTO Address2AddressType (IdAddress, IdAddresstype)
SELECT a.IdAddress, 1
FROM [dbo].[Address] a
WHERE a.IdAddress NOT IN (SELECT ata.IdAddress FROM [dbo].Address2AddressType ata);
I also simplified the table aliases.
Note: I don't recommend NOT IN for this purpose, because it does not handle NULLs the way you expect (if any values returned by the subquery are NULL no rows at all will be inserted). I recommend NOT EXISTS instead:
INSERT INTO Address2AddressType (IdAddress, IdAddresstype)
SELECT a.IdAddress, 1
FROM [dbo].[Address] a
WHERE NOT EXISTS (SELECT 1
FROM [dbo].Address2AddressType ata
WHERE ata.IdAddress = a.IdAddress
);

I can not use the 'insert all' to insert values into the table [duplicate]

This question already has answers here:
Multiple insert SQL oracle
(2 answers)
Closed 3 years ago.
I can not use the insert all to insert values into the first_name,last_name and phone columns.
CREATE TABLE accounts (
account_id NUMBER GENERATED BY DEFAULT AS IDENTITY,
first_name VARCHAR2(25) NOT NULL,
last_name VARCHAR2(25) NOT NULL,
email VARCHAR2(100),
phone VARCHAR2(12) ,
full_name VARCHAR2(51) GENERATED ALWAYS AS(
first_name || ' ' || last_name
),
PRIMARY KEY(account_id)
);
INSERT ALL
INTO accounts(first_name,last_name,phone)VALUES('John','Mobsey','410-555-0197')
INTO accounts(first_name,last_name,phone)VALUES('Ted','Scherbats','410-555-0198')
INTO accounts(first_name,last_name,phone)VALUES('Leeanna','Bowman','410-555-0199')
SELECT* FROM DUAL;
This is the error message I get when I try to Run the code:
ORA-00001: unique constraint (BTMDATABASE.SYS_C0086595925) violated
ORA-06512: at "SYS.DBMS_SQL", line 1721
1. INSERT ALL
2. INTO accounts(first_name,last_name,phone)VALUES('Trinity','Knox','410-555-0197')
3. INTO accounts(first_name,last_name,phone)VALUES('Mellissa','Porter','410-555-0198')
Exactly, you can not. The way you decided to create unique values for the account_id column won't work in insert all as all rows get the same value which violates the primary key constraint.
Two workarounds:
don't use insert all but separate insert statements
switch to a sequence in order to set primary key column's values
And, here's an example (if you need it):
SQL> create table accounts
2 (account_id number primary key,
3 first_name varchar2(20) not null
4 );
Table created.
SQL> create sequence seq_acc;
Sequence created.
SQL> create or replace trigger trg_acc_seq
2 before insert on accounts
3 for each row
4 begin
5 :new.account_id := seq_acc.nextval;
6 end;
7 /
Trigger created.
SQL> insert all
2 into accounts (first_name) values ('John')
3 into accounts (first_name) values ('Ted')
4 into accounts (first_name) values ('Leeanna')
5 select * from dual;
3 rows created.
SQL> select * from accounts;
ACCOUNT_ID FIRST_NAME
---------- --------------------
1 John
2 Ted
3 Leeanna
SQL>
Your statement:
INSERT ALL
INTO accounts(first_name,last_name,phone)VALUES('John','Mobsey','410-555-0197')
INTO accounts(first_name,last_name,phone)VALUES('Ted','Scherbats','410-555-0198')
INTO accounts(first_name,last_name,phone)VALUES('Leeanna','Bowman','410-555-0199')
SELECT* FROM DUAL;
Will try to give all the rows the same account_id which will violate your primary key.
Instead, you can use separate INSERT statements; or, if you want a single statement/transaction then you can wrap the INSERT statements in an anonymous PL/SQL block:
BEGIN
INSERT INTO accounts(first_name,last_name,phone)VALUES('John','Mobsey','410-555-0197');
INSERT INTO accounts(first_name,last_name,phone)VALUES('Ted','Scherbats','410-555-0198');
INSERT INTO accounts(first_name,last_name,phone)VALUES('Leeanna','Bowman','410-555-0199');
END;
/
or, you can also use INSERT INTO ... SELECT ... UNION ALL ...:
INSERT INTO accounts(first_name,last_name,phone)
SELECT 'Trinity', 'Knox', '410-555-0197' FROM DUAL UNION ALL
SELECT 'Mellissa','Porter','410-555-0198' FROM DUAL;
Output:
SELECT * FROM accounts;
ACCOUNT_ID | FIRST_NAME | LAST_NAME | EMAIL | PHONE | FULL_NAME
---------: | :--------- | :-------- | :---- | :----------- | :--------------
2 | John | Mobsey | null | 410-555-0197 | John Mobsey
3 | Ted | Scherbats | null | 410-555-0198 | Ted Scherbats
4 | Leeanna | Bowman | null | 410-555-0199 | Leeanna Bowman
5 | Trinity | Knox | null | 410-555-0197 | Trinity Knox
6 | Mellissa | Porter | null | 410-555-0198 | Mellissa Porter
Note: account_id of 1 is the failed INSERT ALL.
db<>fiddle here
INSERT INTO accounts(first_name,last_name,phone)VALUES('John','Mobsey','410-555-0197');
INSERT INTO accounts(first_name,last_name,phone)VALUES('Ted','Scherbats','410-555-0198');
INSERT INTO accounts(first_name,last_name,phone)VALUES('Leeanna','Bowman','410-555-0199');

Get row values as new columns [duplicate]

This question already has answers here:
Create a pivot table with PostgreSQL
(3 answers)
Closed 8 years ago.
I was wondering if it would be possible to get all values of rows with the same ID and present them as new columns, via a query.
For example, if I have the following table:
ID | VALUE
1 | a
1 | b
1 | c
2 | a
2 | b
[...]
I want to present it as:
ID | VALUE1 | VALUE2 | VALUE3 [...]
1 | a | b | c
2 | a | b | -
Thank you for any help
A query wouldn't do it. Unless you do 3 seperate querys.
SELECT ID,VALUE1 FROM Table
SELECT ID,VALUE2 FROM Table
ect...
If you have a problem with your database values not being recursive, then i would set up your table differently.
ID | VALUE
1 | a
1 | b
1 | c
2 | a
2 | b
[...]
You should set up the Table atributes like that rather than your first table.
if you are going to set up your tables differently I would do insert Statements.
INSERT INTO newTable (ID, VALUE)
SELECT ID,VALUE1 FROM oldTable
INSERT INTO newTable (ID, VALUE)
SELECT ID,VALUE2 FROM oldTable
ect..
Another possible way to do it is to display it in your application. Take php for instance.
foreach($sqlArray as $var){
echo $var['id'] ' | ' $var['value1']
echo $var['id'] ' | ' $var['value2']
echo $var['id'] ' | ' $var['value3']
}