Filesystem like permissions in DBMS - sql

Sorry for a big question, but I can't explain my situation with less information.
I designed a database system that is like this:
CREATE TABLE servers(
ID bigint primary key not null
/* other fields of the server */
);
CREATE TABLE producers(
ID bigint not null,
ServerID bigint not null,
/* other field of the producer */
constraint "PK_producers"
primary key (ID, ServerID),
constraint "FK_producer_servers"
foreign key("ServerID") references "servers"
);
CREATE TABLE records(
ID bigint primary key,
ServerID bigint not null,
ProducerID bigint not null
/* other fields of record */
constraint "FK_records_servers"
foreign key("ServerID") references "servers"
constraint "FK_records_producers"
foreign key("ServerID", "ProducerID") references "producers"
);
CREATE TABLE groups(
ID bigint not null primary key,
GroupName nvarchar(50) not null,
Permissions int not null
/* other fields of the group */
);
CREATE TABLE users(
ID bigint not null primary key,
UserName nvarchar(50) not null unique,
Permissions int not null
/* other fields of user */
);
CREATE TABLE users_in_groups(
UserID bigint not null,
GroupID bigint not null,
constraint "PK_users_in_groups" primary key (UserID, GroupID),
constraint "FK_uig_users" foreign key("UserID") references "users",
constraint "FK_uig_groups" foreign key("GroupID") references "groups"
);
Data will be added to records from producers and each producer may provide 500~2000 records per day and each server usually have 2~4 producer and it is completely normal to have 3~6 server. As you see records table is growing really fast(I periodically archive some data and shrink database but records table usually have > 100000 record).
Now my question is:
As you see users have different permissions, based on which group they currently belong and which permissions applied to them by administrators, now I have to advance this system in a way that each user may have different permissions to different items(server, producer and record) in a way that if a user have explicit permission to an item use that permission otherwise use permission from parent and at least global permissions of the user. For example user permissions to a record will be indicated from permissions that applied to that record, its producer, its server or permissions that defined for the user.
As first workaround I think I will implement relation tables that provide relation between user and various objects as follow:
CREATE TABLE user_server_permissions(
UserID bigint not null,
ServerID bigint not null,
Permissions int not null,
constraint "PK_usp" primary key ("UserID", "ServerID"),
constraint "FK_usp_users" foreign key ("UserID") references "users",
constraint "FK_usp_server" foreign key ("ServerID") references "servers"
);
CREATE TABLE user_producer_permissions(
UserID bigint not null,
ServerID bigint not null,
ProducerID bigint not null,
Permissions int not null,
constraint "PK_upp" primary key ("UserID", "ServerID", "ProducerID"),
constraint "FK_upp_users" foreign key ("UserID") references "users",
constraint "FK_upp_server" foreign key ("ServerID") references "servers"
constraint "FK_upp_producer" foreign key ("ServerID", "ProducerID") references "producers"
);
CREATE TABLE user_record_permissions(
UserID bigint not null,
RecordID bigint not null,
Permissions int not null,
constraint "PK_urp" primary key ("UserID", "ServerID"),
constraint "FK_urp_users" foreign key ("UserID") references "users",
constraint "FK_urp_record" foreign key ("RecordID") references "records"
);
But using this approach really decrease performance, because main table that I work with it is records and it is a rare condition that an administrator set special permission for a record and most records should use permissions from their producer but using this technique I should check multiple tables for each access to records table. For example a simple query like:
SELECT * FROM "records" WHERE /* Some condition */ AND record_is_accessible( "ID" )
Will kill my server that should be able to respond to multiple clients!
Most my clients use MSSQL as DBMS but there are few cases where they use MySQL or ORACLE.
Now can anyone direct me in a way to accomplish the task!?

At first glance, a quick solution would be
to not have 3 separate tables for permissions. Instead you would have this one
CREATE TABLE user_entity_permissions(
UserID bigint not null,
EntityID bigint not null,
EntityType int not null,
Permissions int not null,
constraint "PK_usp" primary key ("UserID", "EntityID"),
constraint "FK_usp_users" foreign key ("UserID") references "users"
--- Third FK constraint ---
);
Now you want to shape your Third Fk constraint accordingly (if needed)
For example lets say you give Servers an EntityType = 1, Producer --> 2, Record --> 3
Then EntityId and EntityType = 1 references to servers and so on...
This way you can also sort the priority of your permissions by using EntityType (e.g. 1 first, then 2, then 3)

