Creating Oracle Database Table with Numeric Permission Level - sql

I am creating an Oracle database with a table called Roles.
I want the Permission_Level row to store values 1,2,3,4, or 5. How to I limit the input to only those numbers? Here is my attempt:
CREATE TABLE ROLES
(
ROLE_ID NUMBER(20) NOT NULL
, ROLE_NAME VARCHAR2(20) NOT NULL
, PERMISSION_LEVEL NUMBER(1,0) NOT NULL
, CONSTRAINT ROLES_PK PRIMARY KEY
(
ROLE_ID
)
ENABLE
);

Check constraint is one option:
SQL> create table roles
2 (role_id number(20) constraint roles_pk primary key,
3 role_name varchar2(20) not null,
4 permision_level int constraint ch_lvl check
5 (permision_level in (1, 2, 3, 4, 5))
6 );
Table created.
Testing:
SQL> insert into roles (role_id, role_name, permision_level) values (1, 'Role 1', 3);
1 row created.
Level 10 doesn't fit:
SQL> insert into roles (role_id, role_name, permision_level) values (2, 'Role 2', 10);
insert into roles (role_id, role_name, permision_level) values (2, 'Role 2', 10)
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CH_LVL) violated
SQL>

Related

Add data to many-to-many relation with one SQL command

I have a basic understanding of SQL databases and I might overlooked something, but I cannot figure out the following problem: there is a many-to-many relationship (for example: users - user_roles - roles). Is it possible to add (new) role to a (new) user with one SQL command (atomic operation)? Currently I use Sqlite.
I am aware of the SELECT last_insert_rowid(); command and with this and several SQL commands I can achieve what I want. But I want to incorporate it into one command (so the server, in this case Sqlite, can optimize the query, etc.). I have no idea, how it is done in real life (one command vs. several one in one transaction), that´s the root cause of this question.
So far this is what I was able to do:
pragma foreign_keys = on;
CREATE TABLE users (
user_id integer primary key autoincrement,
user_name text not null unique
);
CREATE TABLE roles (
role_id integer primary key autoincrement,
role_name text not null unique
);
CREATE TABLE user_roles (
user_id integer not null,
role_id integer not null,
foreign key (user_id) references users(user_id),
foreign key (role_id) references roles(role_id),
primary key (user_id, role_id)
);
insert into users (user_name) values ('Joe');
insert into roles (role_name) values ('admin');
insert into user_roles (user_id, role_id) values (
(select user_id from users where user_name = 'Joe'),
(select role_id from roles where role_name = 'admin')
);
If both user and role exists (Joe and admin), then it works fine.
But I cannot figure out, how to achieve "add-if-missing-then-return-id" behavior if Joe or admin is mission from database.
Example (both user and role are missing):
insert into user_roles (user_id, role_id) values (
(select user_id from users where user_name = 'Bill'),
(select role_id from roles where role_name = 'user')
);
Result:
Execution finished with errors.
Result: NOT NULL constraint failed: user_roles.user_id
You could create view from user_roles table:
CREATE VIEW user_roles_view AS
SELECT
U.user_name, R.role_name
FROM user_roles AS UR
INNER JOIN users AS U ON u.user_id = UR.user_id
INNER JOIN roles AS R ON r.role_id = UR.role_id;
Views in SQLite are read-only unless you create an INSTEAD OF trigger on it. This way you can specify a command or sequence of commands that are executed when the view is modified using INSERT, UPDATE or DELETE statement. For INSERT it could go like this:
CREATE TRIGGER user_roles_view_insert INSTEAD OF INSERT ON user_roles_view
BEGIN
INSERT OR IGNORE INTO users (user_name) VALUES (NEW.user_name);
INSERT OR IGNORE INTO roles (role_name) VALUES (NEW.role_name);
INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (
(SELECT user_id FROM users WHERE user_name = NEW.user_name),
(SELECT role_id FROM roles WHERE role_name = NEW.role_name)
);
END;
Note the usage of INSERT OR IGNORE to prevent inserting duplicate values into all of the three tables. Here's how you would insert values via the view:
INSERT INTO user_roles_view VALUES ('Joe', 'admin');
-- The above statement creates:
-- a row (1, 'Joe') in table users,
-- a row (1, 'admin) in table roles,
-- a row (1, 1) in table user_roles.
INSERT INTO user_roles_view VALUES ('Joe', 'admin');
-- The above statement doesn't add any additional records, because all appropriate records
-- already exist.
INSERT INTO user_roles_view VALUES ('Joe', 'system');
-- The above statement creates:
-- a row (2, 'system') in table roles,
-- a row (1, 2) in table user_roles.
INSERT INTO user_roles_view VALUES ('Alice', 'admin'), ('Bob', 'system');
-- The above statement creates:
-- a row (2, 'Alice') in table users,
-- a row (3, 'Bob') in table users,
-- a row (2, 1) in table user_roles,
-- a row (3, 2) in table user_roles
All of the above statements produce the following output from user_roles_view (SELECT * FROM user_roles_view):
user_name
role_name
Joe
admin
Joe
system
Alice
admin
Bob
system

SQL Constraint on more than one row

I have a table PROPERTY_RUNTIME having the columns of PROPERTY_NAME AND PROPERTY_VALUE
table PROPERTY_RUNTIME
I am adding a constraint to make the values of CNT_DISP_PREP1 and CNT_DISP_PREP2 not equal to 0 at the same time:
ALTER TABLE KLASSX.PROPERTY_RUNTIME
ADD (
CONSTRAINT CK_BOTH_LINE_CLOSE
CHECK (
(
CASE
WHEN
(
((PROPERTY_NAME = 'CNT_DISP_PREP1') AND (PROPERTY_VALUE = '1'))
OR
((PROPERTY_NAME = 'CNT_DISP_PREP2') AND (PROPERTY_VALUE = '1'))
)
THEN 1
ELSE 0
END
) = 1
)
DISABLE NOVALIDATE);
However, when I activate the constraint, and enter four combinations: 1/1, 1/0, 0/1, 0/0, all the four combinations violate the constraint, rather than that only 0/0 violates.
So I wonder if I have any logical mistake in the constraint?
Thanks in advance!
You can use a unique index to enforce that rule across multiples rows as follows:
FSITJA#db01> create table property_runtime (property_name varchar2(30), property_value varchar2(1));
Table created.
FSITJA#db01> create unique index ck_both_line_close on property_runtime (
2 case when property_name in ('CNT_DISP_PREP1', 'CNT_DISP_PREP2') and property_value = '0'
3 then 1 end)
4 ;
Index created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '1');
1 row created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP2', '1');
1 row created.
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '0');
1 row created.
FSITJA#db01 2019-10-18 11:46:08> insert into property_runtime values ('CNT_DISP_PREP2', '0');
insert into property_runtime values ('CNT_DISP_PREP2', '0')
*
ERROR at line 1:
ORA-00001: unique constraint (FSITJA.CK_BOTH_LINE_CLOSE) violated
FSITJA#db01> insert into property_runtime values ('CNT_DISP_PREP1', '0');
insert into property_runtime values ('CNT_DISP_PREP1', '0')
*
ERROR at line 1:
ORA-00001: unique constraint (FSITJA.CK_BOTH_LINE_CLOSE) violated

