An existing SQL Server 2014 database stores permissions for users in a 32 bit integer.
bit 0 = Read files
bit 1 = Move files
bit 2 = Delete files
bit 3 = Save files
etc.
If the bit is 1 the permission is set, if the bit = 0 the permission is not set.
I need a query to show all permissions set for a given user. I have a table that has a list of all 32 permissions and am not sure of how to join the user table with the permissions table to get a list.
For example, if User Joe has permission INT = 3 then the query should return 2 rows:
Read Files
Move Files
If user Peter has permission INT = 8 then the query should return only 1 row:
Save Files
If user Dave has permission INT = 7 then the query should return 3 rows:
Read files
Move Files
Delete files
The permissions table has 3 columns: bit#, value, description
0 0 Read Files
1 2 Move Files
2 4 Delete Files
3 8 Save Files
etc.
I can easily change the design of the permissions table if different or additional columns would be helpful.
One way is to store the flags in a table (maybe you already have one):
CREATE TABLE dbo.PermissionDefinitions
(i int, Permission varchar(32));
INSERT dbo.PermissionDefinitions(i, Permission)
VALUES
(1, 'Read files'),
(2, 'Move files'),
(4, 'Delete files'),
(8, 'Save files'); -- ... more ...
And you have a Users table with a column containing their permission set:
CREATE TABLE dbo.Users(Username sysname, PermissionSet int);
INSERT dbo.Users VALUES(N'Joe', 3), (N'Suzy', 15);
Then you can just cross join all those with bitwise and:
SELECT u.Username, ps.Permission
FROM dbo.Users AS u
CROSS JOIN dbo.PermissionDefinitions AS ps
WHERE u.PermissionSet & ps.i = ps.i;
Example fiddle.
You can also use INNER JOIN, same results, same plan, etc.
SELECT u.Username, ps.Permission
FROM dbo.Users AS u
INNER JOIN dbo.PermissionDefinitions AS ps
ON u.PermissionSet & ps.i = ps.i;
Related
We have a data collection program that dynamically creates tables for data storage based on the identity value from another table. For example if 15 devices are created then the Devices table would have 15 entries (name, address, etc) and the DeviceID value would be say 134 - 149 then 15 tables would be created called Dev134 through Dev149.
Occasionally an issue occurred where some DEV tables were deleted but the record in the device table was not deleted leaving a orphan entry in the devices table. I.e. there is a DeviceID = 1245, but there is no table Dev1245.
What we would like to do is go through the Devices table and see if there is a corresponding Dev table in the database, and if not list the ID.
I have done this through a separate program, pulling the DeviceID's from the Device table into a list and then doing a
SELECT *
FROM #DeviceID
(#DeviceID = "Dev" + DeviceID)
and if I get something I know it's there and if I return nothing it's missing but I was hoping to do this with a single select statement that would return the ID of the missing tables.
You can select table information from sys.tables:
https://learn.microsoft.com/en-us/sql/relational-databases/system-catalog-views/sys-tables-transact-sql?view=sql-server-ver16
This statement should give you all entries which misses the corresponding table:
SELECT [devices].*
FROM Devices AS [devices]
LEFT JOIN sys.tables AS [tables]
ON [devices].[name] = [tables].[name]
WHERE [tables].[name] IS NULL
SELECT 'Dev'+CAST(deviceId AS VARCHAR(10))
FROM devices
WHERE NOT EXISTS (SELECT * FROM sys.tables WHERE name='Dev'+CAST(deviceId AS VARCHAR(10)));
Here is DBFiddle demo
query2 = "SELECT k.key_tag, k.key_avail
FROM keyinfo k
JOIN keyingroup kig ON k.key_id = kig.key_id
WHERE kig.group_id = (
SELECT uig.group_id
FROM useringroup uig
JOIN users u ON uig.user_id = u.user_id
WHERE u.user_csn = '" & GV.userCSN & "')"
keyingroup table
useringroup table
Other than the 2 tables shown in the images, there is a keyinfo table, a accessgroup table and a users table. The idea here is that the 2 images is made as a 'many to many' relation table. Where in the table in image 1 is to show that a key can be accessed by multiple access groups while in the second image users can have multiple access groups and as long as a key falls within that access group, the user has permission to draw/loan it. The SQL statement above is what I have right now but it is not returning any rows at all and I have no idea if the way I'm querying is even right. Can someone provide some pointers as to what could be wrong and what I should have done?
I have two tables one is master and another one is details table , Details table contain master table id as reference
Here is my table
**Table_Customer**
CustomerId CustomerName
1 A
2 B
**Table_CustomerRelatives**
RelativesId CustomerId RelativesName Address
1 1 M xyz
2 1 N mno
3 2 L pqr
4 2 O ghy
Here CustomerId and RelativesId are identity columns so automatic generating columnvalue
Here I have two temp tables like
**TembreryTableCustomer**
CustomerId CustomerName
1 F
2 G
3 H
**TembreryTableDetails**
CustomerId RelativesName Address
1 S fgg
1 T dfg
2 U ghj
3 V jkl
3 W rty
How can Insert Temp table data to my tables with identity.
here i want to insert rows of temp table customer and get identity value then insert temp table details data using temp table customer identity.
You can use the output clause of the merge statement to build a mapping of the temporary customer's id to the new id.
declare #Map table (Old int, New int);
;merge Table_Customer dest
using TembreryTableCustomer src
on dest.CustomerName = src.CustomerName
when not matched then
insert (CustomerName) values (CustomerName)
output inserted.CustomerID, src.CustomerID
into #map;
insert Table_CustomerRelatives
(CustomerId, RelativesName, Address)
select m.New
, t.RelativesName
, t.Address
from TembreryTable2 t
join #Map m
on t.CustomerID = m.Old;
Example at SQL Fiddle.
There are several approaches depending on the version of SQL and your preference for where to put the work.
I find using sequences far easier to use than identity columns. You can use the same sequence in your temp tables as in your persistent tables so there's no "fixup" to do.
Barring that, you can insert into the persistent table with an output clause to capture the newly assigned IDs. From the link, check out example "E". The salient feature is to output persistent IDs alongside the temp table IDs.
I am trying to create an SQL Syntax to search my database for folders on the network that users can't access.
Let me explain:
My Database has 5 tables:
Table: Folders
FUID - INT - Unique ID
Path - Varchar - Example: E:\shared
STATUS - INT - 1 = good, 0 = stale
Table: Groups
GRUID - INT - Unique ID
Class - varchar - User or Admin Group (admin group being a sysadmin group)
Name - Example HR_Users
STATUS - INT - 1 = good, 0 = stale
Table: Users:
URUID - INT - Unique ID
Class - varchar - Standard User or Admin User (admin user being a sysadmin)
Name - Varchar - Example: Smith, John
STATUS - INT - 1 = good, 0 = stale
Table: UG_JOIN:
UID: Unique ID
GRUID - INT - Relationship to Groups
URUID - INT - Relationship to Users
STATUS - INT - 1 = good, 0 = stale
Table: ACLS:
UID - INT - Unique ID
FUID - INT - Relationship to Folders FUID
GRUID - INT - Relationship to Group GRUID
URUID - INT - Relationship to User URUID
ACCESS - VARCHAR - Type of Access, list, read, modify, full control
STATUS - INT - 1 = good, 0 = stale
The reason I have to have both the users and groups in the ACL table is because some users are directly assigned permissions at the folder, not by group.
Anyway, What I am trying to find out is:
What folders don't have any permissions for standard users
Including the above, I want to be able to filter out by ACCESS, so search for folders where Standard Users don't have Modify Access).
Also want to filter out stale groups, users, UG_Join, folders and acls using the STATUS Column
I'm NOT trying to find what certain users have access to. I don't care about that, what I want is what folders standard users cant access (list, read, modify, full control). I don't expect to see usernames or group names in my result, just paths.
Logically all I can come up with is get all the paths that Admins have access to (which is 100% of the paths) then skip paths that end-users have access two, leaving only paths that only Admins have access to. Any idea on how I would go about this? Thanks!
Please try the query below. It gets all the folders, and then uses all standard users and any standard groups of that user before applying to filter to only show the folders without a standard user or a standard user in a standard group assigned.
SELECT DISTINCT
F.Path
FROM Folders F
INNER JOIN ACLS A
ON A.FUID = F.FUID
LEFT OUTER JOIN (
SELECT
U.URUID,
G.GRUID
FROM Users U
LEFT OUTER JOIN UG_JOIN UG
ON UG.URUID = U.URUID
LEFT OUTER JOIN Groups G
ON G.GRUID = UG.GRUID
AND G.Class = 'User' -- Whatever class needs to be user group
WHERE U.Class = 'Standard User' -- Whatever class neeed to be standard group
-- AND U.STATUS = 1 -- If you only care about active users
) UG
ON (A.URUID = UG.URUID OR A.GRUID = UG.URUID)
WHERE UG.URUID IS NULL
not sure how to combine first and second requirement, so i use Or operator at below, lets me know if i misunderstand u :)
DECLARE #Class NVARCHAR(100) = 'STANDARD USER'
DECLARE #Access NVARCHAR(100) = 'Modify'
SELECT F.[PATH]
FROM Folders F
INNER JOIN ACLS A
ON A.FUID = F.FUID
INNER JOIN Users U
ON U.URUID = A.URUID
INNER JOIN Groups G
ON G.GRUID = A.GRUID
INNER JOIN UG_JOIN UG
ON UG.URUID = U.URUID
INNER JOIN UG_JOIN UG2
ON UG2.GRUID = G.GRUID
WHERE (U.Class != #Class --First requirement : What Folders dont have any permission for standard user
-- means i will filter out standuser
OR (A.Access Not LIKE '%' + #Access + '%'
AND U.Class = #Class)-- second requirement: search for folders where standard users dont have modify access
)
AND F.Status != 0 --Third reqirement : also want to filter out stale group for all the table
AND A.Status != 0
AND U.Status != 0
AND G.Status != 0
AND UG.Status != 0
AND UG2.Status != 0
I have a users table, with following format :
users(id,name.....,settings)
settings field is of type number and contains a bitmask of settings.
I have to support(inter alia) queries like :
find all users which have setting1, setting23, setting125
Today such a query looks like :
select * from users where bit_and(settings,2^1+2^23+2^125) = 2^1+2^23+2^125
Of course it is not a perfect implementation, but it already works in this way a lot of time.
The problem is that today we have 126 different settings and it is exactly the limit of oracle 11g for bitwise operations. That means that we can't add new settings anymore.
I'm trying to find an alternative solution to this issue.
The obvious way is instead of setting field, create table of mapping (user-->setting), like :
user_id | setting
128 | 1
128 | 23
128 | 125
But then the query like above will be like :
select *
from users u1 join settings s1 on u1.id = s1.user_id and s1.setting = 1
join settings s2 on u1.id = s2.user_id and s2.setting = 23
join settings s3 on u1.id = s3.user_id and s3.setting = 125
It doesn't look good...
So if someone can advise any solution/approach to this issue it will be very helpful...
Here's my answer to a related question.
You could easily simplify your query:
select *
from users u
join settings s
on u.id = s1.user_id
and s1.setting in (1, 23, 125)
This gives you an "or" version of the query.
select u.userid, sum(s.setting)
from users u
join settings s
on u.id = s1.user_id
and s.setting in (1, 23, 125)
group by u.userid
having sum(s.setting) = 149
Gives you the "and" version of the query.
Your new design is fundamentally OK, but assuming the "get users of given settings" will be a predominant query, you can fine-tune it in the following way...
CREATE TABLE "user" (
user_id INT PRIMARY KEY
-- Other fields ...
);
CREATE TABLE user_setting (
setting INT,
user_id INT,
PRIMARY KEY(setting, user_id),
CHECK (setting BETWEEN 1 AND 125),
FOREIGN KEY (user_id) REFERENCING "user" (user_id)
) ORGANIZATION INDEX COMPRESS;
Note the order of fields in PRIMARY KEY and the ORGANIZATION INDEX COMPRESS clause:
ORGANIZATION INDEX will cluster (store physically close together) the rows having the same setting.
COMPRESS will minimize the storage (and caching!) cost of repeated setting fields.
You can then get users connected to any of the given settings like this...
SELECT * FROM "user"
WHERE user_id IN (
SELECT user_id FROM user_setting
WHERE setting IN (1, 23, 125)
);
...which will be very quick thanks to the favorable indexing and minimized I/O.
You can also get users that have all of the give settings like this:
SELECT * FROM "user"
WHERE user_id IN (
SELECT user_id
FROM user_setting
WHERE setting IN (1, 23, 125)
GROUP BY user_id
HAVING COUNT(setting) = 3
);
Using bitfield for all settings makes it awkward to query and hard to optimize for query performance (in your old design, every query is a table scan!). OTOH, the "column per setting" design would require a separate index per column for good performance and you'd still have some less-than-elegant queries.
Also, these approaches are inflexible, unlike your new design that can be easily extended to accept more settings or to to store additional information about each setting (instead of just number) by adding another table and referencing it from the user_setting.
Store each setting in it's own column.