PostgreSQL: How to check if a list is contained in another list? - postgresql

I'm working with PostgreSQL 13.
I have two tables like this:
permission_table
name
permission
Ann
Read Invoice
Ann
Write Invoice
Ann
Execute Payments
Bob
Read Staff data
Bob
Modify Staff data
Bob
Execute Payroll
Carl
Read Invoice
Carl
Write Invoice
risk_table
risk_id
permission
Risk1
Read Invoice
Risk1
Write Invoice
Risk1
Execute Payments
Risk2
Read Staff data
Risk2
Modify Staff data
Risk2
Execute Payroll
I'd like to create a new table containing the names of the employees of the first table whose permissions are pointed as risks in the second table. After the execution, the results should be like this:
name
risk_id
Ann
Risk1
Bob
Risk2
Since Carl only has two of the three permissions belonging to Risk2, he will not be included in the results.
My first brute force approach was to compare the list of permissions belonging to a risk to the permissions belonging to an employee. If the first list is included in the second one, then that combination of employee/risk will be added to the results table.
INSERT INTO results_table
SELECT a.employee, b.risk_id FROM permission_table a, risk_table b WHERE
((SELECT permission FROM risk_table c WHERE b.permission = c.permission ) EXCEPT
(SELECT permission FROM permission_table d WHERE a.employee=d.employee)
) IS NULL;
I'm not sure if the results could be correct using this approach, because if the tables are big, it takes a very long time even if I add a WHERE clause limiting the query to just one employee.
Could you please help?

One way of approaching this one is by
computing the amount of permissions for each "risk_id" value
joining the "permissions" and "risks" table with counts on matching "permission" values
making sure that the distinct count of permissions for each triplet "<permissions.name, risks.risk_id, risks.cnt>" corresponds to the full amount of permissions.
WITH risks_with_counts AS (
SELECT *, COUNT(permission) OVER(PARTITION BY risk_id) AS cnt
FROM risks
)
SELECT p.name, r.risk_id
FROM permissions p
INNER JOIN risks_with_counts r
ON p.permission = r.permission
GROUP BY p.name, r.risk_id, r.cnt
HAVING COUNT(DISTINCT r.permission) = r.cnt
Carl won't be included in the output as he doesn't have all permissions from "risk_id = 'Risk 1'"
Check the demo here.

Related

Postgres SELECT id LAG(id) OVER (ORDER BY id) FROM invoice WHERE id = 2

I've looked all over the internet and I fail to get this query running as expected.
I've got a table of invoices and some invoices are related to one another because they belong to the same project.
My ticket says I've got to get the PREVIOUS invoice based on a provided invoice.
Say Project A has 10 invoices, and I'm looking at invoice #4, I've got to write a query which will return the ID of the previous Invoice. Bear in mind, the invoice table is home to all sorts of projects, and each project could have many invoices on their own, so I want to avoid getting many IDs back and then iterating over them.
To illustrate the issue, I've written this fiddle.
It works somewhat acceptably when I don't filter for steps.id, but that means returning hundreds of IDs to sift through.
I've tried and tried but I can't seem to get the column previousStep to be kind of bound to the ID column.
Simply find the invoice with the next largest id for the same project:
SELECT inv2.id
FROM invoice AS inv1
JOIN invoice AS inv2
ON inv1.project = inv2.project AND inv1.id > inv2.id
WHERE inv1.id = 1057638
ORDER BY inv2.id DESC
LIMIT 1;

Rooms per user in matrix synapse database

How can I get the total number of matrix rooms a user is currently joined using the synapse postgres database? (excluding those rooms the user has left or been kicked, or been banned from)
I spent several hours looking for this, so I think maybe it can help others.
You can get the number of rooms a user is currently joined querying the table user_stats_current:
SELECT joined_rooms FROM user_stats_current WHERE user_id='#myuser:matrix.example.com';
And if you want to get specifically the ids of the rooms the user is currently joined, you can use the table current_state_events like in this query:
SELECT room_id FROM current_state_events
WHERE state_key = '#myuser:matrix.example.com'
AND type = 'm.room.member'
AND membership = 'join';
Even further, if you want not only the room id but the room name as well, you can add the table room_stats_state like in this other query:
SELECT e.room_id, r.name
FROM current_state_events e
JOIN room_stats_state r USING (room_id)
WHERE e.state_key = '#myuser:matrix.example.com'
AND e.type = 'm.room.member'
AND e.membership = 'join';

How do I set my hasura permission to only see the rows of my table corresponding to a user?

Here's the thing. I have the 3 tables depicted here:
People on my application can place orders. Then, I want
a user with rex permission to see all the orders table's rows
a user with delivery permission to only see the rows of the orders table that have the zip column set to the delivery user's zip
From the orders table, I can get for each order a zip. With the table zip_user, I can get a user_id out of a zip. Out of that user_id, I can get the delivery user from the users table.
While it is trivial to get the rex to see all of the orders table, I have not yet been able to configure the permissions for the delivery user. What do I need to do?
In other words, given the user performing a select on the orders table has x-hasura-user-id set to some user id and x-hasura-role set to delivery, how does that user get only the rows from the orders table that match with the zips associated with that user's user_id?
Hasura has the concept of relations. If you have foreign keys, it makes the relations automatically, if not you can make them yourself in the UI. Once the relationships have been set up, you will be able to set deep permissions, so on the orders table, you'll be able to use users.id.
Start here: https://hasura.io/docs/1.0/graphql/manual/schema/relationships/index.html

TSQL - Deleting with Inner Joins and multiple conditions

My question is a variation on one already asked and answered (TSQL Delete Using Inner Joins) but I have a different level of complexity and I couldn't see a solution to it.
My requirement is to delete Special Prices which haven't been accessed in 90 days. Special Prices are keyed on Customer ID and Product ID and the products have to matched to a Customer Order Detail table which also contains a Customer ID and a Product ID. I want to write one function that will look at the Special Price table for each Customer, compare each Product for that Customer with the Customer Order Detail table and if the Maximum Order Date is more than 90 days earlier than today, delete it from the Special Price table.
I know I can use a CURSOR (slow but effective) but would prefer to have a single query like the one in the TSQL Delete Using Inner Joins example. Any ideas and/or is more information required?
I cannot dig more on the situation of your system but i think and if it is ok for you, check MERGE STATEMENT, it might be a help instead of using cursors. check this Link MERGE STATEMENT

products and configurable_products in postgresql

I have a Product table and a ConfigurableProduct table.
If there are several variations of the same product like a shirt in different colors I create a ConfigurableProduct.
When a user is looking at the catalog he should see a list of products unless there is a ConfigurableProduct, then he should see it with a select box for each variations.
How do I structure the tables for Product and ConfigurableProduct and how do I query the db so I can page through the results?
Thanks
I am going to answer this as if you do not have tables created. I am not sure if that is true though.
The following is a simple example, but I assume you have more data.
products
id
name
configurable_products
id
variation
product_id REFERENCES products(id)
You can just make the configurable products a reference to products.
If you want a listing of products with their configurations then you can do:
select p.name, c.variation
from products p left outer join configurable_products c
on (p.id = c.product_id);
Of course you can just search for all the configurable_products based on the product id too when needed.
As for the paging part of your question you will have to clarify what you mean. You can use limit to limit results if you don't want to get everything at once.