Insert into row if value exists in a different table - sql

I have two tables:
Users:
+---------------------+---------------------+
| UserId | ValueToUpdate |
+---------------------+---------------------+
| 1 | |
| 2 | |
| 3 | |
+---------------------+---------------------+
Subscribers:
+---------------------+
| UserId |
+---------------------+
| 1 |
| 2 |
+---------------------+
I need to write a SQL query that will insert some value into the Users table, column (ValueToUpdate) if the user id in the Users table exists in the Subscribers table.
Essentially I'm looking for something like this
UPDATE Users
SET ValueToUpdate = "some value"
WHERE (UserId from the Users table exists in the Subscribers table);
How could I achieve this?

You can do this using an exists clause:
UPDATE Users u
SET ValueToUpdate = 'some value'
WHERE EXISTS (SELECT 1 FROM Subscribers S WHERE s.userid = u.userid);

Related

select values or NULL if it does not exists

I have a table user with columns id (primary key) and name (unique constraint) containing:
+----+------+
| id | name |
+----+------+
| 1 | u1 |
| 2 | u2 |
| 3 | u3 |
+----+------+
I would like a make a query, something like SELECT id, name FROM user WHERE name IN (?,?), with parameters ("u2", "u4"), that I can reuse in CTE, but which returns:
+------+------+
| id | name |
+------+------+
| 2 | u2 |
| NULL | u4 |
+------+------+
(edit: s/NULL/u4).
Is it possible? I'm interested in MariaDB and PostgreSQL.
One use case would be to insert the ids result in another table where NULL is not accepted, so it would insert the names and check for invalid values in only 1 request. (no need for 2 separates requests + application code).
There are different ways.. An easy one in Postgresql could be:
select a, "name"
from unnest(ARRAY['u2', 'u4']::text[]) AS a
LEFT JOIN "user" AS u ON a = u."name";

SQL DELETE From with sub query syntax error

I am having difficulty runing SQL code in MS Access 2010. I would like to ask for help in reviewing and correcting it.
Data:
- two tables with names: Tbl_001_WholeBase and Tbl_002_NewKVG
- they have connected by column named Key
Problem:
I want to delete all rows from Tbl_001_WholeBase which have Key that is not available in table Tbl_002_NewKVG
Example:
Tbl_001_WholeBase
ID Key
1 Hronic1
2 Hronic2
3 Hronic3
Tbl_002_NewKVG
ID Key
1 Hronic1
2 Hronic2
So as a result I would like to leave in Tbl_001_WholeBase only 3rd record, base would look like this:
ID Key
3 Hronic3
What I wanted to use in Access was:
DELETE
FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.KEY IN
(SELECT *
FROM Tbl_001_WholeBase
LEFT JOIN Tbl_002_NewKVG
ON Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
WHERE (((Tbl_002_NewKVG.Key) Is Null)));
The subquery is working properly, however I can't connect it with Delete statement.
Error I got when running this code is:
You have written a subquery that can return more than one field without using the Exists reserved word in the main query's FROM clause. Revise the SELECT statement of the subquery to request only one field.
Select a key in subquery instead of (*)
DELETE
FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.KEY IN
(SELECT keyId
FROM Tbl_001_WholeBase
LEFT JOIN Tbl_002_NewKVG
ON Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
WHERE (((Tbl_002_NewKVG.Key) Is Null)));
Here keyId will be your column name or your unique key through which you want to delete the row.
Join the two tables using a LEFT JOIN.
This will return all records from the table on the left of the join, and any matching records from the table on the right. NULL is used where the record on the right isn't available.
SELECT *
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
| Tbl_001_WholeBase.ID | Tbl_001_WholeBase.Key | Tbl_001_NewKVG.ID | Tbl_001_NewKVG.Key |
|----------------------|-----------------------|-------------------|--------------------|
| 1 | Hronic1 | 1 | Hronic1 |
| 2 | Hronic2 | 2 | Hronic2 |
| 3 | Hronic3 | NULL | NULL |
You can see that the last NewKVG.Key is NULL, so you can omit it from the results:
SELECT *
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
WHERE NOT Tbl_001_NewKVG.Key IS NULL
| Tbl_001_WholeBase.ID | Tbl_001_WholeBase.Key | Tbl_001_NewKVG.ID | Tbl_001_NewKVG.Key |
|----------------------|-----------------------|-------------------|--------------------|
| 1 | Hronic1 | 1 | Hronic1 |
| 2 | Hronic2 | 2 | Hronic2 |
Or you can delete it from the table:
DELETE DISTINCTROW Tbl_001_WholeBase.*
FROM Tbl_001_WholeBase LEFT JOIN Tbl_001_NewKVG ON Tbl_001_WholeBase.Key = Tbl_001_NewKVG.Key
WHERE NOT Tbl_001_NewKVG.Key IS NULL
| ID | Key |
|----------|----------|
| #Deleted | #Deleted |
| #Deleted | #Deleted |
| 3 | Hronic3 |
Use EXISTS:
DELETE FROM Tbl_001_WholeBase
WHERE EXISTS (SELECT 1
FROM Tbl_002_NewKVG
WHERE Tbl_001_WholeBase.Key = Tbl_002_NewKVG.Key
);
Or use IN with no JOIN:
DELETE FROM Tbl_001_WholeBase
WHERE Tbl_001_WholeBase.Key IN (SELECT Tbl_002_NewKVG.Key
FROM Tbl_002_NewKVG
);

