User permission hierarchy in DB - sql

I am trying to add a permission hierarchy for users e.g. child, parent, admin
My idea is to create a generic user table with usernames and passwords and accessibility which would be from 0 to 2 , 0 - child 1 - parent 2 - admin, but I do not know how to connect the user table to parent/child table since they have different variables.
Access picture of my database right now
To be clear parent/child wouldn't have username/password like in this picture anymore with user table.
Update:
DB using only one table for users
This table would keep the fields that are only for parents empty if this is a child etc. I would want feedback if the variable 'accessibility' makes sense that would be value from 0 to 2 which would allow me in code to check if it is a parent child or admin

What you want is a kind-of table inheritance that prevents two derived entities from sharing the same supertype instance.
Databases like Access and MS SQL Server do not support table-inheritance like how PostgreSQL does, but you can fake it:
TABLE Users (
UserId int PRIMARY KEY IDENTITY(1,1),
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
)
TABLE Parents (
UserId int NOT NULL PRIMARY KEY,
-- insert parent-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
TABLE Children (
UserId int NOT NULL PRIMARY KEY,
-- insert child-specific fields here
FOREIGN KEY UserId REFERENCES Users ( UserId )
)
This schema means that a Parent or Children entity cannot exist without a single specific User entity also existing. However because Access does not support true table-inheritance it cannot easily constrain UserId values such that only 1 Parent or Children row can have that value (i.e. there can be a Parent AND a Child that share the same UserId value).
Fortunately there's a hack that is mathematically correct (as far as relational-algebra is concerned) but which unfortunately breaks aspects of OOP in that the superclass (User) is now aware of it subclasses - but this might be desirable is certain circumstances...
...anyway. the trick is to add an enum value to User's primary key (so it's a composite-key) which identifies a singular subclass, then add a constant (enforced via CHECK CONSTRAINT) composite-key component to match in each "derived" table, like so (using pseudo-SQL - the relational-algebra is portable, but concepts like enums and check-constraints don't necessarily port to MS Access very well):
ENUM UserType ( HumanParent = 1, HumanChild = 2, Other = 3 )
TABLE Users (
UserId int IDENTITY(1,1),
UserType UserType NOT NULL,
UserName nvarchar(50) NOT NULL,
PasswordHash binary(32) NOT NULL, -- assuming sha256
PasswordSalt binary(8) NOT NULL
PRIMARY KEY ( UserId, UserType )
)
TABLE Parents (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert parent-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 1
)
TABLE Children (
UserId int NOT NULL,
UserType UserType NOT NULL,
-- insert child-specific fields here
PRIMARY KEY ( UserId, UserType )
FOREIGN KEY ( UserId, UserType ) REFERENCES Users ( UserId, UserType )
CHECK CONSTRAINT UserType = 2
)
So at the cost of slight inefficiency (i.e. the extra storage needed for the UserType columns - and computational expense of evaluating the CHECK constraints) you gain guarantees of the correctness of your data.
Now have fun porting that to Access :)

Related

Design SQL tables