Related

SQL table stopping me from adding foreign key

I'm creating a SQL table in VS that stores what rooms each client is, So the table has RoomId (int) and UserID (int).
Because I only want to add to the table only rooms and clients that exist they are both keys that have a foreign key to 2 tables, one that stores RoomID and Name and another that stores Client ID and Name.
Room and UserId tables:
CREATE TABLE [dbo].[UsersInRoomsTable]
(
RoomId INT NOT NULL,
UserId INT NOT NULL,
CONSTRAINT PK_RS PRIMARY KEY(RoomId, UserId),
CONSTRAINT [fk_room] FOREIGN KEY([RoomId]) REFERENCES [dbo].[RoomsTable]([RoomId]),
CONSTRAINT [fk_user] FOREIGN KEY ([UserId]) REFERENCES [dbo].[UserInfoTable] ([UserId])
);
Table that stores all the users:
CREATE TABLE [dbo].[UserInfoTable]
(
[UserName] NVARCHAR (50) NOT NULL,
[UserId] INT NOT NULL,
CONSTRAINT [PK_roomuser] PRIMARY KEY CLUSTERED ([UserName] ASC, [UserId] ASC)
);
Table that stores all the rooms
CREATE TABLE [dbo].[RoomsTable]
(
[RoomId] INT NOT NULL,
[RoomName] NVARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([RoomId] ASC)
);
Everything works except the last line in the Rooms and users table:
CONSTRAINT [fk_user] FOREIGN KEY ([UserId]) REFERENCES [dbo].[UserInfoTable] ([UserId])
When I try to Update the table I get an error SQL71516:
SQL71516: The referenced table '[dbo].[UserInfoTable]' contains no primary or candidate keys that match the referencing column list in the foreign key.
If the referenced column is a computed column, it should be persisted
How can I solve this problem and what is causing it?
Edit: I think I know what is code is colliding: For some reason I can not have a foreign key connecting to a key that contains 2 indexes IE: fk_user is a Fk to table UserInfoTable that has 2 keys (UserID and UserName)
is there a way to pass this obstacle?
The columns of a foreign key have to match the columns they reference by number, type and order.
You have a primary key on userinfotable of (username, userid). But in usersinroomstable you are trying to let the foreign key (userid) to reference that. The number of columns doesn't match, so the foreign key cannot be added.
Presumably the username shouldn't really be part of the primary key of userinfotable and got there by accident. Remove it from the primary key constraint.
Or, if username has to be in the primary key, add such a column to the table usersinroomstable and add it to the foreign key constraint.

ERROR: violates foreign key constraint, key is not present in parent table (but it is??)

