SQL - Why Does This Happen? - sql

These are the tables that I'm working with.
With that in mind, I want to showcase the Employees that are both a supervisor and a manager.
But when I used this
select e1.fname,e1.lname
from employee e1,employee e2,department
where e1.ssn=e2.super_ssn and e1.ssn = Mgr_ssn
This was the output
I know I can solve the problem with 'distinct', but I'm more interested to know why the output turned out like it did.

How about exists?
select e.*
from employee e
where exists (select 1 from employee e2 where e2.mgr_ssn = e.ssn) and
exists (select 1 from employee e2 where e2.super_ssn = e.ssn) ;
Your query returns duplicates for two reasons. First, presumably managers and supervisors have multiple employees below them. You end up with rows for each such employee. Second, you have a cartesian product with department, which further multiplies the rows. The department table is not used in the query.
Using select distinct is not a good solution in this case. The database just ends up having to do a lot more work than necessary -- first to create the duplicate rows and then to remove them.

add department matching clause in where like
select e1.fname,e1.lname
from employee e1,employee e2,department d
where e1.ssn=e2.super_ssn and e1.ssn = Mgr_ssn and
d.Dnumber=e1.Dno

Related

Display all results queried on PostgreSQL where the JOINING value is missing

I have two tables on PostgreSQL, namely: employees and employers.
There is data in each of these tables.
Columns in employees: employee_id, employee_name, employer_id.
Columns in employers: employer_id, employer_name.
I want to display all employee_name's that don't have an associating employer_name.
I used the below query:
SELECT DISTINCT a.employee_name, b.employer_name
FROM employees a
NATURAL JOIN employers b
WHERE a.employee_name LIKE 'Jack';
NB!
I have also tried adding in the below to my query:
COALESCE(b.employer_name, '') = ''
Problem:
If there is no record in the employer table containing the associating employee_id value, the query returns nothing all. I am assuming this is because there is nothing for the two tables to join on?... But I would like to at least find all employees that don't have an employer. I would ideally like the employer_name value in my result to either return: blank/''/NULL.
Your assistance is greatly appreciated.
select employees.employee_name ,employers.employer_name
from employees
left join employers
on employees.employee_id = employees.employee_id
where employers.employer_name is NULL
Use a LEFT JOIN:
SELECT a.employee_name,
COALESCE(b.employer_name, 'NA') AS employer_name
FROM employees a
LEFT JOIN employers b
ON a.employer_id = b.employer_id
WHERE b.employer_id IS NULL
Your current query has several problems, first of which is that you are using a NATURAL JOIN, which should behave link an INNER JOIN, discarding employee records which do not match to any employer. Instead, by using LEFT JOIN, we can retain all employee records, regardless of whether or not they have an employer. The WHERE clause checks for NULL in the joined table, with NULL indicating that the employee did not match to any employer.
Your table name employees table is a little odd, IMO. There is a design 'rule of thumb' that says a table models either an entity or the relationship between entities but not both. To demonstrate this oddness, consider the relational operation is
employees MINUS employers
...which suggests something is off.
Another symptom of this design problem is that the employer_id in the employees table must have some kind of placeholder to represent the predicate employee has no employer, possibly a null (and nulls are to be avoided, IMO).
I suggest you fix this design by introducing a third table to model the relationship between an employee and their employer. Herein lies another design problem: shouldn't this new table be named employees? In other words, isn't the very definition of an employee a person who has an employer?
Consider this design instead:
People: person_id, person_name
Employers: employer_id, employer_name
Employees: employer_id, person_id
To find the names of people who are not employees in pseudo-relational notation:
People MINUS Employees { person_name }
SQL is quite a bit more verbose:
SELECT DISTINCT person_name
FROM People
NATURAL JOIN
( SELECT person_id FROM People
EXCEPT
SELECT person_id FROM Employees ) t;
Note your original query needlessly uses range variables a and b. One of the benefits of NATURAL JOIN is the elminiation of duplicate column names. Range variables with NATURAL JOIN always looks odd to me!

Oracle SQL change value of field to other value from list as long as new value different from current?

