Combining SQL statements into one - sql

I have the task to read data of two tables which requires several SQL statements, with only 1 query.
I know that this is possible and i have done some research on that matter but this sadly did not help me as i had no clue how to apply this to my problem.
I believe it is not too complex but because of my lack of SQL experience it's quite a challenge for me.
For understanding purposes i would like to know how to combine these three statements into one.
I have two tables. "PEmpl" and "Department" and i have the value ID.
PEmpl has the columns: "DepartmentID" and "PEmplID"
Department has the columns: "DepartmentID" "DepartmentName" and "FatherDepID"
I have the EmployerID and want to find out to what departments he has permissions so i came up with these 3 statements.
(I already programmed it working in C# with these querys, but sadly the task was to do it in only 1 query and no c#)*
SELECT DepartmentID
FROM PEmpl
WHERE PEmplID = #ID <-- the ID i have
SELECT DepartmentName
FROM Department
WHERE DepartmentID = #DepartmentID <- The DepID i got from the last query
SELECT DepartmentName
FROM Department
WHERE FatherDepID = #DepartmentID <- same ID as last querys
But i have no idea how to combine these into 1. I hope that someone can give me some clue about this. I dont expect to get the finished answer but a nudge into the right direction would be appreciated.
This is what i programmed in C#. I have the Person and it's ID and there you see that he has permission for "Musterfirma Gmbh" and because this is the father department of the others, he has also permission for those.
PS: I hope i explained it understandable. If not let me know and i will try to rephrase it :)

I am not sure I have understood correctly your question (pls post some sample data). I think you could try something like following query.
I use only one join with a OR condition, and add a column to identify if father, direct or both deps.
I used "aliases" of tables (A for PEmpl and B for Department to simplify reading the query)
SELECT B.DepartmentName
, CASE WHEN A.DepartmentID=B.DepartmentID AND A.DepartmentID=B.FatherDepID THEN 'B' /* both */
WHEN A.DepartmentID=B.DepartmentID THEN 'D' /* "direct" dep */
ELSE 'F' /* father */
END AS GR /* case updated thanks to Caleth suggestion */
FROM PEmpl A
INNER JOIN Department B ON A.DepartmentID=B.DepartmentID OR A.DepartmentID=B.FatherDepID
WHERE A.PEmplID = #ID
If you need only records without (possible) "repetitions" of departments, you can remove GR column and use DISTINCT after SELECT.

Like this maybe
SELECT
DepartmentID as dep0
FROM PEmpl
LEFT JOIN Department as dep1 on (dep0.DepartmentID = dep1.DepartmentID)
LEFT JOIN Department as dep2 on (dep0.DepartmentID = dep2.DepartmentID)
WHERE PEmplID = #ID

You might be looking for this
SELECT
A.DepartmentID,
A.PEmplID,
B.DepartmentID,
B.DepartmentName,
B.FatherDepID
FROM PEmpl A
INNER JOIN Department B
ON A.DepartmentID = B.DepartmentID
AND A.PEmplID = #ID;

Try this out, if it isn't working, fix joining columns, and check out joins that's what you need
SELECT d2.DepartmentId, d2.DepartmentName
FROM PEmpl e
JOIN Department d1 on e.DepartmentID = d1.DepartmentID
JOIN Department d2 on d2.FatherDepID = d1.DepartmentID
WHERE e.PEmplID = #ID <-- your ID

You need a JOIN, or possibly many.
WITH Deps AS (
-- This finds the parent departments. We have two copies of the same DepartmentID to have the same shape results as below
SELECT DepartmentName, DepartmentID AS Link, DepartmentID
FROM Departments
-- Combine results from two SELECT clauses (without duplicates)
UNION
-- This will find all the child departments, all the child's children, etc
SELECT child.DepartmentName, child.DepartmentID AS Link, parent.DepartmentId
FROM Departments AS child
-- We can join on the results we are selecting, to follow links
JOIN Deps AS parent ON child.FatherDepID = parent.Link )
SELECT DepartmentName
-- To add any other columns from Departments, be sure to include them in both SELECT clauses above
FROM PEmpl
JOIN Deps ON PEmpl.DepartmentID = Deps.DepartmentID

Related

SQL for each row do something

