Postgres error when updating row with value from another table - postgresql

I've being trying to update a row with a value from another table but I keep getting the same error:
table name "chats" specified more than once
I'm trying to insert a row into the messages table, and then use that timestamp (message_timestamp) to update the last_updated field in the chats table.
Any help would be appreciated! Been stuck on this for a day now:
WITH result AS
(INSERT INTO messages (user_id, chat_id, message_timestamp, users_read, message_text)
VALUES ($1, $2, NOW(), '{}', $3) RETURNING message_timestamp, chat_id)
UPDATE chats SET chats.last_updated=result.message_timestamp FROM result, chats WHERE chats.id=result.chat_id;
Edit:
On the other hand, removing chats from my FROM clause as so:
UPDATE chats SET chats.last_updated=result.message_timestamp FROM result WHERE chats.id=result.chat_id;
results in a different error:
column "chats" of relation "chats" does not exist
Which is weird considering I never call chats.chats
Edit 2: Create statement for the chat table:
CREATE TABLE IF NOT EXISTS chats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chat_name VARCHAR,
last_message TIMESTAMP NOT NULL
);

From UPDATE:
Do not include the table's name in the specification of a target
column
So the column last_updated that you want to update should not be qualified with the table's name like chats.last_updated:
UPDATE chats
SET last_updated = result.message_timestamp
FROM result
WHERE chats.id = result.chat_id;
See a simplified demo.

Related

Can I refer to data in jsonb table if no matching field in cross join table

I have json data in table that I use to insert new data into final table as follows
CREATE TABLE musicbrainz.acoustid_track (
id int NOT NULL,
created timestamp with time zone DEFAULT current_timestamp,
gid uuid NOT NULL,
new_id varchar(30)
);
CREATE TABLE musicbrainz.acoustid_track_json (
data jsonb
);
......
tables loaded
......
The json column data is visible in the query and you can refer to it in the WHERE clause, e.g.:
insert into musicbrainz.acoustid_track
select id, created, gid, new_id
from musicbrainz.acoustid_track_json
cross join jsonb_populate_record(null::musicbrainz.acoustid_track, data);
and this works except acoustid_track_json can contains new records or replacement records, and this is detemrined by if they have an updated field
e.g
New record
{"id":67028798,"gid":"18575a2d-bc9c-48c0-b5d7-f815b97421ed","created":"2020-02-03T00:02:11.315629+00:00"}
Updated record
{"id":66277512,"gid":"a31e1ecc-af48-4b8f-ba65-de5187a5c9a7","new_id":65603612,"created":"2019-11-17T12:37:49.81505+00:00","updated":"2020-02-03T13:12:58.043985+00:00"}
but I cant seem to modify INSERT to refer to updated field, possibly because no updated field in the final table, how do I do this.
The json column data is visible in the query and you can refer to it in the WHERE clause, e.g.:
insert into musicbrainz.acoustid_track
select id, created, gid, new_id
from musicbrainz.acoustid_track_json
cross join jsonb_populate_record(null::musicbrainz.acoustid_track, data)
where data->'updated' is null;

postgreql insert when no row exists

I have a table in postgresql named 'views', containing information about users viewing a classified ad.
CREATE TABLE views (
view_id uuid DEFAULT random_gen_uuid() NOT NULL,
user_id uuid NOT NULL,
ad_id uuid NOT NULL,
timestamp timestamp with time zone DEFAULT 'NOW()' NOT NULL
);
I want to be able to insert a row for a specific user/ad ONLY when there is no other row 'younger' than 5 minutes. So I want to check if there already is a row with the user ID and the ad ID and where the timestamp is less than 5 minutes old. If so, I want to do something like INSERT... ON CONFLICT DO NOTHING.
Is this possible to do with a UNIQUE constraint? Or do I need a CHECK constraint, or do I have to do a separate query first every time I insert this?
You have to do a lookup first, but you can do the lookup and the insert in one statement using something like this:
with invars (user_id, ad_id) as (
values (?, ?) -- Pass your two ids in
)
insert into views (user_id, ad_id)
select user_id, ad_id
from invars i
where not exists (select 1
from views
where (user_id, ad_id) = (i.user_id, i.ad_id)
and "timestamp" >= now() - interval '5 minutes');