I have a test table which contains 33 employee records in my Oracle 10g XE. Examples are as the following (I'm showing 5 only):
I'm not a SQL person so I only know those basic INSERT, UPDATE etc. What I want to achive is to change the department of each employee to another department from another employee as long as it's not the same as their current department. Basically what I'm thinking is:
Get all of the department from each employee (DEPT A, DEPT B, DEPT B, DEPT C, DEPT A), maybe store it in a variable?
For each of the employee, put a different department into its record from the list of department collected before. The new department must not be the same as what that record has before.
The result of the above example would be like the following.
For the record, there are more than just DEPT A - C. My 33 employee records mostly have unique department. Only 2 - 3 department have more than 1 employee under it. Therefore, I can assure that there probably won't be any cases where an employee record cannot be assign under a different department from what it currently has.
This is a harder problem than you may realize. First, it is a permutation problem because you want the final counts for the departments to be the same. Second, there are times where it is not solvable (say, if more than half the employees are in the same department).
If you want to shuffle the departments for employees, here is a method:
with cte as (
select e.*,
row_number() over (order by emp_id) as orig_seqnum,
row_number() over (order by dbms_random.value) as new_seqnum
from employees e
)
select e1.*, e2.dept as new_dept
from cte e1 join
cte e2
on e1.orig_seqnum = e2.new_seqnum;
This does not guarantee that the new department is different from the old one. There are actually ways to make that happen. However, given that most of your departments have only one employee, this might solve your problem.

In SQL, omit entire record if column (of multiple values) matches condition

I'm pretty new to SQL... I have a table with the following columns :
Employee,Title,Age,Children
Output of a basic SELECT would be :
Steve |Foreman|40|Billy
Steve |Foreman|40|Amy
Steve |Foreman|40|Michelle
Daniel|Smith |35|Eric
Daniel|Smith |35|Jake
Erin |Otis |29|Eileen
Hopefully, I've shown that each record can contain multiple children. What I'd like to be able to do is to only return values if the Employee doesn't have a child who's name starts with 'E'. Right now I'm still returning Employees but it only lists the records that don't have a child starting with 'E'. I want to completely omit the Employee if any of their children start with an 'E' not just omit the child starting with 'E'.
Is this possible?
Thanks.
EDIT :
So in actuality there are two tables, one for EMPLOYEES and one for CHILDREN. So my current query looks like this :
SELECT E.EMPLOYEE_NAME, E.EMPLOYEE_TITLE, E.EMPLOYEE_AGE, C.CHILDREN_NAME
FROM EMPLOYEE E INNER JOIN CHILDREN C ON E.EMPLOYEE_ID = C.EMPLOYEE_ID
WHERE C.CHILDREN_NAME NOT LIKE 'E%'
This returns all rows minus any children that have a name starting with E. The desired effect, is solution 2 that Trinimon provided; do not return an employee if any of their children have a name that start with E.
I'm hoping that explains it a bit more and someone can explain how to produce the desired results. As mentioned, Trinimon's solution returns the proper results but since there are two tables I'm not how to adjust the solution to my schema.
Thanks.
Either go for ...
SELECT *
FROM employees
WHERE Children NOT LIKE 'E%';
if you want all records where the child's name doesn't start with E or for ...
SELECT *
FROM employees e1
WHERE NOT EXISTS (SELECT 1
FROM employees e2
WHERE e1.Employee = e2.Employee
AND Children LIKE 'E%');
if none of the returned employees should have a child that starts with E.
Check out version one and version two.
p.s. based on your structure it's
SELECT E.EMPLOYEE_NAME,
E.EMPLOYEE_TITLE,
E.EMPLOYEE_AGE,
C.CHILDREN_NAME
FROM EMPLOYEE E INNER JOIN CHILDREN C ON E.EMPLOYEE_ID = C.EMPLOYEE_ID
WHERE NOT EXISTS (SELECT 1
FROM CHILDREN C2
WHERE E.EMPLOYEE_ID = C2.EMPLOYEE_ID
AND C2.CHILDREN_NAME LIKE 'E%');
Check this Fiddle.
You could try:
select * from YourTable T
where NOT EXISTS (select 1 from YourTable where Employee=T.Employee and Children like 'E%')
This, of course, will have a problem if your have two employees with the same name. You could expand the WHERE clause to cover all the attributes that make an employee the same:
select * from YourTable T
where NOT EXISTS (select 1 from YourTable where Employee=T.Employee and Title=T.Title and Age=T.Age and Children like 'E%')
However, you should consider making Children a separate table. Have a single Employee (with a unique EmployeeID) in your table, and have Children contain each child with a reference to EmployeeID.
Here is a mysql solution in sql fiddle. I second, that you normalize your data and make a child table.
http://sqlfiddle.com/#!2/f52cc/8/0

Counting number of occurrences in subquery

My task is to find the number of occurrences of late timesheet submissions for each employee in our database. There are two tables which I have primarily been looking at, but I'm having trouble putting the two together and coming up with a decent view of the COUNT of occurrences and the employee ID for which they are associated with.
I have created this query which provides me with the EmployeeID for each occurrence.
SELECT db.Employee.EmployeeID
FROM db.LateTimesheets
INNER JOIN db.Employee ON Employee.LastName = LateTimesheets.LastName AND Employee.FirstName = Late Timesheets.FirstName
Now, with this simple query I have a view of the EmployeeID repeated however many times these incidents have occured. However, what I ultimately want to end up with is a table that displays a count for each occurance, along with the EmployeeID for which this count is associated with.
I would assume I would need to use the COUNT() function to count the amount of rows for each EmployeeID, and then select that value along with EmployeeID. However, I am having trouble structuring the subquery correctly, and everything I have tried thus far has only generated errors with MS SQL Server Management Studio.
A simpler version of usr's answer would be the following which avoids the construction of the derived table:
Select db.Employee.EmployeeID, Count( db.LateTimesheets.somecolumn ) As Total
From db.Employee
Left Join db.LateTimesheets
On LateTimesheets.LastName = Employee.LastName
And Late Timesheets.FirstName = Employee.FirstName
Group By db.Employee.EmployeeID
I may have misunderstood the question, but wouldn't GROUP BY solve your problem?
SELECT COUNT(db.LateTimesheets.somecolumn), db.Employee.EmployeeID
FROM db.LateTimesheets
INNER JOIN db.Employee ON Employee.LastName = LateTimesheets.LastName
AND Employee.FirstName = Late Timesheets.FirstName
GROUP BY db.Employee.EmployeeID
Just replace somecolumn with the name of a column that's actually in the table.
select e.*, isnull(lt.Count, 0) as Count
from Employee e
left join (
select LastName, count(*) as Count from LateTimesheets
) LateTimesheets lt on e.LastName = lt.LastName
The trick is to do the grouping in a derived table. You don't want to group everything, just the LateTimesheets.
We need a left join to still get employees with no LateTimesheets.

Why table exists two times under different aliases in FROM clause of SQL query?

I have this query:
SELECT to_number(gr.code) group_index,
gr.NAME group_name, f.*,
gr.description gr_desc
FROM obj$groups gr, obj$group_objects gro,
obj$group_objects gro2, tdf$flex_fields f,
inv$requests w, inv$Direction_Criterions c
WHERE gr.NO = gro.object_no
AND gro.group_no = obj$group_service.path_to_no(
'Some Condition String',
tdf$flex_field_service.get_local_list_group_no)
AND gro2.group_no = gr.NO
AND f.NO = gro2.object_no
AND w.no = 11593597
AND c.direction_no = w.direction_no
AND f.no = c.criterion_no
ORDER BY to_number(gr.code), f.name
Why are two same tables (group_objects) present here? I tried to reverse-engineer this, but couldn't myself, maybe anyone here already know of this trick?
This happens in Oracle database.
It's an operation called self-join. When you want to join records from the same table.
It usually happens when you have records related to records in the same table. Example:
create table tree
(
id number primary key,
parent_id number,
value varchar2(100)
);
So, if you want to retrieve nodes and their parents you would do:
select c.id, c.value, p.value as parent_value
from tree c inner join tree p on (c.parent_id = p.id)
Something similar is happening in the query you posted.
group_objects is being joined to groups in the clause gro2.group_no = gr.NO and to flex_fields in the clause f.NO = gro2.object_no. I suspect this covers the case where obe set of group object isn't exactly the same as the other set and this limits the rows in the two joins where one join removes a group_object that would then not be available to join to the other table.
It's hard to divine the original programmer's intent from this snippet, especially without a description of what the various tables hold.
However, it appears to me as if the final result of this query is supposed to report information from two different records from the group_objects table — one in which the group no matches "Some Condition String" and the other in which the group no matches a column value from the groups table. If I had to guess, it's retrieving an operation in which an item was transferred between two groups, or substituted to be used in place of an object from a second group, or something like that.
For illustration, the equivalent with the standard EMP table would be:
select e.ename, m.ename manager_name
from emp e, emp m
where m.empno = e.mgr;
Or in more modern syntax:
select e.ename, m.ename manager_name
from emp e
join emp m on m.empno = e.mgr;
i.e. show the names of employees with managers and the name of their managers.
The point is that the same table is used twice in the query in a different "role". It need not be a self-join, there could be another table (or more) in between like this:
select e.ename, pm.ename projmanager_name
from emp e
join project_assignments pa on p.empno = pa.empno
join projects p on p.proj_id = pa.proj_id
join emp pm on pm.empno = p.projmanager_empno;
i.e. show the names of employees assigned to projects and show the name of the project manager of that project.