I know this question has been asked many times, but none of the answers have solved my issue.
I am creating a database for a uni assignment, using PostgreSQL through pgadmin 4, and I have a table named "staff" populated with staff members with a primary key of "staffid". I then have another table named "client_international", which includes a foreign key of "staffid" which relates to the staff tables primary key.
When trying to insert into the client table, I am getting the following error:
ERROR: insert or update on table "client_international" violates foreign key constraint "intclient_staff_fkey"
DETAIL: Key (staffid)=(100000024) is not present in table "staff".
SQL state: 23503
I am certain that that '100000024' key is in the staff table.. yet I still get the error. Any suggestions? Below I will paste the code I used to create the staff and client tables, in case anyone notices an error in them.
Staff table:
CREATE SEQUENCE staff_seq
start 100000000
increment 1;
CREATE TABLE staff
(
staffid integer default nextval('staff_seq'),
firstname varchar(20) NOT NULL,
lastname varchar(20) NOT NULL,
"position" varchar(20) NOT NULL,
mobile varchar(20) NOT NULL,
email varchar(100) NOT NULL,
"location" integer NOT NULL,
CONSTRAINT staff_pkey PRIMARY KEY (staffid)
);
Client table:
CREATE SEQUENCE client_seq
start 200000000
increment 1;
CREATE TABLE client
(
clientid integer default nextval('client_seq'),
company varchar(100) NOT NULL,
sector varchar(100) NOT NULL,
pointofcontact varchar(20) NOT NULL,
mobile varchar(20) NOT NULL,
email varchar(100) NOT NULL,
approvalstatus boolean default (false),
"location" integer NOT NULL,
staffid integer NOT NULL,
CONSTRAINT client_pkey PRIMARY KEY (clientid)
);
CREATE TABLE client_international
(
CONSTRAINT client_international_pkey PRIMARY KEY (clientid)
) INHERITS ("client");
ALTER TABLE client
ADD CONSTRAINT client_location_fkey FOREIGN KEY ("location") REFERENCES "location" (locationid),
ADD CONSTRAINT client_staff_fkey FOREIGN KEY (staffid) REFERENCES staff (staffid);
ALTER TABLE client_international
ADD CONSTRAINT intclient_location_fkey FOREIGN KEY ("location") REFERENCES "location" (locationid),
ADD CONSTRAINT intclient_staff_fkey FOREIGN KEY (staffid) REFERENCES staff (staffid);
I get the error when running the following statements:
INSERT INTO client_international(company, sector, pointofcontact, mobile, email, approvalstatus, "location", staffid)
VALUES ('Moores Dogs', 'Border Patrol', 'Carol Moore', '07911 653453', 'jenkinsj#k9solutions.co.uk', 'false', '500000001', '100000024');
Here's a screenshot of the entry in the staff table, showing that it's definitely in there:
Foreign keys aren't "inherited".
Quote from the manual
A serious limitation of the inheritance feature is that [...] foreign key constraints only apply to single tables, not to their inheritance children. This is true on both the referencing and referenced sides of a foreign key constraint.
(emphasis mine)
So what you are trying to do, simply isn't supported.

error: there is no unique constraint matching given keys for referenced table "incident"

