SQL Server constraint subqueries not allowed - sql

I have the following 3 tables:
employees
departments
employees_departments (join table between employees and departments with additional info)
The department also has a column manager_id which is a foreign key to the employees table (a department has a manager from the employees table).
I have a requirement in which I must place a constraint in which no employee under a manager can have a higher salary than the manager.
My attempt at solving this:
ALTER TABLE employees
ADD CONSTRAINT check_salary
CHECK (salary > (SELECT salary
FROM employees e2
INNER JOIN employees_departments ed2 ON e2.id_employee = ed2.id_employee
INNER JOIN departments d2 ON d2.id_department = ed2.id_department
WHERE e2.id_employee != manager_id (but how do I get the manager id)
AND d2.id_departament = manager_department_id (again don't know how to do get it)
)
I get an error that subqueries are not allowed. Is this even possible to do in a constraint? How can it be modeled in a normal query?
I must also add that I have close to none experience with SQL Server and very little with SQL in general.
Any help greatly appreciated!

Even if you could do this, what would you expect to happen when the data changes such that for some rows the constraint fails (like a manager gets a salary reduction)? Sounds like this might be a bad problem/business rule description.
If it's purely an insert/update time check then use code or (yuk) a trigger.
Another alternative might be to use an updatable view which only selects employees that earn below their manager (i.e. everyone). By updating and inserting there you might be able to make it work in every case because if the insert/update into the view would not satisfy the view's criteria it will reject it.
Read up on the view and specifically the WITH CHECK OPTION.
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-view-transact-sql

Related

My question is how to create a VPD in Oracle with SQL that will also mask data

I am trying to create a VPD in Oracle using SQL statements. The purpose of this problem is so an employee could ONLY view records for employees in the same department while masking their coworker's salaries as NULL.
The code for the table being used is as follows
create table Employee
(
ID number primary key,
DEPT varchar2(25),
SALARY number(8,2),
NAME varchar2(25)
);
I am unsure what the best way to go about doing this would be.... would it be to create a package and use an application context. I believe getting the table to only display those in same "DEPT" I understand but unsure how to mask the data of those with same DEPT but different ID.
Native RLS will get you close but not totally there. Using "sec_relevant_cols" will give you the option between
only seeing the rows that match your predicate, but all values are present
seeing all the rows, but masking values that do not match your predicate
whereas (if I'm reading correctly) you want to see only predicate matching rows AND mask out some values as well.
You could achieve this with a two-step method
Your context contains two keys (say) DEPT and YOUR_ID
The RLS policy is "where dept = sys_context(ctx,'DEPT')"
You have a view EMP to which that policy is applied, being
select
id,
dept,
name,
case when id = sys_context(ctx,'YOUR_ID') then sal else null end sal
from EMP_TABLE

Created View with some constraint but it doesnt appear when insert

I have created the view with some constraints in Oracle 12c. But when I insert values which violate the constraint also be able to insert from the view. It seems like a loophole.
--Create view Emp_1
CREATE VIEW Emp_1
AS
SELECT Empid, Ename, Salary
FROM employees
WHERE salary < 25000
This view is to show only employees who are having less than 25000 for salary.
But when I insert employee who have more than 25000 as salary, View allows me to insert(this is incorrect). but it doesn't appear when i query the view(this is correct)
Is this as design or what do i can do to overcome this?
Technically this is a insert-able view. so it allows to insert. But conceptually user can see only employees who have salary less than 25000. So it does not allow to insert more than the condition.
Please correct me if I am wrong and please help.
Friends thanks for the help. I found it now.
You can use WITH CHECK OPTION CONSTRAINT to over come this.
CREATE VIEW Emp_2
AS
SELECT Empid, Date_Joined, Salary
FROM employees
WHERE salary < 25000
WITH CHECK OPTION CONSTRAINT Emp_2_Salary_Constraint;
Then it shows this error when you are violating it.
SQL Error: ORA-01402: view WITH CHECK OPTION where-clause violation
You will have to add the constraint on the employees table. A view is just a representation of the query. It does not have an assigned storage, and since the underlying table do not have any such constraint, you are able to insert that data. So the design and output is as it should be.

Generate connections table in SQL

I looked for this and did not find a solution that would apply to my scenario.
I'm building a database of game devs and I wish to generate a connections table:
I have the following:
Employee
(
name, date of birth, department they work at, task they do
)
Department
(
department name
)
Task
(
task name
)
and I need to generate a connections table that shows which department contributes to which task. I would do that by checking for each employee their department (only one) and task (also only one) and upon a match, the department contributes to that task.
That is the idea but I have to clue how to code it using Oracle
SELECT DISTINCT "department they work at", "task they do"
FROM Employee;
You should first work out an entity-relationship diagram, that lists the entities you use and with what attributes (and which primary keys), and the relations between those entities. Relationships can be: 1-to-1, 1-to-many and many-to-1, and many-to-many.
In the last case (M:N relation), the implementation in database tables requires an extra table to record such a M:N relationship.
The way to implement a 1:N relationship in a table is adding a foreign key in the child table to the primary key of the parent table.
EDIT: I see that you now supplied some details, and it is clear now that EMPLOYEE table is in fact the connection table, so you could simply query that table and show the DEPTID and TASKID (both the primary keys of their respective tables) to have a connection between departments and tasks. See the query in the other answer, and just add an ORDERBY on DEPTID, to show results in the order of DEPTID.

How to automatically update related field in table?

I'm creating 2 test tables in my oracle 10g database: STAFFS and DEPARTMENTS. Neither table has any primary keys, constraints, joins or any other thing since I don't know any of those anyway. Both are just plain tables with their fields.
In DEPARTMENTS, I have 2 fields: DEPT_NAME and DEPT_CODE. This table will serve as a reference table.
In STAFFS, I have 4 fields: STAFF_ID, STAFF_NAME, DEPT_NAME, and DEPT_CODE.
Say I change the DEPT_NAME of a staff record, how do I make the DEPT_CODE for that staff also change? I've tried creating triggers but all I get is either some mutating error thing or some recursive have been reach error.
Simple solution.
Remove DEPT_NAME column from STAFFS table.
JOIN DEPARTMENTS table using DEPT_CODE when ever you want to do so
This is called normalisation

How to Delete all data from a table which contain self referencing foreign key

I have a table which has employee relationship defined within itself.
i.e.
EmpID Name SeniorId
-----------------------
1 A NULL
2 B 1
3 C 1
4 D 3
and so on...
Where Senior ID is a foreign key whose primary key table is same with refrence column EmpId
I want to clear all rows from this table without removing any constraint. How can i do this?
Deletion need to be performed like this
4, 3 , 2 , 1
How can I do this
EDIT:
Jhonny's Answer is working for me but which of the answers are more efficient.
I don't know if I am missing something, but maybe you can try this.
UPDATE employee SET SeniorID = NULL
DELETE FROM employee
If the table is very large (cardinality of millions), and there is no need to log the DELETE transactions, dropping the constraint and TRUNCATEing and recreating constraints is by far the most efficient way. Also, if there are foreign keys in other tables (and in this particular table design it would seem to be so), those rows will all have to be deleted first in all cases, as well.
Normalization says nothing about recursive/hierarchical/tree relationships, so I believe that is a red herring in your reply to DVK's suggestion to split this into its own table - it certainly is viable to make a vertical partition of this table already and also to consider whether you can take advantage of that to get any of the other benefits I list below. As DVK alludes to, in this particular design, I have often seen a separate link table to record self-relationships and other kinds of relationships. This has numerous benefits:
have many to many up AND down instead of many-to-one (uncommon, but potentially useful)
track different types of direct relationships - manager, mentor, assistant, payroll approver, expense approver, technical report-to - with rows in the relationship and relationship type tables instead of new columns in the employee table
track changing hierarchies in a temporally consistent way (including terminated employee hierarchy history) by including active indicators and effective dates on the relationship rows - this is only fully possible when normalizing the relationship into its own table
no NULLs in the SeniorID (actually on either ID) - this is a distinct advantage in avoiding bad logic, but NULLs will usually appear in views when you have to left join to the relationship table anyway
a better dedicated indexing strategy - as opposed to adding SeniorID to selected indexes you already have on Employee (especially as the number of relationship types grows)
And of course, the more information you relate to this relationship, the more strongly is indicated that the relationship itself merits a table (i.e. it is a "relation" in the true sense of the word as used in relational databases - related data is stored in a relation or table - related to a primary key), and thus a normal form for relationships might strongly indicate that the relationship table be created instead of a simple foreign key relationship in the employee table.
Benefits also include its straightforward delete scenario:
DELETE FROM EmployeeRelationships;
DELETE FROM Employee;
You'll note a striking equivalence to the accepted answer here on SO, since, in your case, employees with no senior relationship have a NULL - so in that answer the poster set all to NULL first to eliminate relationships and then remove the employees.
There is a possibly appropriate usage of TRUNCATE depending upon constraints (EmpployeeRelationships is typically able to be TRUNCATEd since its primary key is usually a composite and not a foreign key in any other table).
Try this
DELETE FROM employee;
Inside a loop, run a command that deletes all rows with an unreferenced EmpID until there are zero rows left. There are a variety of ways to write that inner DELETE command:
DELETE FROM employee WHERE EmpID NOT IN (SELECT SeniorID FROM employee)
DELETE FROM employee e1 WHERE NOT EXISTS
(SELECT * FROM employee e2 WHERE e2.SeniorID = e.EmpID
and probably a third one using a JOIN, but I'm not familiar with the SQL Server syntax for that.
One solution is to normalize this by splitting out "senior" relationship into a separate table. For the sake of generality, make that second table "empID1|empID2|relationship_type".
Barring that, you need to do this in a loop. One way is to do it:
declare #count int
select #count=count(1) from table
while (#count > 0)
BEGIN
delete employee WHERE NOT EXISTS
(select 1 from employee 'e_senior'
where employee.EmpID=e_senior.SeniorID)
select #count=count(1) from table
END