Reducing a Postgres table to JSON

I have a following table in Postgres
+----+-----------+----------------------+---------+
| id | user_fk| language_fk | details |
+----+-----------+----------------------+---------+
| 1 | 2 | en-us | 123 |
| 2 | 3 | en-us | 456 |
| 3 | 4 | en-us | 789 |
| 4 | 4 | es-la | 012 |
+----+-----------+----------------------+---------+
And I want to reduce this to the following SQL statement:
UPDATE users SET details = '{"en-us": "789", "es-la": "012"}' WHERE id = 4;
UPDATE users SET details = '{"en-us": "123"}' WHERE id = 2;
UPDATE users SET details = '{"en-us": "456"}' WHERE id = 3;
So I want to reduce languages per user and put it in a different table. Is there a way to do this in Postgres?
Use the function jsonb_object_agg() to get the expected output:
select
min(id) as id,
user_fk,
jsonb_object_agg(language_fk, details) as details
from users
group by user_fk
id | user_fk | details
----+---------+----------------------------------
1 | 2 | {"en-us": "123"}
2 | 3 | {"en-us": "456"}
3 | 4 | {"en-us": "789", "es-la": "012"}
(3 rows)
You cannot update the table in this way because of different types of old and new details column. Create a new table with reduced columns using create table from select:
create table new_users as
select
min(id) as id,
user_fk,
jsonb_object_agg(language_fk, details) as details
from users
group by user_fk;

SQL - get from table and join with same table

I have two tables, ChatRoom and ChatRoomMap, I want to get a list of chatrooms a user belongs to, along with all the other users in each chatroom.
// this contains a map of user to chatroom, listing which user is in what room
CREATE TABLE ChatRoomMap
(
user_id bigint NOT NULL,
chatroom_id text NOT NULL,
CONSTRAINT uniq UNIQUE (userid, roomid)
)
// sample values
==========================
| user_id | chatroom_id |
| 1 | 7 |
| 1 | blue |
| 7 | red |
==========================
And
CREATE TABLE ChatRoom
(
id text NOT NULL,
admin bigint,
name text,
created timestamp without time zone NOT NULL DEFAULT now(),
CONSTRAINT uniqid UNIQUE (id)
)
// sample values
======================================================
| id | admin | name | timestamp |
| blue | 7 | blue room | now() |
| red | 2 | red | now() |
| 7 | 11 | mine | now() |
======================================================
To get a list of rooms a user is in, I can do:
SELECT DISTINCT ON (id) id, userid, name, admin
FROM ChatRoomMap, ChatRoom WHERE ChatRoomMap.user_id = $1 AND ChatRoomMap.chatroom_id = ChatRoom.id
This will get me a distinct list of chat rooms a user is in.
I would like to get the distinct list of rooms along with all the users in each room (concatenation of all as a separate column), how can this be done?
Example result:
=======================================================
| user_id | chatroom_id | name | admin | other_users |
| 10 | 7 | One | 1 | 1, 2, 3, 8 |
| 10 | 4 | AAA | 10 | 7, 11, 15 |
=======================================================
First up, use proper joins - the explicit join syntax was introduced to the SQL92 standard and the major vendors implemented it in the early 2000's (and it's the only way to achieve an outer join).
Try this:
SELECT DISTINCT id, crm2.user_id, name, admin,
FROM ChatRoomMap crm1
JOIN ChatRoom ON crm1.chatroom_id = ChatRoom.id
LEFT JOIN ChatRoomMap crm2 ON crm2.chatroom_id = crm1.chatroom_id
AND crm2.user_id != crm1.user_id -- only other users
WHERE crm1.user_id = $1
The LEFT JOIN is needed in case there are no other users in the room it will still list the room (with a null for other user id).

Postgres Array Prefix Matching

I have an array search in Postgres hat matches at least one tag as this:
SELECT * FROM users WHERE tags && ['fun'];
| id | tags |
| 1 | [fun,day] |
| 2 | [fun,sun] |
It is possible to match on prefixes? Something like:
SELECT * FROM users WHERE tags LIKE 'f%';
| id | tags |
| 1 | [fun,day] |
| 2 | [fun,sun] |
| 3 | [far] |
| 4 | [fin] |
try this
create table users (id serial primary key, tags text[]);
insert into users (tags)
values
('{"fun", "day"}'),
('{"fun", "sun"}'),
('{"test"}'),
('{"fin"}');
select *
from users
where exists (select * from unnest(tags) as arr where arr like 'f%')
SQL FIDDLE EXAMPLE
Here's a working example that should get you more or less what you're after. Note that I am not claiming that this approach will scale...
create table users (
id serial primary key,
tags text[] not null
);
insert into users (tags) values
('{"aaaa","bbbb","cccc"}'::text[]),
('{"badc","dddd","eeee"}'::text[]),
('{"gggg","ffbb","attt"}'::text[]);
select *
from (select id,unnest(tags) arr from users) u
where u.arr like 'a%';
id | arr
----+------
1 | aaaa
3 | attt