How to insert and then update returned Id from insert query as returning id in a single command in postgres?

I have a demo table
CREATE TABLE items (
id SERIAL primary key,
user_id integer,
name character varying,
created timestamp with time zone default now()
);
And I want a single query to run and first insert data, then return primary key using returning id and then update the same table with the returned id.
INSERT INTO items (name) values ('pen') RETURNING id as idd
update items set user_id=(select idd) where id=(select idd)
but the above command doesn't work and throws syntax error.
Any help will be appriciated.
You can do that right within the INSERT statement:
INSERT INTO items
(name, user_id)
values
('pen', currval(pg_get_serial_sequence('items','id')));
Online example
You can try this way also :
create temp table insert_item as
with insert_item_cte as (
INSERT INTO items (name)
values ('pen') returning id
)
select id from insert_item_cte;
update items set user_id = items.id
from insert_item ii
where ii.id = items.id;
Online Demo

Postgres upsert avoid 2nd unique constraint?

I have a table of the form (based on):
create table foo (
name text unique,
ref_id int,
mod_time timestamptz
);
I would like to be able to insert into it, but
instead updating mod_time if (name, ref_id) pair was in use, and failing if name were in use for another ref_id.
I can do this by creating another unique constraint as follows:
alter table foo add unique (name, ref_id);
Then
insert into foo values ( $1, $2, $3 )
on conflict (name, ref_id) do update set
mod_time = excluded.mod_time;
will function as I want:
If new name, inserts
If same name and ref_id, changes mod_time
If same name for different ref_id, fails
However, the cost is a 2nd unique index, which is in fact superfluous for enforcing the constraint as records unique by name will automatically be unique by name and ref_id.
Is there some workaround that will get me this behavior without the 2nd index, and without additional roundtrips to the database?
It depends on what you mean by fail. If it's okay to just not do anything in this case: where If same name for different ref_id you could do the following:
insert into foo values ($1, $2, $3)
on conflict (name) DO update set
mod_time = excluded.mod_time
WHERE foo.ref_id = excluded.ref_id;
You could check how many rows were modified after running the query and raise an exception if none were modified.

Ambiguous column in PostgreSQL UPSERT (writeable CTE) using one table to update another

I have a table called users_import into which I am parsing and importing a CSV file. Using that table I want to UPDATE my users table if the user already exists, or INSERT if it does not already exist. (This is actually a very simplified example of something much more complicated I'm trying to do.)
I am trying to do something very similar to this:
https://stackoverflow.com/a/8702291/912717
Here are the table definitions and query:
CREATE TABLE users (
id INTEGER NOT NULL UNIQUE PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE users_import (
id INTEGER NOT NULL UNIQUE PRIMARY KEY,
name TEXT NOT NULL
);
WITH upsert AS (
UPDATE users AS u
SET
name = i.name
FROM users_import AS i
WHERE u.id = i.id
RETURNING *
)
INSERT INTO users (name)
SELECT id, name
FROM users_import
WHERE NOT EXISTS (SELECT 1 FROM upsert WHERE upsert.id = users_import.id);
That query gives this error:
psql:test.sql:23: ERROR: column reference "id" is ambiguous
LINE 11: WHERE NOT EXISTS (SELECT 1 FROM upsert WHERE upsert.id = us...
^
Why is id ambiguous and what is causing it?
The RETURNING * in the WITH upsert... clause has all columns from users and all columns from the joined table users_import. So the result has two columns named id and two columns named name, hence the ambiguity when refering to upsert.id.
To avoid that, use RETURNING u.id if you don't need the rest of the columns.