Database schema & pulling info - mysql

I'm far from being a database expert, so please feel free to let me know I'm doing it entirely wrong. I'm trying to create a set of tables that has the following basic rules:
There are companies, managers and employees. Managers can only belong to one company, but employees can belong to more then one manager. The table structure I've come up with is something like this:
# Companies
company_id
company_name
# Managers
manager_id
company_id
# Employees
employee_id
company_id
employee_name
# Managed By
employee_id
manager_id
Does this structure seem reasonable? I thought I need something like "Managed By" since an employee can have multiple managers.
What I'm boggling on is now how do I manage to grab the records I'd want. For example:
Get all employee names belonging to a certain manager
Get all employee names belonging to two certain managers
All employees to the same company
edit: I think I'm getting the queries figured out but this still feels unweidy, so any help on the table structure would be appreciated.
The fact I'm having problems writing queries for this information makes me think I've done something fundamentally wrong with the table structure. Hopefully someone here can set me right?

Generally, the scheme is correct. The one possibility that does exist, however, is that you could have data where an employee is managed by managers at more than two companies.
For your queries:
select distinct Employees.employee_name as name from Employees, Managers, ManagedBy where Managers.manager_id = X and ManagedBy.manager_id = Managers.manager_id and Employees.employee_id = ManagedBy.employee_id;
and
select distinct Employees.employee_name as name from Employees, Managers, ManagedBy where (Managers.manager_id = X or Managers.manager_id = Y) and ManagedBy.manager_id = Managers.manager_id and Employees.employee_id = ManagedBy.employee_id;
where X and Y are the manager IDs you want.
edit: I know you crossed out the queries you wanted, but the third one is this:
select distinct Employees.employee_name as name from Employees where Employees.company_id = Z;
where Z is the company ID you want.

All employees to the same company
SELECT employee_id, employee_name, company_name
FROM Employees
LEFT JOIN Companies ON Employees.company_id = Companies.company_id
WHERE Companies.company_id = ????
or if you pulling by name
WHERE Companies.company_name = 'ABC'
Get all employee names belonging to a certain manager
SELECT employee_id, employee_name, manager_id
FROM Employees
LEFT JOIN Managed_By ON Employees.employee_id = Managed_By.employee_id
WHERE Managed_By.manager_id = ????
manager_name in Managers table would be nice to have

Related

how to design: users with different roles see different records