Compilation error on oracle trigger

I have a table that has employees (oracle SQL), where each employee has a manager (which is represented as a fk of another employee). I want to create a trigger that when an employee is deleted, all employees that have his key in their manager field have their manager changed to null (or -1, or some reserved value). I'm having some trouble figuring out what's wrong with my current code:
EDIT:
I fixed up most of my code, and I was going about it the wrong way. I used the ON DELETE option that was suggested and now everything works fine. Here's my code:
CREATE TABLE EmployeeA
(
employeeID integer,
firstName varchar (255),
lastName varchar (255),
phone integer,
jobTitle varchar (255),
payGrade integer,
fk_EmployeeemployeeID integer,
PRIMARY KEY(employeeID),
FOREIGN KEY(fk_EmployeeemployeeID) REFERENCES EmployeeA (employeeID) ON DELETE SET NULL
);
INSERT INTO EMPLOYEEA VALUES (1, null, 'Powell', 0, 'President', 100, 1);
INSERT INTO EMPLOYEEA VALUES (2, null, 'Hicke', 0, 'Dean (Natural Science)', 100, 1);
INSERT INTO EMPLOYEEA VALUES (3, null, 'Fenves', 0, 'Dean (Engineering)', 100, 1);
INSERT INTO EMPLOYEEA VALUES (4, null, 'Porter', 0, 'Chairman (Computer Science)', 100, 2);
INSERT INTO EMPLOYEEA VALUES (5, null, 'Beckner', 0, 'Chairman (Mathematics)', 100, 2);
INSERT INTO EMPLOYEEA VALUES (6, null, 'Miranker', 0, 'Professor (Computer Science)', 100, 4);
INSERT INTO EMPLOYEEA VALUES (7, null, 'Mok', 0, 'Professor (Computer Science)', 100, 4);
DELETE FROM employeeA WHERE lastName = 'Porter'; 
You don't want to use a trigger here. Use the on delete property of the foreign key. When you define the foreign key constraint, you can specify that you want it to set the child rows to NULL when the parent row is deleted.
SQL> ed
Wrote file afiedt.buf
1 create table employee2(
2 employee_id number primary key,
3 manager_id number,
4 employee_first_name varchar(30),
5 constraint fk_manager_emp
6 foreign key( manager_id )
7 references employee2( employee_id )
8 on delete set null
9* )
SQL> /
Table created.
If we add a boss, a manager (who reports to the boss), and an employee (who reports to the manager)
SQL> insert into employee2( employee_id, manager_id, employee_first_name )
2 values( 1, null, 'Boss' );
1 row created.
SQL> ed
Wrote file afiedt.buf
1 insert into employee2( employee_id, manager_id, employee_first_name )
2* values( 2, 1, 'Emp1' )
SQL> /
1 row created.
SQL> ed
Wrote file afiedt.buf
1 insert into employee2( employee_id, manager_id, employee_first_name )
2* values( 3, 2, 'Emp2' )
SQL> /
1 row created.
then when we delete the manager, the employee's manager_id automatically gets set to NULL
SQL> delete from employee2
2 where employee_first_name = 'Emp1';
1 row deleted.
SQL> select *
2 from employee2
3 where employee_first_name = 'Emp2';
EMPLOYEE_ID MANAGER_ID EMPLOYEE_FIRST_NAME
----------- ---------- ------------------------------
3 Emp2

