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

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)

Related

SQL check if exists and insert [duplicate]

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);

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
);

Copy data from one database to another - referenced tables and identities included

I have a database with this two tables :
[OldDb].[Per].[Person]
PersonId | FirstName | LastName | Code
2003 | 'Mike' | 'Jordan' | 2
2357 | 'Sara' | 'Jacobs' | 1
3481 | 'John' | 'Gates' | 5
[OldDb].[Sal].[Customer]
CustomerId | PersonId | CustomerType
830 | 2003 | 3
945 | 2357 | 2
1333 | 3481 | 2
And my new database with same tables and schema :
[NewDb].[Per].[Person]
PersonId | FirstName | LastName | Code
[NewDb].[Sal].[Customer]
CustomerId | PersonId | CustomerType
PersonId in table Person is identity and I can use this code to copy people but PersonId's will be different from old database so I can't use the second query I said below to copy data from customer table.
INSERT INTO NewDb.per.Person
(FirstName,LastName,Code)
SELECT FirstName,LastName,Code
FROM OldDb.per.Person
INSERT INTO NewDb.Sal.Customer
(PersonId,CustomerType)
SELECT PersonId,CustomerType
FROM OldDb.Sal.Customer
Now I want a query so I can copy data to new db for both tables.
Any help would be a great help.
Thank you.
Your new database is empty, if you want to keep your old PersonId, you could use SET IDENTITY_INSERT NewDb.per.Person ON
SET IDENTITY_INSERT NewDb.per.Person ON -- then you could use personId in Insert
INSERT INTO NewDb.per.Person
(PersonId, FirstName,LastName,Code)
SELECT PersonId, FirstName,LastName,Code
FROM OldDb.per.Person
SET IDENTITY_INSERT NewDb.per.Person OFF -- remember set it off after insert
-- then insert new Customer without conflict
INSERT INTO NewDb.Sal.Customer
(PersonId,CustomerType)
SELECT PersonId,CustomerType
FROM OldDb.Sal.Custome
Reference link: SET IDENTITY_INSERT
And if you want new PersonId auto increment you could do this:
----CREATE `OldPersonId` column in your NewDb.per.Person
INSERT INTO NewDb.per.Person
(OldPersonId, FirstName,LastName,Code)
SELECT PersonId, FirstName,LastName,Code
FROM OldDb.per.Person
-- You could insert you new customer Inner join by `OldPersonId` Column
INSERT INTO NewDb.Sal.Customer
(PersonId,CustomerType)
SELECT np.PersonId,CustomerType
FROM OldDb.Sal.Customer oc
INNER JOIN NewDb.per.Person np ON oc.PersonId = np.OldPersonId
-----DELETE `OldPersonId` column in NewDb.per.Person

Inserting a row at the specific place in SQLite database

I was creating the database in SQLite Manager & by mistake I forgot to mention a row.
Now, I want to add a row in the middle manually & below it the rest of the Auto-increment keys should be increased by automatically by 1 . I hope my problem is clear.
Thanks.
You shouldn't care about key values, just append your row at the end.
If you really need to do so, you could probably just update the keys with something like this. If you want to insert the new row at key 87
Make room for the key
update mytable
set key = key + 1
where key >= 87
Insert your row
insert into mytable ...
And finally update the key for the new row
update mytable
set key = 87
where key = NEW_ROW_KEY
I would just update IDs, incrementing them, then insert record setting ID manually:
CREATE TABLE cats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR
);
INSERT INTO cats (name) VALUES ('John');
INSERT INTO cats (name) VALUES ('Mark');
SELECT * FROM cats;
| 1 | John |
| 2 | Mark |
UPDATE cats SET ID = ID + 1 WHERE ID >= 2; -- "2" is the ID of forgotten record.
SELECT * FROM cats;
| 1 | John |
| 3 | Mark |
INSERT INTO cats (id, name) VALUES (2, 'SlowCat'); -- "2" is the ID of forgotten record.
SELECT * FROM cats;
| 1 | John |
| 2 | SlowCat |
| 3 | Mark |
Next record, inserted using AUTOINCREMENT functionality, will have next-to-last ID (4 in our case).

Creating a query to find matching objects in a "join" table