I have a schema design question for my application, hope I can get advices from teachers. This is very alike of Role Based Access Controll, but a bit different in detail.
Spec:
For one company, there are 4 roles: Company (Boss) / Department (Manager) / Team (Leader) / Member (Sales), and there are about 1 million Customers records. Each customer record can be owned by someone, and he could be Boss or Manager or Leader or Sales. If the record's owner is some Sales, then his upper grade (say: his leader / manager / boss) can see this record as well (but others: say the same level of his workmates, cannot see, unless his upper grade manager share the customer to his workmates), but if the record's owner is boss, none except the boss himself can see it.
My Design is like this (I want to improve it to make it more simple and clear):
Table:
departments:
id (P.K. deparment id)
d_name (department name)
p_id (parent department id)
employees
id (P.K. employee id)
e_name (employee name)
employee_roles
id (P.K.)
e_id (employee id)
d_id (department id)
customers
id (P.K. customer id)
c_name (customer name)
c_phone (customer phone)
permissions
id (P.K.)
c_id (customer id)
e_id (owner employee id)
d_id (this customer belongs to which deparment)
share_to (this customer share to other's id)
P.S.: each employee can have multi roles, for example, employee A can be the manager of department_I and meanwhile he can also be one sales of deparment_II >> Team_X.
So, when an employee login to application, by querying from employee_roles table, we can get all of the department ids and sub department ids, and save them into an array.
Then I can use this array to query from permissions table and join it with customers table to get all the customers this employee should see. The SQL might look like this:
SELECT * FROM customers AS a INNER JOIN permissions AS b ON a.id =
b.c_id AND (b.d_id IN ${DEP_ARRAY} OR e_id = ${LOGIN_EMPLOYEE_ID} OR
share_to = ${LOGIN_EMPLOYEE_ID})
I don't really like the above SQL, especially the "IN" clause, since I am afraid it will slow down the query, since there are about 1 million records or even more in the customer table; and, there will be as many records as the customers table in the permissions table, the INNER JOIN might be very slow too. (So what I care about is the performance like everyone :))
To my best knowledge, this is the best design I can work out, could you teachers please help to give me some advice on this question? If you need anything more info, please let me know.
Any advice would be appreciated!
Thanks a million in advance!!
Do not use an array, use a table, ie the value of a select statement. And stop worrying about performance until you know more basics about thinking in terms of tables and queries.
The point of the relational model is that if you structure your data as tables then you can looplessly describe the output table and the DBMS figures out how to calculate it. See this. Do not think about "joining"; think about describing the result. Whatever the DBMS ends up doing is its business not yours. Only after you become knowledgeable about variations in descriptions and options for descriptions will you have basic knowledge to learn about performance.

Make a specific UPDATE to a SQL Database

The database above is given.
I have an Employee Good Employee and another Employee Excellent Employee. Excellent Employee runs a Department.
I want to update my database so Good Employee works in the Department which is led by Excellent Employee
Is it possible to do something like this?
UPDATE Employee
SET Department = (
SELECT AID
FROM Department
WHERE AID = Department AND Employee.Name = 'Excellent Employee')
WHERE Name = 'Good Employee' LIMIT 1;
(The statement above doesn't work)
Not sure exactly what you've got going on, but here is basic syntax for updating with a join in MySQL:
UPDATE FROM EmployeeAS e
LEFT JOIN Department as dept
ON e.? = dept.?
AND e.Name = 'Good Employee'
SET e.Department = dept.AID
This is definitely possible. You're off to a pretty good start. The parts that are correct are:
You are updating Employee where Name='Good Employee'
You are selecting a Department's AID.
From your query, it looks like the "works_at" relationship is stored in the Employee table under the column Department.
I can't tell how you are modeling the "runs" relationship. Let's just assume an Employee who runs a Department must also work at that department. Hence, we can just find which department "Excellent Employee" works at.
To find the department Excellent works at, we can use this query:
SELECT Department FROM Employee WHERE Name = 'Excellent Employee'
Thus, to move Good to Excellent's department, we can put that into the UPDATE:
UPDATE Employee
SET Department = (
...
) WHERE Name = 'Good Employee'
Unfortunately, MySQL has some quirks, so we need to change our subquery to work around that:
UPDATE Employee
SET Department = (
SELECT X.Department FROM (SELECT * FROM Employee) as X WHERE X.Name = 'Excellent Employee'
) WHERE Name = 'Good Employee'
Here's a fiddle showing it in action: http://sqlfiddle.com/#!2/b68bb/2

What will happen if I natural join both these tables?

I have two tables. Employee and Manager. Employee table stores the information of all the employees of the company. Manager table shows which employees work under which manager. Manager is also an employee of the company. The tables are given below.
employee(name, city, street)
manager(name, employee_name)
Bold means they are the primary key. Now the thing is that, in the manager table, both name and employee name is a foreign key referencing employee. So what will happen if I natural join them. Wouldn't there be any problem? After natural joining them, whose city and street will I see in the final table? manager's or employee's?
You'd most likely nothing if you're lucky, or junk if you're unlucky. Natural join would do something like this:
SELECT * FROM managers LEFT JOIN employees
WHERE managers.name = employees.name
Natural join attempts to use COMMONLY NAMED COLUMNS.
When what I assume you want something more like this:
SELECT * FROM managers LEFT JOIN employees
WHERE managers.employee_name = employees.name
You will get a list of all the managers city, street, and employee_name
Name, City, Street, Employee_Name
Otherwise the join stuff, I suggest to review the arquitecture of your DB.
If in the manager table, the name is PK, you can't have more then one employee with the same manager..

Is this possible w/ SQL?

I'm working with a SQL database for a Skills Matrix application and I need a query to return a list of employees that are qualified for a given position. Here's a brief overview of the relevant relationships:
Employee has many skills through
qualifications
Position has many
skills through requirements
Is there a efficient way to return a list of employees that have the qualifications to meet a certain position's requirements?
EDIT
employees
- id
- name
positions
- id
- title
skills
- id
- name
requirements
- position_id
- skill_id
qualifications
- employee_id
- skill_id
SELECT *
FROM employees e
WHERE NOT EXISTS (
SELECT *
FROM requirements r
WHERE r.position_id = 1234
AND NOT EXISTS (
SELECT *
FROM qualifications q
WHERE q.skill_id = r.skill_id
AND q.employee_id = e.employee_id
)
)
It will find all employees such that there are no requirement that is not filled by the employee's qualifications.
The use of nested NOT EXISTS is even mentioned in the MySQL Reference Maual
Sure, it's possible. I count seven tables there: Employee, Qualifation, EmployeeQualifications, Position, Requirement, PositionRequirements, and RequirementQualifications. If your Qualifications and Requirements will always be 1:1 (ie, they are the same thing), you can do it more simply (5 tables) like this: Employee, Position, Skill, EmployeeSkills, PositionSkills.
Once you have the tables defined, the trick to building the query is to first look for any positions for which an employee is not qualified, and then do an exclusion join with that result set back to the position table to get your results.
Yes, to achieve what you want you need, given a Position to return only those Employees, that have qualifications that collectively include all the skills the Position requires. If you retrieve these Employees, this list it will represent the list of the customers you are after.
Update
Since I do not have any experience with mysql, my reply only deals with sql server.
Try something like this:
select
qualifications.employee_id
from
qualifications
join
requirements
on
requirements.skill_id = qualifications.skill_id
where
requirements.position_id = 1234
group by
qualifications.employee_id
having
count(qualifications.skill_id) = (select count(distinct skill_id) from requirements where requirements.position_id = 1234)
Create an index on each of
employees.id
qualifications.employee_id
qualifications.skill_id
requirements.skill_id
requirements.position_id

SQL Server Reporting Services, how to best apply filters

I have a bunch of records (orders) that I want to make available to users to make reports from.
The users come from different departments, and I would like to make it, so each department can only see their own stuff.
I can't figure out how to do this the right way.
What I have now is:
- A model where I have placed a Filter on the Order table.
The filter can use GetUserID() to get the users name, but I can't figure out how I get from that to the "UserDepartment" table that maps users to specific departments.
Ofcourse, I would prefer a solution whereby I didn't have to create new access groups or edit the model for each department that someone might dream up.
Any clues?
(Using SQL server 2008)
EDIT: This link http://blogs.msdn.com/bobmeyers/articles/Implementing_Data_Security_in_a_Report_Model.aspx shows the basics of what I'm trying to do, but the author seems to assume that each record have a UserName field that can be matched.
In my case i want all users of department X to be able to access the line.
We had a similar problem to this and ended up writing a function in SQL.
The function did the following:
Received the username parameter from SRSS
Performed a lookup on the permissions table and retrieved the records (department Id's in your case).
returned the department Id's
Then our sql statement looked like this:
SELECT *
FROM ImportantData
WHERE DepartmentId IN (SELECT Id FROM fn_GetUserDepartmentAllocations(#UserName))
This did force us to modify all of the sql queries but it allowed us to do it with minimal complex logic.
The other thing that this allows for is if you have one user who transcends department boundaries: for example a manager of 2 departments.
CREATE FUNCTION [dbo].[fn_GetUserDepartmentAllocations]
(
#UserName NVARCHAR(100)
)
RETURNS
#TempPermissions TABLE
(
DepartmentId Int
)
AS
BEGIN
INSERT INTO #TempPermissions
SELECT DepartmentId
FROM DepartmentPermissions
WHERE DepartmentAllowedUsername = #UserName
RETURN
END
The main benefit to doing it this way is it also allows you to edit one place to change the entire permissions structure, you don't have to go through each and every report to change it, instead you change one place
For example you could have a manager who belongs to 2 departments but is not allowed to view them except on thursdays (I know silly example but you get the point hopefully).
Hope this helps
Pete
This assume that Users have Orders.
So, filter by users who exist in the same dept as the filter user. Don't filter orders directly.
I've guessed at schema and column names: hoep you get the idea...
SELECT
MY STuff
FROM
Order O
JOIN
UserDept UD ON O.UserCode = UD.UserCode
WHERE
EXISTS (SELECT *
FROM
UserDept UD2
WHERE
UD2.UserCode = #MYUSerCode
AND
UD2.DeptID = UD.DeptID)
--or
SELECT
MY STuff
FROM
Order O
JOIN
UserDept D ON O.UserCode = D.UserCode
JOIN
UserDept U ON D.DeptID = U.DeptID
WHERE
U.UserCode = #MYUSerCode
What you're trying to achieve is difficult using the GetUserID() method. To use that your source query would have to return a lot of redundant data, imagine something like the following:
/*
Table: User
Fields: UserID, LoginName, FullName
Table: Department
Fields: DepartmentID, Name
Table: UserDepartments
Fields: UserID, DepartmentID
Table: Order
Fields: OrderNumber, DepartmentID
*/
SELECT O.OrderNumber, O.DepartmentID, U.LoginName
FROM Order O
JOIN Department D ON D.DepartmentID = O.DepartmentID
JOIN UserDepartments UD ON UD.DepartmentID = D.DepartmentID
JOIN User U ON U.UserID = UD.UserID
This will give you more rows than you want, basically a copy of the order for each user in the department that owns the order.
Now you can apply your filter as described in the link you provided. This will filter it down to just one copy of the order rows for the current user if they're in the right department.
If this is a performance issue there's other alternatives, easiest being using a local report (.RDLC) in either ASP.NET, WinForms or WPF and passing user details off to the data call so the filtering can be done in the SQL.