I have a set of business objects, which will need to store properties specific to the object type. I want these properties to be definable by the user at runtime. My solution for that is to have a table for each object type that contains the property keys for that object type.
For example, a user object may have a set of properties: fname, lname, address, phone, SSN. And if desired, that list can be built upon within the application, to now include email.
I then have a set of tables which contain all the possible values for a given property. So I'd have a table containing all addresses, a table containing all emails, etc. These may be used by different object types, and some property keys may have multiple values. If a a new property is created, a new table to contain that new set of values would also be created.
The problem I'm having is mapping out everything, in such a way that I can identify the value of each property, for each instance, of each object.
Any suggestions on what I might be missing, what I should be looking into, or any other solutions to the problem would be much appreciated.
Thanks!
Disclaimer: I'm a relativity new self taught programmer. Forgive any ignorance or shortcoming.
You could perhaps have something like this. I have not tested the code so please excuse any typos. The code is MS-SQL
CREATE TABLE tblUserDefinedEntity
(
UserDefinedEntityID INT NOT NULL PRIMARY KEY,
Name VARCHAR(255)
)
INSERT INTO tblUserDefinedEntity
VALUES
(1,'Person'),
(2,'Business')
-- properties each entity can have.
CREATE TABLE tblUserDefinedEntityProperty
(
UserDefinedEntityPropertyID INT NOT NULL PRIMARY KEY,
Name VARCHAR(255)
)
INSERT INTO tblUserDefinedEntityProperty
VALUES
(1,'FirstName'),
(2,'Surname'),
(3,'Address')
--maps properties to entities EG a person has a firstname
CREATE TABLE tblUserDefinedEntityPropertyMapping
(
ID INT NOT NULL PRIMARY KEY,
UserDefinedEntityID INT FOREIGN KEY REFERENCES tblUserDefinedEntityID(UserDefinedEntityID),
UserDefinedEntityPropertyID INT FOREIGN KEY REFERENCES tblUserDefinedEntityPropertyID(UserDefinedEntityPropertyID ),
)
-- a person has a firstname, surname and address, a business has a firstname and address
INSERT INTO tblUserDefinedEntityPropertyMapping
VALUES
(1,1,1),
(2,1,2),
(3,1,3),
(4,2,1),
(5,2,3)
--all the available values for each attribute, eg all the firstnames, surnames and addresses
CREATE TABLE tblValue
(
ValueID INT NOT NULL PRIMARY KEY,
Value VARCHAR(255),
UserDefinedEntityPropertyID INT FOREIGN KEY REFERENCES tblUserDefinedEntityProperty (UserDefinedEntityPropertyID),
)
INSERT INTO tblValue
VALUES
(1,'John',1),
(2,'James,1),
(3,'Jill',1),
(4,'Smith',2),
(5,'Jones',2),
(6,'123 Fake Street',3)
(7,'124 Fake Street',3)
--creating instances of your entities and assigning them values
CREATE TABLE tblDataEnty
(
DataItemID INT,
UserDefinedEntityPropertyMappingID INT FOREIGN KEY REFERENCES tblUserDefinedEntityPropertyMapping(UserDefinedEntityPropertyMappingID),
ValueID INT FOREIGN KEY REFERENCES tblValue(ValueID),
PRIMARY KEY (DataItemID, UserDefinedEntityPropertyMappingID)
)
-- we have added
-- john jones (no address)
-- james smith (124 fake street)
INSERT INTO tblDataEnty
VALUES
(1,1,1),
(1,2,5),
(2,1,2),
(2,2,4),
(2,3,7)

Stored procedure with multiple tables with foreign key

I am working on an event management project. In that project, I have a form in admin panel as add event which contains events name, category, sub-category, event admin etc and more. And in the database, I have different tables like event category, event sub-category.
And also I have a table that is cultural event which includes form fields and foreign key. I pass event catid, event sub-cat id.
On button click that cultural event is added.
I want to insert data in their tables and I want id that I gave in cultural event table.
On single click I want to insert this
How can I achieve this? Using a stored procedure?
CREATE TABLE EVENT_SCAT (ESUBCAT_ID INT NOT NULL PRIMARY KEY, ECAT_ID INT NOT NULL FOREIGN KEY REFERENCES EVENT_CAT(ECAT_ID), ESUBCAT_NAME VARCHAR(255) NOT NULL, )
create table EVENT_CAT (ECAT_ID INT NOT NULL IDENTITY PRIMARY KEY, ECAT_NAME VARCHAR(255)NOT NULL, EID INT NOT NULL FOREIGN KEY REFERENCES EVENTDETAILS(EID)
CREATE TABLE Cultural_E (c_ID INT NOT NULL IDENTITY PRIMARY KEY, cEVENT_NAME VARCHAR(255) NOT NULL, cE_SDATE DATE NOT NULL, cE_EDATE DATE NOT NULL, SE_RULES1 VARCHAR(MAX), SE_RULES2 VARCHAR(MAX), SE_RULES3 VARCHAR(MAX), cE_RULES4 VARCHAR(MAX), cE_EFEES INT, EID INT NOT NULL FOREIGN KEY REFERENCES EVENTDETAILS(EID), ECAT_ID INT NOT NULL FOREIGN KEY REFERENCES EVENT_CAT(ECAT_ID) )
and i have form in asp.net which includes all fields in single form but there are different tables with foreign keys. and i want id of ecat id and subcatid in last table i.e Cultural_e.
for ex:THESE ARE TABLE
eVENT_T
ID ENAME EADMIN
1 CULTURAL NIKHIL
E_CAT
ID ECAT_NAME E_iD
1 SINGING 1
event_scat
ID eCAT_iD ESUBCAT_NAME
1 1 SOLO
NOW I HAVE TABLE THAT IS CULTURAL_T
THESE TABLE HAVE ITS OWN FILEDS AS WELL AS FOREIGN KEY LIKE EID ,ECAT_ID AS YOU CAN SEE ABOVE
AND I HAVE FORM IN ASP.NET AND FORM CONTAINS TEXTBOX TO ENTER DATA OF ALL THESE TABLE.
SO PLZ TELL ME HOW TO ACHIEVE THIS
THANK YOU
As per my understanding of your question,try to use triggers rather than stored procedures if possible.
CREATE TRIGGER InsertEvents
AFTER INSERT ON EVENT_CAT
BEGIN
/* Insert Query to EVENT_SCAT */
/* Insert Query to cultural */
END

SQL Server : create a foreign key with a condition

I'm designing a new database for a company, trying to keep strict constraints with foreign keys etc for integrity. I have a table [Member] which holds companies on the system. This table has a column of [internalContact] for the user in our company who deals with this member which has a foreign linked to the users table by user id.
What I would like to know is if it is possible to assign a condition to the foreign key, since the users table contains internal and external users. ie. for the field to only accept a user id where the user type is 5. Can this be done, or can I only control this in my application code?
Thanks
You can use a check constraint for this.
(The code is untested some syntax errors will be in there)
CREATE TABLE Member
(
P_Id int NOT NULL,
LastName varchar(255) NOT NULL,
FirstName varchar(255),
Address varchar(255),
City varchar(255),
InternalContactId
CONSTRAINT chk_Person CHECK (isInternalUser(internalContactId) > 0)
)
ALTER TABLE Member
ADD FOREIGN KEY (InternalContacId)
REFERENCES Persons(P_Id)
Then just create a function isInternalUser that returns 1 if user in ok to be an internal contact
CREATE FUNCTION isInternalUser ( #userId int(10) )
RETURNS int
AS
BEGIN
DECLARE #tmp int
SELECT #tmp = count(*)
FROM users
WHERE userId = #UserId and <check to see if user is internal>
RETURN(#CtrPrice)
END
GO

Creating a table specifically for tracking change information to remove duplicated columns from tables

When creating tables, I have generally created them with a couple extra columns that track change times and the corresponding user:
CREATE TABLE dbo.Object
(
ObjectId int NOT NULL IDENTITY (1, 1),
ObjectName varchar(50) NULL ,
CreateTime datetime NOT NULL,
CreateUserId int NOT NULL,
ModifyTime datetime NULL ,
ModifyUserId int NULL
) ON [PRIMARY]
GO
I have a new project now where if I continued with this structure I would have 6 additional columns on each table with this type of change tracking. A time column, user id column and a geography column. I'm now thinking that adding 6 columns to every table I want to do this on doesn't make sense. What I'm wondering is if the following structure would make more sense:
CREATE TABLE dbo.Object
(
ObjectId int NOT NULL IDENTITY (1, 1),
ObjectName varchar(50) NULL ,
CreateChangeId int NOT NULL,
ModifyChangeId int NULL
) ON [PRIMARY]
GO
-- foreign key relationships on CreateChangeId & ModifyChangeId
CREATE TABLE dbo.Change
(
ChangeId int NOT NULL IDENTITY (1, 1),
ChangeTime datetime NOT NULL,
ChangeUserId int NOT NULL,
ChangeCoordinates geography NULL
) ON [PRIMARY]
GO
Can anyone offer some insight into this minor database design problem, such as common practices and functional designs?
Where i work, we use the same construct as yours - every table has the following fields:
CreatedBy (int, not null, FK users table - user id)
CreationDate (datetime, not null)
ChangedBy (int, null, FK users table - user id)
ChangeDate (datetime, null)
Pro: easy to track and maintain; only one I/O operation (i'll come to that later)
Con: i can't think of any at the moment (well ok, sometimes we don't use the change fields ;-)
IMO the approach with the extra table has the problem, that you will have to reference somehow also the belonging table for every record (unless you only need the one direction Object to Tracking table). The approach also leads to more I/O database operations - for every insert or modify you will need to:
add entry to Table Object
add entry to Tracking Table and get the new Id
update Object Table entry with the Tracking Table Id
It would certainly make the application code that communicates with the DB a bit more complicated and error-prone.

Beginner with triggers

Im a beginner in database and i got this difficult auction database project.
Im using SQL Server Management Studio also.
create table user(
name char(10) not null,
lastname char(10) not null
)
create table item(
buyer varchar(10) null,
seller varchar(10) not null,
startprice numeric(5) not null,
description char(22) not null,
start_date datetime not null,
end_date datetime not null,
seller char(10) not null,
item_nummer numeric(9) not null,
constraint fk_user foreign key (buyer) references user (name)
)
Basically what the rule im trying to make here is:
Column buyer has NULL unless the time (start_date and end_date) is over and startprice didnt go up or increased. Then column buyer will get the name from table user who bidded on the item.
The rule is a bid too difficult for me to make, i was thinking to make a trigger, but im not sure..
Your model is incorrect. First you need a table to store the bids. Then when the auction is over, you update the highest one as the winning bid. Proably the best way is to have a job that runs once a minute and finds the winners of any newly closed auctions.
A trigger will not work on the two tables you have because triggers only fire on insert/update or delete. It would not fire because the time is past. Further triggers are an advanced technique and a db beginner should avoid them as you can do horrendous damage with a badly written trigger.
You could have a trigger that works on insert to the bids table, that updates the bid to be the winner and takes that status away from the previous winner. Then you simply stop accepting new bids at the time the auction is over. Your application could show the bidder who is marked as the winner as the elader if the auction is till open and teh winner if it is closed.
There are some initial problems with your schema that need addressed before tackling your question. Here are changes I would make to significantly ease the implementation of the answer:
-- Added brackets around User b/c "user" is a reserved keyword
-- Added INT Identity PK to [User]
CREATE TABLE [user]
(
UserId INT NOT NULL
IDENTITY
PRIMARY KEY
, name CHAR(10) NOT NULL
, lastname CHAR(10) NOT NULL
)
/* changed item_nummer (I'm not sure what a nummer is...) to ItemId int not null identity primary key
Removed duplicate Seller columns and buyer column
Replaced buyer/seller columns with FK references to [User].UserId
Add currentBid to capture current bid
Added CurrentHighBidderId
Added WinningBidderId as computed column
*/
CREATE TABLE item
(
ItemId INT NOT NULL
IDENTITY
PRIMARY KEY
, SellerId INT NOT NULL
FOREIGN KEY REFERENCES [User] ( UserId )
, CurrentHighBidderId INT NULL
FOREIGN KEY REFERENCES [User] ( UserId )
, CurrentBid MONEY NOT NULL
, StartPrice NUMERIC(5) NOT NULL
, Description CHAR(22) NOT NULL
, StartDate DATETIME NOT NULL
, EndDate DATETIME NOT NULL
)
go
ALTER TABLE dbo.item ADD
WinningBidderId AS CASE WHEN EndDate < CURRENT_TIMESTAMP
AND currentBid > StartPrice THEN CurrentHighBidderId ELSE NULL END
GO
With the additional columns a computed column can return the correct information. If you must return the winner's name instead of id, then you could keep the schema above the same, add an additional column to store the user's name, populate it with a trigger and keep the computed column to conditionally show/not show the winner..