How to avoid bad data entries in table through proper Key relationship?

I have 3 tables. widgets, widget types, widget type ID. I have created direct relationship between them. How can I avoid bad data going into Widget tables based on Widget_type and Widget_sub_Type. Please see my code. There is just one small thing missing.
Create table widgets (
Widget_ID int not null primary key,
Widget_type_ID int not null,
Widget_Sub_type_ID int,
Widget_Name varchar (50)
)
Create table Widget_Type (
Widget_Type_ID int not null primary key,
)
Create table Widget_Sub_type (
Widget_Sub_Type_ID int not null primary key,
Widget_Type_ID int not null
)
---adding foregin key constraints
Alter table widgets
ADD constraint FK_Widget_Type
FOREIGN KEY (Widget_type_ID)
References Widget_Type (Widget_type_ID)
Alter table widgets
ADD constraint FK_Widget_Sub_Type
FOREIGN KEY (Widget_Sub_type_ID)
References Widget_SUB_Type (Widget_SUB_type_ID)
Alter table widget_Sub_Type
ADD Constraint FK_Widget_Type_Alter
Foreign key (widget_Type_ID)
References Widget_Type (Widget_Type_ID)
---- insert values
insert Widget_Type values (1)
insert Widget_Type values (5)
insert Widget_Sub_type values (3,1)
insert Widget_Sub_type values (4,1)
insert Widget_Sub_type values (7,5)
insert Widget_Sub_type values (9,5)
-- This will error out which is correct
insert Widget_Sub_type values (5,6)
select * from Widget_Sub_type
select * from Widget_type
--Good
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (1, 'TOY', 1, 3)
select * from widgets
--Good
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (2, 'BatMan', 5, 7)
-- How to prevenet this, 3 is not sub_type_id of type_ID 5. This is bad data, It should not be inserted.
insert widgets (Widget_ID,Widget_Name, Widget_type_ID, Widget_Sub_type_ID)
values (3, 'Should Not', 5, 3)

An Oracle Check Constraint

Suppose I have a
table User(userid, usernama) and a
table Owner(ownerid, ownername) and a joined
table UserOwnerMapping(userid, ownerid, IS_MASTER)
which create a many-to-many between user and owner
The is_master default is 'N'. A user can only has one master.
How can I used a constraint to force no more than one master
for a user?
I am using Oracle.
You can achieve the constraint using a unique function-based index:
create unique index idx on userownermapping
(case when is_master='Y' then userid end);
This will only index the userid when is_master = 'Y', so forces userid to be unique when is_master = 'Y' but not otherwise:
SQL> create table UserOwnerMapping (userid integer, ownerid integer, IS_MASTER varchar2(1));
Table created.
SQL>
SQL> create unique index idx on userownermapping
2 (case when is_master='Y' then userid end);
Index created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 2, 'Y');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 3, 'N');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 4, 'N');
1 row created.
SQL>
SQL> insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 5, 'Y');
insert into UserOwnerMapping (userid, ownerid, IS_MASTER) values (1, 5, 'Y')
*
ERROR at line 1:
ORA-00001: unique constraint (MYSCHEMA.IDX) violated
You will need to create a pre-insert trigger on your UserOwnerMapping table.
This needs to check for the existence of the IS_MASTER = Y row for that userid/ownerid combination.
Making the userid column unique could solve the problem. Why do you need is_master?
Do you mean ownerid can be null in UserOwnerMapping?