select values or NULL if it does not exists - sql

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

Related

Check if value of referenced row matches value in current row

See the following database structure:
v---------------------------------------------------|
v----------------------------|---------------------------| |
+---------------+ +----+---------+------+ +----+---------+---------+-----+
| id | username | | id | user_id | tag | | id | user_id | message | tag |
+----+----------+ +----+---------+------+ +----+---------+---------+-----+
| 1 | User1 | | 1 | 1 | tech | | 1 | 1 | Test1 | 1 |
| 2 | User2 | | 2 | 1 | news | | 2 | 2 | Test2 | 1 |
+----+----------+ +----+---------+------+ +----+---------+---------+-----+
users tags messages
tags.user_id and messages.user_id both reference users.id. messages.tag references tags.id.
Users have tags available (rows in tags where rows.user_id = users.id) and messages (rows in messages where messages.user_id = users.id).
The problem is that any tag can be "attached" to the message, instead of only tags that are owned by the user. So I need an extra restriction that ensures that the tag referenced in messages.tag not only exists (foreign key restriction), but is also owned by the same user as the message itself (messages.user_id = tags.user_id).
I have not found a way yet to achieve this restriction, which is why I'm asking help.
python: 3.8.10
sqlite3.version: 2.6.0
sqlite3.sqlite_version: 3.31.1
From the manual creating a composite FK in Sqlite3 looks like:
CREATE TABLE parent(a PRIMARY KEY, c, d, e, f);
CREATE UNIQUE INDEX i1 ON parent(c, d);
CREATE TABLE child3(j, k, FOREIGN KEY(j, k) REFERENCES parent(c, d));

PostgreSQL row-level security involving foreign key with other table

I wonder if the following is possible in PostgreSQL using RLS (or any other mechanism). I want a user to be able to get certain rows of a table if its id matches a column in another table.
For e.g. we have following tables:
"user" table:
columns: id, name
| id | name |
| --- | --- |
| 1 | one |
| 2 | two |
| 3 | three|
| 4 | four |
"tenant" table:
columns: id, name
| id | name |
| --- | --- |
| 1 | t1 |
| 2 | t2 |
"user_tenant" table:
columns: user_id, tenant_id
| user_id | tenant_id|
| --- | --- |
| 1 | t1 |
| 2 | t2 |
| 3 | t1 |
| 4 | t2 |
Now I want only users who has same tenant_id.
output:
| id | name |
| --- | --- |
| 1 | one |
| 3 | three|
To achieve this, I need to create policy something like this:
CREATE POLICY tenant_policy ON "user" USING (tenant_id = current_setting('my_user.current_tenant')::uuid);
but with above policy it's not working as I am getting all users.
Note: user & tenant table have many-to-many relationship.
P.S. I know we can do this either by join or some other condition. But I want to achieve the above output using PostgreSQL using RLS(row level security)
Thanks in advance!!
If row level security is not working that may be because one of the following applies:
you didn't enable row level security:
ALTER TABLE "user" ENABLE ROW LEVEL SECURITY;
the user owns the table
You can enable row level security for the owner with
ALTER TABLE "user" FORCE ROW LEVEL SECURITY;
you are a superuser, which is always exempt from RLS
you are a user defines with BYPASSRLS
the parameter row_security is set to off
Other than that, you will probably have to join with user_tenant in your policy:
CREATE POLICY tenant_policy ON "user"
USING (
EXISTS(SELECT 1 FROM user_tenant AS ut
WHERE ut.user_id = "user".id
AND ut.tenant_id = current_setting('my_user.current_tenant')::uuid
)
);

How to extract values from multiple tables in SQL an and IN condition?

I have three tables with data for which I want to get a combined overview created using an SQL query.
The first table "Epics" conists of a column with key values and another column which contains a list of keys.
The second and third tables contain a status for each of these keys.
The tables can be illustrated like follows:
Epics
Key Sub-Tasks
+--------+----------------+
| MCR-1 | MCR-21, MCR-31 |
+--------+----------------+
| MCR-2 | MCR-22, MCR-32 |
+--------+----------------+
| MCR-3 | MCR-23, MCR-33 |
+--------+----------------+
QM Sub-Tasks
Key Status
+--------+-------------+
| MCR-21 | DONE |
+--------+-------------+
| MCR-22 | OPEN |
+--------+-------------+
| MCR-23 | IN PROGRESS |
+--------+-------------+
E3 Sub-Tasks
Key Status
+--------+--------------+
| MCR-31 | NOT RELEVANT |
+--------+--------------+
| MCR-32 | DONE |
+--------+--------------+
| MCR-33 | OPEN |
+--------+--------------+
Now I created the following SQL statement:
SELECT epics.'Key' AS 'MCR-Key'
qm.'Status' AS 'QM Status'
e3.'Status' As 'E3 Status'
FROM T3 epics
LEFT JOIN T1 qm ON (qm.'Key' IN epics.'Sub-Tasks')
LEFT JOIN T2 e3 ON (e3.'Key' IN epics.'Sub-Tasks')
However, by using this statement, only the first row contains the status values of tables two and three, but all subsequent rows only contain the epic's key:
Output
MCR-Key QM Status E3 Status
+---------+--------------+-----------+
| MCR-1 | NOT RELEVANT | DONE |
+---------+--------------+-----------+
| MCR-2 | | |
+---------+--------------+-----------+
| MCR-3 | | |
+---------+--------------+-----------+
Any idea why this is the case and how I can resolve this issue?
I must 1st split string from epics.sub-tasks and left part to join with QM sub-tasks table, right to join with E3 sub-tasks table.
The answer will be:
SELECT
e.[Key] 'MCR-Key',
q.[Status] 'QM Status',
e3.[status] 'E3 Status'
FROM Epics e
LEFT JOIN [QM Sub-tasks] Q
ON Q.[Key] = SUBSTRING(e.[sub-tasks], 0, CHARINDEX(',', e.[sub-tasks]))
LEFT JOIN [E3 Sub-tasks] E3
ON E3.[Key] = right(e.[sub-tasks], charindex(',', reverse(e.[sub-tasks])) - 2)
Here you can check my demo on DB<>FIDDLE
And .... I must say something more
NEVER NAME YOUR COLUMN WITH RESERVED WORDS
and do NOT use hyphen (-) into column name

Insert into row if value exists in a different table

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

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