I have a SQL query that finds the "departments" the specified person has access to.
And some departments are subdepartments of others, so when the person has acces to a department he has also access to all of his subdepartments.
This works because in the departments table, every department has a name and ID and a "fatherID" which specifies what department is his father department.
But it is also possible that this nests even further and i found no way to show the names of all departments below 1 level of subdepartments.
SELECT B.DepartmentDesc FROM CompanyDepartmentPermission A
INNER JOIN CompanyDepartment] B
ON A.DepartmentID=B.DepartmentID
OR A.DepartmentID=B.FatherDepartmentID
WHERE A.PEmplID = 68
This is possible. I get all the names from the department and all his subdepartments:
But in this case i get every subdepartment, except the on nested a level lower.
My goal is to be able to recursively also show all nested subdepartments.
And i HAVE to do this in only 1 query. As i am very inexperienced and have not much insight in nested querys i have not found a way to solve this. I hope someone can give me a nudge to the right direction.
PS: The pictures where from a C# program i made where i made it work and this is just for clarification how it looks. And i hope i could explain what i want. Otherwise let me know and i will try to rephrase this :)
EDIT:
This is de structure of the two tables. Thanks Akaino for pointing out
SOLUTION:
Sadly none of the asnwers here helped me, but instead a halfway similar question here on SO.
The WITH UNION statements helped me a lot. This is The working query for my problem.
WITH DepartmentHirarchy AS
(
SELECT B.DepartmentID,DepartmentDesc,FatherDepartmentID, DepartmentResponsiblePEmplID
FROM CompanyDepartment AS B
INNER JOIN CompanyDepartmentPermission AS A ON A.DepartmentID=B.DepartmentID
WHERE A.PEmplID= 34 OR DepartmentResponsiblePEmplID = 34
UNION ALL
SELECT nextLevel.DepartmentID,nextLevel.DepartmentDesc,nextLevel.FatherDepartmentID, nextLevel.DepartmentResponsiblePEmplID
FROM DepartmentHirarchy AS recCall
INNER JOIN CompanyDepartment AS nextLevel ON nextLevel.FatherDepartmentID=recCall.DepartmentID
)
SELECT * FROM DepartmentHirarchy
ORDER BY FatherDepartmentID,DepartmentID
GO
For parent/child tree hierarchy, try the following SQL code.
I assume, your table name is Departments. You can change the table name in the following SQL.
I works with SQL Server.
WITH RELATION (DepartmentID, FatherDepartmentID, DepartmentDesc, [Level], [Order])
AS
(
SELECT DepartmentID, FatherDepartmentID, DepartmentDesc, 0, CAST(DepartmentID AS VARCHAR(200))
FROM Departments
WHERE FatherDepartmentID IS NULL
UNION ALL
SELECT P.DepartmentID, P.FatherDepartmentID, P.DepartmentDesc, R.[Level]+1, CAST(R.[Order] + '_' + CAST(p.DepartmentID AS VARCHAR(200)) AS VARCHAR(200))
FROM Departments P
JOIN RELATION R ON P.FatherDepartmentID = R.DepartmentID
)
SELECT RIGHT('----------', ([Level]*3)) + DepartmentDesc
FROM RELATION R
JOIN CompanyDepartment D ON D.DepartmentID = R.DepartmentID
WHERE PEmplID= 68
ORDER BY R.[ORDER]
It seems like you want to do an hierarchy search.
If your server supports the use of the hierarchy datatype (if not, you might want to consider it if you are planning to use a lot of hierachy searches) you could try and use the isDescendantOf method.
Example
DECLARE #parent hierarchyid
SELECT #parent = B.DepartmentDesc
FROM CompanyDepartment B
Where B.DepartmentDesc = 'Name your deparment'
SELECT *
FROM CompanyDepartment
WHERE DepartmentDesc.IsDescendantOf(#parent) = 1 ;

SQL - Why Does This Happen?

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

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

Duplicate table in Oracle query

I have a query that looks like this:
SELECT *
FROM
employees e,
departments d1,
departments d2,
departments d3,
departments d4
WHERE e.dep1 = d1.dep(+)
AND e.dep2 = d2.dep(+)
AND e.dep3 = d3.dep(+)
AND e.dep4 = d4.dep(+);
Is there a better way to write this so that I don't need to use the same table in my query multiple times? I know Oracle's optimizer probably works around this rather nicely, but if there is a more efficient way to write a query like this, I'm all ears. Keep in mind that the above is merely an example, my actual query has a lot more going on in it. I'm using Oracle 11.2.0.3 on Windows 2003 x64.
Thanks,
Tom
This is perfectly valid.
The contrived example me and my colleague often use is city_of_birth and city_of_residence. Let's assume we want to query employees who now live in a different country to which they were born in (and that all countries have states).
You would query this as follows:
select e.*
from employees e,
cities city_of_birth,
cities city_of_residence,
states state_of_birth,
states state_of_residence
where e.city_of_birth_id = city_of_birth.id
and e.city_of_residence_id = city_of_residence.id
and city_of_birth.state_id = state_of_birth.id
and city_of_residence.state_id = state_of_residence.id
and state_of_birth.country_id != state_of_residence.country_id;
The thing to note is that tables need to be referenced in the from clause of a query as often as there are different meanings to them.
Another way to think about it: you need to reference the same table multiple times in the from clause if you're going to be selecting different rows from each "instance".
If you want the details to all the departments flat in one row you need several joins but I suggest to write it this way:
SELECT *
FROM employees e
LEFT JOIN departments d1 on ( d1.dep = e.dep1 )
LEFT JOIN departments d2 on ( d2.dep = e.dep1 )
LEFT JOIN departments d3 on ( d3.dep = e.dep1 )
LEFT JOIN departments d4 on ( d4.dep = e.dep1 )
this doesn't make much sense to me... you are comparing an employee to find their department (when they have many possible in non-normalized columns) - what about using an OR clause...
SELECT *
FROM
employees e,
departments d
WHERE ( e.dep1 = d.dep
OR e.dep2 = d.dep
OR e.dep3 = d.dep
OR e.dep4 = d.dep );
or better - rework your table so it is properly structured. :)

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.