I know that this question has been already answered a million of times, but I couldn't find any solution. Well I have these three tables on postgres sql.
CREATE TABLE user_account (
id SERIAL not null,
firstName VARCHAR(60) not null,
lastName VARCHAR(60) not null,
password VARCHAR(150) not null,
email VARCHAR(40) not null UNIQUE,
isVolunteer BOOLEAN,
complete BOOLEAN,
CONSTRAINT pk_user PRIMARY KEY (id));
CREATE TABLE incident (
id SERIAL not null,
patientId INTEGER not null,
incidentTime VARCHAR(10) not null,
latitude NUMERIC not null,
longitude NUMERIC not null,
city VARCHAR(60) not null,
state VARCHAR(60),
country VARCHAR(60),
complete BOOLEAN,
CONSTRAINT pk_incident PRIMARY KEY (id, patientId),
CONSTRAINT fk_incident FOREIGN KEY (patientId)
REFERENCES user_account (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE);
CREATE TABLE incident_has_volunteer (
incidentId INTEGER not null,
volunteerId INTEGER not null,
incidentTime VARCHAR(10) not null,
complete BOOLEAN,
CONSTRAINT pk_incident_has_volunteer PRIMARY KEY (incidentId, volunteerId),
CONSTRAINT fk_volunteer FOREIGN KEY (volunteerId)
REFERENCES user_account (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_incident FOREIGN KEY (incidentId)
REFERENCES incident (id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE);
When I try to create the table incident_has_volunteer it throws the error there is no unique constraint matching given keys for referenced table "incident".
I tried to add on the third table and the patientId as a foreign key from table incident table but with no luck. I can't understand why it throws this error even if I have already set the primary keys on the incident table.
I'm not an expert in postgres, but I believe that the problem is while fk_incident is referencing incident.id, incident's primary key is made of id + patientId. Since incident.id is guaranteed to be unique only in combination with patientId, there's no way to ensure referential integrity.
I believe that if you add a unique constraint to incident.id (I'm assuming that it would be unique), your foreign key will be legal.
Very simply - one table of primary key acts as a foreign key for another table, so you must ensure that both key is referenced or not.
Simply you will not assign foreign key to the column of another table which does not have primary key. this is called as RDBMS.
Thanks

SQL Management Studio 2012 Foreign Key Is A Mess

I am trying to add a foreign key to a table. I have created two tables.
CREATE TABLE madeupbusiness.staff
(
staffnum int NOT NULL,
forename varchar(30) NOT NULL,
surname varchar(30) NOT NULL,
meeting int NOT NULL,
PRIMARY KEY (staffnum),
)
GO
meeting should use the PK from from the meeting table to create a FK :
CREATE TABLE madeupbusiness.meeting
(
meetingnum int NOT NULL,
room varchar(30) NOT NULL,
PRIMARY KEY (meetingnum),
)
GO
To create the foreign key I run this query
ALTER TABLE madeupbusiness.staff
WITH CHECK
ADD CONSTRAINT FK_staff_meetingnum FOREIGN KEY (meetingnum)
REFERENCES madeupbusiness.meeting(meetingnum)
ON DELETE CASCADE
ON UPDATE CASCADE
;
GO
The query runs but when I create a database diagram there is a square loop the staff table from the staffnum key back onto it. Sorry but I don't really know how to describe it. There is no relationship between the two tables. What am I doing wrong?
I have tried to add the relationship from design view but the foreign key table is greyed out.
If you can rebuild the Table do this:
CREATE TABLE madeupbusiness.meeting
(meetingnum int NOT NULL PRIMARY KEY REFERENCES madeupbusiness.meeting(YourColumnYouWantItShouldBeReferenced),
room varchar(30) NOT NULL);
GO

SQL Many-to-many relationship between 3 tables

I have the following tables:
Users
- user_id (PK)
Projects
- project_id (PK)
Tasks
- taks_id (PK)
- project_id (FK)
The requirements are:
Each Task belongs to only one project.
Each Project has one or more users.
Each Task has 0 or more users (but the users must belong to the project to which the task belongs to).
I'm currently trying to do the above with 2 join tables:
UsersProjects
[user_id] INT NOT NULL,
[project_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([project_id]) REFERENCES [dbo].[_Projects] ([project_id]),
CONSTRAINT [PK_UsersProjects] PRIMARY KEY ([user_id], [project_id])
UsersTasks
[user_id] INT NOT NULL,
[task_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([task_id]) REFERENCES [dbo].[_Tasks] ([task_id]),
CONSTRAINT [PK_UsersTasks] PRIMARY KEY ([user_id], [task_id])
Am I on the right track for the many-to-many relationships and for the requirements?
How can I optimise it?
Thanks for taking the time to read this!
Each Task has 0 or more users (but the users must belong to the project to which the task belongs to).
I assume you're asking how to enforce this last constraint - it's not there in your current DB schema. I see two options:
You have to do it via SQL (e.g. when you insert a record into UsersTasks you issue a SELECT on UsersProjects JOIN Tasks to see if there's at least record matching)
You change the structure of the Tasks Primary Key. I.e. you create a PK to be the compound (Project_ID + Task_ID) and Task_ID is then no longer an identity field but starts with e.g. 1 for each new Project. Then the PK for UsersTasks also becomes User_ID + Project_ID + Task_ID and then you can reference a completely FK straight to UserProjects (User_ID + Project_ID). It's less intuitive than Solution 1 but works and requires no custom SQL for enforcing integrity.
For Option 2:
Tasks has got a slightly different constraint
...
CONSTRAINT [PK_Tasks] PRIMARY KEY ([project_id], [task_id])
UsersTasks
[user_id] INT NOT NULL,
[project_id] INT NOT NULL,
[task_id] INT NOT NULL,
[accepted] BIT NULL,
FOREIGN KEY ([user_id]) REFERENCES [dbo].[_Users] ([user_id]),
FOREIGN KEY ([project_id], [task_id]) REFERENCES [dbo].[_Tasks] ([project_id],[task_id]),
FOREIGN KEY ([user_id], [project_id]) REFERENCES [dbo].[_User_Projects] ([user_id],[project_id]),
CONSTRAINT [PK_UsersTasks] PRIMARY KEY ([user_id], [project_id], [task_id])