I am trying to find an efficient query to find all matching objects in a "join" table.
Given an object Adopter that has many Pets, and Pets that have many Adopters through a AdopterPets join table. How could I find all of the Adopters that have the same Pets?
The schema is fairly normalized and looks like this.
TABLE Adopter
INTEGER id
TABLE AdopterPets
INTEGER adopter_id
INTEGER pet_id
TABLE Pets
INTEGER id
Right now the solution I am using loops through all Adopters and asks for their pets anytime it we have a match store it away and can use it later, but I am sure there has to be a better way using SQL.
One SQL solution I looked at was GROUP BY but it did not seem to be the right trick for this problem.
EDIT
To explain a little more of what I am looking for I will try to give an example.
+---------+ +------------------+ +------+
| Adptors | | AdptorsPets | | Pets |
|---------| +----------+-------+ |------|
| 1 | |adptor_id | pet_id| | 1 |
| 2 | +------------------+ | 2 |
| 3 | |1 | 1 | | 3 |
+---------+ |2 | 1 | +------+
|1 | 2 |
|3 | 1 |
|3 | 2 |
|2 | 3 |
+------------------+
When you asked the Adopter with the id of 1 for any other Adopters that have the same Pets you would be retured id 3.
If you asked the same question for the Adopter with the id of 3 you would get id 1.
If you asked again the same question of the Adopter with id 2` you would be returned nothing.
I hope this helps clear things up!
Thank you all for the help, I used a combination of a few things:
SELECT adopter_id
FROM (
SELECT adopter_id, array_agg(pet_id ORDER BY pet_id)
AS pets
FROM adopters_pets
GROUP BY adopter_id
) AS grouped_pets
WHERE pets = array[1,2,3] #array must be ordered
AND adopter_id <> current_adopter_id;
In the subquery I get pet_ids grouped by their adopter. The ordering of the pet_ids is key so that the results in the main query will not be order dependent.
In the main query I compare the results of the subquery to the pet ids of the adopter I am looking to match. For the purpose of this answer the pet_ids of the particular adopter are represented by [1,2,3]. I then make sure that that the adopter I am comparing to is not included in the results.
Let me know if anyone sees any optimizations or if there is a way to compare arrays where order does not matter.
I'm not sure if this is exactly what you're looking for but this might give you some ideas.
First I created some sample data:
create table adopter (id serial not null primary key, name varchar );
insert into adopter (name) values ('Bob'), ('Sally'), ('John');
create table pets (id serial not null primary key, kind varchar);
insert into pets (kind) values ('Dog'), ('Cat'), ('Rabbit'), ('Snake');
create table adopterpets (adopter_id integer, pet_id integer);
insert into adopterpets values (1, 1), (1, 2), (2, 1), (2,3), (2,4), (3, 1), (3,3);
Next I ran this query:
SELECT p.kind, array_agg(a.name) AS adopters
FROM pets p
JOIN adopterpets ap ON ap.pet_id = p.id
JOIN adopter a ON a.id = ap.adopter_id
GROUP BY p.kind
HAVING count(*) > 1
ORDER BY kind;
kind | adopters
--------+------------------
Dog | {Bob,Sally,John}
Rabbit | {Sally,John}
(2 rows)
In this example, for each pet I'm creating an array of all owners. The HAVING count(*) > 1 clause ensures we only show pets with shared owners (more than 1). If we leave this out we'll include pets that don't share owners.
UPDATE
#scommette: Glad you've got it working! I've refactored your working example a little bit below to:
use #> operator. This checks if one array contains the other avoids need to explicitly set order
moved the grouped_pets subquery to a CTE. This isn't only solution but neatly allows you to both filter out the current_adopter_id and get the pets for that id
You might find it helpful to wrap this in a function.
WITH grouped_pets AS (
SELECT adopter_id, array_agg(pet_id ORDER BY pet_id) AS pets
FROM adopters_pets
GROUP BY adopter_id
)
SELECT * FROM grouped_pets
WHERE adopter_id <> 3
AND pets #> (
SELECT pets FROM grouped_pets WHERE adopter_id = 3
);
If you're using Oracle then wm_concat could be useful here
select pet_id, wm_concat(adopter_id) adopters
from AdopterPets
group by pet_id ;
--
-- Relational division 1.0
-- Show all people who own *exactly* the same (non-empty) set
-- of animals as I do.
--
-- Test data
CREATE TABLE adopter (id INTEGER NOT NULL primary key, fname varchar );
INSERT INTO adopter (id,fname) VALUES (1,'Bob'), (2,'Alice'), (3,'Chris');
CREATE TABLE pets (id INTEGER NOT NULL primary key, kind varchar);
INSERT INTO pets (id,kind) VALUES (1,'Dog'), (2,'Cat'), (3,'Pig');
CREATE TABLE adopterpets (adopter_id integer REFERENCES adopter(id)
, pet_id integer REFERENCES pets(id)
);
INSERT INTO adopterpets (adopter_id,pet_id) VALUES (1, 1), (1, 2), (2, 1), (2,3), (3,1), (3,2);
-- Show it to the world
SELECT ap.adopter_id, ap.pet_id
, a.fname, p.kind
FROM adopterpets ap
JOIN adopter a ON a.id = ap.adopter_id
JOIN pets p ON p.id = ap.pet_id
ORDER BY ap.adopter_id,ap.pet_id;
SELECT DISTINCT other.fname AS same_as_me
FROM adopter other
-- moi has *at least* one same kind of animal as toi
WHERE EXISTS (
SELECT * FROM adopterpets moi
JOIN adopterpets toi ON moi.pet_id = toi.pet_id
WHERE toi.adopter_id = other.id
AND moi.adopter_id <> toi.adopter_id
-- C'est moi!
AND moi.adopter_id = 1 -- 'Bob'
-- But moi should not own an animal that toi doesn't have
AND NOT EXISTS (
SELECT * FROM adopterpets lnx
WHERE lnx.adopter_id = moi.adopter_id
AND NOT EXISTS (
SELECT *
FROM adopterpets lnx2
WHERE lnx2.adopter_id = toi.adopter_id
AND lnx2.pet_id = lnx.pet_id
)
)
-- ... And toi should not own an animal that moi doesn't have
AND NOT EXISTS (
SELECT * FROM adopterpets rnx
WHERE rnx.adopter_id = toi.adopter_id
AND NOT EXISTS (
SELECT *
FROM adopterpets rnx2
WHERE rnx2.adopter_id = moi.adopter_id
AND rnx2.pet_id = rnx.pet_id
)
)
)
;
Result:
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "adopter_pkey" for table "adopter"
CREATE TABLE
INSERT 0 3
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pets_pkey" for table "pets"
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 6
adopter_id | pet_id | fname | kind
------------+--------+-------+------
1 | 1 | Bob | Dog
1 | 2 | Bob | Cat
2 | 1 | Alice | Dog
2 | 3 | Alice | Pig
3 | 1 | Chris | Dog
3 | 2 | Chris | Cat
(6 rows)
same_as_me
------------
Chris
(1 row)