Duplicate table in Oracle query - sql

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. :)

Related

On an SQL Select, how do i avoid getting 0 results if I want to query for optional data in another table?

I have a table with Customers which includes their contact person in the helpdesk. I have another table that lists all vacancies of the helpdesk employees - if they are currently sick or on vacation etc.
I need to get the helpdesk contact and the start/end time of their vacation IF there is an entry.
I currently have this (simplified):
SELECT *
FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND dbo.Projects.HDContactID = dbo.Vacations.HDContactID
So if there is a vacation listed in the Vacations table, it works fine, but if there is no vacation at all, this will not return anything - what i want is that if there is no vacation, it simply returns the other data, and ignores the missing data (returns NULL, doesn't return anything, not important)
In any case, I need to get the Customers and Project data, even if the query can't find an entry in the Vacations table. How would I do this? I pretty new to SQL and couldn't find a similar question on this site
EDIT: I'm using SQL Server, currently using HeidiSQL
Try below query:
SELECT * FROM dbo.Customers, dbo.Projects
left join dbo.Vacations on dbo.Projects.HDContactID = dbo.Vacations.HDContactID
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
Use left join as mentioned by #Flying Thunder,
Example of the left join:
SELECT country.country_name_eng, city.city_name, customer.customer_name
FROM customer
LEFT JOIN city ON customer.city_id = city.id
LEFT JOIN country ON city.country_id = country.id;
You can find a nice guide for the joins and SQL here:
https://www.sqlshack.com/learn-sql-join-multiple-tables/
You should be using LEFT JOIN. In fact, you should never be using commas in the FROM clause. That is just archaic syntax and closes the powerful world of JOINs from your queries.
I also recommend using table aliases that are abbreviations of table names. The best are abbreviations for the table names:
SELECT *
FROM dbo.Customers c LEFT JOIN
dbo.Projects p
ON c.CustomerID = p.CustomerID LEFT JOIN
dbo.Vacations v
ON p.HDContactID = v.HDContactID
WHERE c.Phone = $Phone;
Have you try this to skip vacation record if not present like this:
SELECT * FROM dbo.Customers, dbo.Projects, dbo.Vacations
WHERE ($Phone = dbo.Customers.Phone)
AND dbo.Customers.CustomerID = dbo.Projects.CustomerID
AND (dbo.Vacations.HDContactID IS NULL OR dbo.Projects.HDContactID = dbo.Vacations.HDContactID)

Combining SQL statements into one

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

Is there a SQL statement that will allow me to see results that match from 2 different tables?

I'm joining two tables (Employee and Matrix) and I'm wondering if there is a statement I can use to only show the names of employees that have a matrix attached to their names after joining the Employee and Matrix tables. I thought of possibly using 'Distinct', but I don't believe that would work as I'm trying to only see employees that have a matrix and this information would be coming from two different tables.
Any suggestion is appreciated. Thank you in advance.
I think exists does what you want:
select e.*
from employee e
where exists (select 1 from matrix m where m.employee_id = e.employee_id);
It would definitely work:
SELECT DISTINCT e.name
FROM employee e
INNER JOIN matrix m
ON e.id = m.employee_id

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

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.