SQL Exclude Records with Leveling - sql

For example, I have a table like this:
+---------+-------+----------+
| sort_id | level | security |
+---------+-------+----------+
| 1 | 1 | A |
| 2 | 2 | A |
| 3 | 3 | U |
| 4 | 4 | A |
| 5 | 5 | A |
| 6 | 3 | A |
| 7 | 4 | U |
| 8 | 5 | A |
| 9 | 6 | A |
| 10 | 7 | A |
| 11 | 3 | A |
| 12 | 3 | A |
+---------+-------+----------+
Security column is A for Authorized and U for Unauthorized. I need to exclude those records under the Unauthorized records based on their level.
For a better picture of the SQL records, it looks like this:
Those pointed with arrow are the Unauthorized records and we should exclude those under it.
So the SQL result should be the following table:
+---------+-------+----------+
| sort_id | level | security |
+---------+-------+----------+
| 1 | 1 | A |
| 2 | 2 | A |
| 3 | 3 | U |
| 6 | 3 | A |
| 7 | 4 | U |
| 11 | 3 | A |
| 12 | 3 | A |
+---------+-------+----------+
How can we produce it using a simple Select statement? Thanks in advanced! Just comment if something is unclear.

If I understand "under the unauthorized records" as meaning a sequence of records with increasing id`s following the unauthorized records (based on the id), then here is an approach:
select sort_id, level, security
from (select t.*, min(case when authorized = 'U' then id end) over (partition by grp) as minuid
from (select t.*,
(row_number() over (order by id) - level) as grp
from table t
) t
) t
where id > minuid;

Related

Postgresql: Group rows in a row and add array

Hi i have a table like this;
+----+----------+-------------+
| id | room_id | house_id |
+----+----------+-------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 1 |
| 4 | 1 | 2 |
| 5 | 2 | 2 |
| 6 | 3 | 2 |
| 7 | 1 | 3 |
| 8 | 2 | 3 |
| 9 | 3 | 3 |
+----+-------+----------------+
and i want to create a view like this
+----+----------+-------------+
| id | house_id | rooms |
+----+----------+-------------+
| 1 | 1 | [1,2,3] |
| 2 | 2 | [1,2,3] |
| 3 | 3 | [1,2,3] |
+----+-------+----------------+
i tried many ways but i cant gruop them in one line
Thanks for any help.
You can use array_agg():
select house_id, array_agg(room_id order by room_id) as rooms
from t
group by house_id;
If you want the first column to be incremental, you can use row_number():
select row_number() over (order by house_id) as id, . . .

Add Index to postgreSQL query result

My query result looks like this:
| A | B |
|-------|
| 1 | 2 |
| 1 | 4 |
| 1 | 6 |
| 1 | 9 |
| 1 | 1 |
| 1 | 6 |
| 1 | 9 |
Now I want to increase column A by the index of the result table, so the result would become like this:
| A | B |
|-------|
| 2 | 2 |
| 3 | 4 |
| 4 | 6 |
| 5 | 9 |
| 6 | 1 |
| 7 | 6 |
| 8 | 9 |
How can I do it?
Thanks!
You want row_number()
select (row_number() over (order by a) + 1) as A, b
from table t;
Maybe something like that:
SELECT
(row_number() OVER (ORDER BY A) + A) AS columnAIndex,
columnB
FROM ...
I don't have a PostgreSQL client installed here, therefore, i don't tested this query.

Row Level Security with multiple users for one row

I'm trying to implement Row Level Security in SQL Server 2016.
The problem is, I can have multiple users that should have read permissions over given rows, and when I write some complex condition in the predicate the performance gets like very very very bad.
I tried to keep all usernames in one column of the table and in the predicate to search through them for the SYSTEM_USER with % LIKE % but performance is low.
Example of the values in the Usernames column in my controlled table for one row:
domain\john.wick;domain\red.eagle;domain\spartak.something....
Here is my function:
CREATE FUNCTION fn_securitypredicate(#Usernames AS nvarchar(4000))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT 1 as Result
WHERE #Usernames LIKE '%' + SYSTEM_USER + '%'
With this execution time from 2 sec became 50 sec. Any suggestions for improvement.
CREATE SECURITY POLICY [Policy]
ADD FILTER PREDICATE [fn_securitypredicate]([Usernames])
ON [dbo].[Products];
This is the solution I came up with for my previous team.
This requires a a users table, a users permissions table as well as a permission column on your controlled table. It should also have a user group and user group permissions table to scale with users.
users user_permissions controlled_table
+-----------+---------+ +---------+---------------+ +---------------+------+------+
| user_name | user_id | | user_id | permission_id | | permission_id | pk_1 | pk_2 |
+-----------+---------+ +---------+---------------+ +---------------+------+------+
| admin | 1 | | 1 | 0 | | 2 | 1 | 1 |
| user1 | 2 | | 2 | 1 | | 2 | 1 | 2 |
| user2 | 3 | | 2 | 2 | | 3 | 1 | 3 |
| user3 | 4 | | 2 | 3 | | 4 | 2 | 1 |
| | | | 2 | 4 | | 3 | 2 | 2 |
| | | | 3 | 1 | | 1 | 2 | 3 |
| | | | 3 | 2 | | 1 | 3 | 1 |
| | | | 4 | 2 | | 5 | 3 | 2 |
| | | | 4 | 3 | | 4 | 3 | 3 |
| | | | 4 | 4 | | 2 | 4 | 1 |
| | | | | | | 3 | 4 | 2 |
| | | | | | | 3 | 4 | 3 |
+-----------+---------+ +---------+---------------+ +---------------+------+------+
For performance, you will want to add the permission_id to whatever index you were using to search the controlled table. This will allow you to join permissions on the index while searching on the remaining columns. You should view the execution plan for specific details on your indexes.

Limit a sorted number of rows joined

I have two tables, A and B, and a join table M. I want to, for each A.id, get the top 2 B.id's sorting on the value in table M, producing the results below. This is running on an Azure SQL database
Table A Table M Table B
+-----+ +-----+-----+-------+ +-----+
| Id | | AId | BId | Value | | Id |
+-----+ +-----+-----+-------+ +-----+
| 1 | | 1 | 3 | 4 | | 1 |
| 2 | | 1 | 2 | 3 | | 2 |
| 3 | | 3 | 2 | 3 | | 3 |
| 4 | | 3 | 5 | 6 | | 4 |
+-----+ | 3 | 3 | 4 | | 5 |
| 4 | 1 | 2 | +-----+
| 4 | 2 | 1 |
| 4 | 4 | 3 |
+-----+-----+-------+
Result
+-----+-----+-------+
| AId | BId | Value |
+-----+-----+-------+
| 1 | 3 | 4 |
| 1 | 2 | 3 |
| 3 | 5 | 6 |
| 3 | 3 | 4 |
| 4 | 1 | 2 |
| 4 | 4 | 3 |
+-----+-----+-------+
I know that I can select all the M.AId rows where they equal 1, sort it, and limit by 2, but I need to do this for every row in Table A. I've made an attempt to use group by, but I wasn't sure how to sort and limit it. I've also tried to search for resources associated with this issue but I couldn't find any resources.
(I also wasn't sure how to word the title for this issue)
You can just use ROW_NUMBER:
SELECT
AId, BId, Value
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY AId ORDER BY Value DESC)
FROM M
) t
WHERE Rn <= 2

Selecting all rows in a master table and summing columns in multiple detail tables

I have a master table (Project List) along with several sub tables that are joined on one common field (RecNum). I need to get totals for all of the sub tables, by column and am not sure how to do it. This is a sample of the table design. There are more columns in each table (I need to pull * from "Project List") but I'm showing a sampling of the column names and values to get an idea of what to do.
Project List
| RecNum | Project Description |
| 6 | Sample description |
| 7 | Another sample |
WeekA
| RecNum | UserName | Day1Reg | Day1OT | Day2Reg | Day2OT | Day3Reg | Day3OT |
| 6 | JustMe | 1 | 2 | 3 | 4 | 5 | 6 |
| 6 | NotMe | 1 | 2 | 3 | 4 | 5 | 6 |
| 7 | JustMe | | | | | | |
| 7 | NotMe | | | | | | |
WeekB
| RecNum | UserName | Day1Reg | Day1OT | Day2Reg | Day2OT | Day3Reg | Day3OT |
| 6 | JustMe | 7 | 8 | 1 | 2 | 3 | 4 |
| 6 | NotMe | 7 | 8 | 1 | 2 | 3 | 4 |
| 7 | JustMe | | | | | | |
| 7 | NotMe | | | | | | |
So the first query should return the complete totals for both users, like this:
| RecNum | Project Description | sumReg | sumOT |
| 6 | Sample description | 40 | 52 |
| 7 | Another sample | 0 | 0 |
The second query should return the totals for just a specified user, (WHERE UserName = 'JustMe') like this:
| RecNum | Project Description | sumReg | sumOT |
| 6 | Sample description | 20 | 26 |
| 7 | Another sample | 0 | 0 |
Multiple parallel tables with the same structure is usually a sign of poor database design. The data should really be all in one table, with additional columns specifying the week.
You can, however, use union all to bring the data together. The following is an example of a query:
select pl.recNum, pl.ProjectDescription,
sum(Day1Reg + Day2Reg + Day3Reg) as reg,
sum(Day1OT + Day2OT + Day3OT) as ot
from ProjectList pl join
(select * from weekA union all
select * from weekB
) w
on pl.recNum = w.recNum
group by l.recNum, pl.ProjectDescription,;
In practice, you should use select * with union all. You should list the columns out explicitly. You can add appropraite where clauses or conditional aggregation to get the results you want in any particular case.