ORA-00904 when using OUTER JOIN with 3 tables in clause - sql

SELECT
s.session_name semester
FROM
lsx.elsp_session s,
lsx.elsp_course_offering co,
its.ACCOUNT ac
LEFT OUTER JOIN lsx.elsp_class_attendance ca on ca.course_id = CO.ID and ca.student_number = ac.id
WHERE co.session_id = s.session_id
I am getting an ORA-00904 error and the problem seems to be with "CO.ID" being an invalid identifier. However, if I put the "lsx.elsp_course_offering co" table to be last in the FROM list, then "ac.id" becomes the problem identifier.
Is it not possible to use another table in a JOIN clause? I seem to recall creating successful queries like this using MySQL.
I am using Oracle 9.2.0.8.0.
Following the advice I received, I was able to get the join working as expected when I reworked the query to this:
SELECT s.session_name semester FROM lsx.elsp_session s
INNER JOIN lsx.elsp_course_offering co
ON co.session_id = s.session_id
LEFT OUTER JOIN lsx.elsp_class_attendance ca
ON ca.course_id = co.id
LEFT OUTER JOIN its.account ac
ON ca.student_number = ac.id
Many thanks for your help.

You may have misunderstood the comment above. Generally people do not mix the multiple FROM tables with JOIN syntax.
I think you should write it this way instead. It should be easier to follow this way.
SELECT s.session_name semester
FROM lsx.elsp_session s
INNER JOIN lsx.elsp_course_offering co
ON co.session_id = s.session_id
INNER JOIN its.account ac
ON ca.student_number = ac.id
LEFT OUTER JOIN lsx.elsp_class_attendance ca
ON ca.course_id = co.id
and then see if you get syntax errors.

Related

TSQL: How does this JOIN resolve?

I am doing these joins:
from #lps_at_lines2 as l2
left join jobmatl as jm on l2.item = jm.item
inner join job as j on jm.job = j.job
and jm.suffix = j.suffix
I'm not sure how the join would resolve and the official documentation is like reading hieroglyphics to me.
My guess is that first #lps_at_lines2 gets LEFT JOIN'd to jobmatl and then somehow job gets INNER JOIN'd to jm afterwards. Is that correct?
In a FROM clause, JOINs are parsed left to right -- the reading order in English. So, the LEFT JOIN is processed (logically) before the INNER JOIN.
The INNER JOIN conditions include:
jm.job = j.job and jm.suffix = j.suffix
These refer to the second table of the LEFT JOIN. Because NULL values fail, the INNER JOIN is turning the preceding LEFT JOIN into an INNER JOIN. In other words, you should get the same results using INNER JOIN for both.
Note that you can adjust the prioritization by using parentheses, but your version of the query does not do this.
In general, when mixing inner joins and left joins, I start by including all the inner joins and then LEFT JOINing the additional tables.

SQL - Consecutive "ON" Statements

As I was cleaning up some issues in an old view in our database I came across this "strange" join condition:
from
tblEmails [e]
join tblPersonEmails [pe]
on (e.EmailID = pe.EmailID)
right outer join tblUserAccounts [ua]
join People [p]
on (ua.PersonID = p.Id)
join tblChainEmployees [ce]
on (ua.PersonID = ce.PersonID)
on (pe.PersonID = p.Id)
Table tblUserAccounts is referenced as a right outer join, but the on condition for it is not declared until after tblChainEmployees is referenced; then there are two consecutive on statements in a row.
I couldn't find a relevant answer anywhere on the Internet, because I didn't know what this kind of join is called.
So the questions:
Does this kind of "deferred conditional" join have a name?
How can this be rewritten to produce the same result set where the on statements are not consecutive?
Maybe this is a "clever" solution when there has always been a simpler/clearer way?
(1) This is just syntax and I've never heard of some special name. If you read carefully this MSDN article you'll see that (LEFT|RIGHT) JOIN has to be paired with ON statement. If it's not, expression inside is parsed as <table_source>. You can put parentheses to make it more readable:
from
tblEmails [e]
join tblPersonEmails [pe]
on (e.EmailID = pe.EmailID)
right outer join
(
tblUserAccounts [ua]
join People [p]
on (ua.PersonID = p.Id)
join tblChainEmployees [ce]
on (ua.PersonID = ce.PersonID)
) on (pe.PersonID = p.Id)
(2) I would prefer LEFT syntax, with explicit parentheses (I know, it's a matter of taste). This produces the same execution plan:
FROM tblUserAccounts ua
JOIN People p ON ua.PersonID = p.Id
JOIN tblChainEmployees ce ON ua.PersonID = ce.PersonID
LEFT JOIN
(
tblEmails e
JOIN tblPersonEmails pe ON e.EmailID = pe.EmailID
) ON pe.PersonID = p.Id
(3) Yes, it's clever, just like some C++ expressions (i.e. (i++)*(*t)[0]<<p->a) on interviews. Language is flexible. Expressions and queries can be tricky, but some 'arrangements' lead to readability degradation and errors.
Looks to me like you have tblEmail and tblPerson with their own independent IDs, emailID and ID (person), a linking table tblPersonEmail with the valid pairs of emailID/IDs, and then the person table may have a 1-1 relationship with UserAccount, which may then have a 1-1 relationship with chainEmployee, so to get rid of the RIGHT OUTER JOIN in favor of LEFT, I'd use:
FROM
((tblPerson AS p INNER JOIN
(tblEmail AS e INNER JOIN
tblPersonEmail AS pe ON
e.emailID = pe.emailID) ON
p.ID = pe.personID) LEFT JOIN
tblUserAccount AS ua ON
p.ID = ua.personID) LEFT JOIN
tblChainEmployee AS ce ON
ua.personID = ce.personID
I can't think of a great practical example of this off the top of my head so I'll give you a generic example that hopefully makes sense. Unfortunately I'm not aware of a generic name for this either.
Many people will start off with a query like this:
select ...
from
A a left outer join
B b on b.id = a.id left outer join
C c on c.id2 = b.id2;
The look at the results and realize that they really need to eliminate the rows in B that don't have a corresponding C but if you tried to say where b.id2 is not null and c.id2 is not null you've defeated the whole purpose of the left join from A.
So next you try to do this but it doesn't take long to figure out it's not going to work. The inner join at the tail end of the chain has basically converted both the joins to inner joins.
select ...
from
A a left outer join
B b on b.id = a.id inner join
C c on c.id2 = b.id2;
The problem seems simple yet it doesn't work right. Essentially after you ponder for a while you discover that you need to control the join order and do the inner join first. So the three queries below are equivalent ways to accomplish that. The first one is probably the one you're more familiar with:
select ...
from
A a left outer join
(select * from B b inner join C c on c.id2 = b.id2) bc
on bc.id = a.id
select ...
from
A a left outer join
B b inner join
C c on c.id2 = b.id2
on b.id = a.id
select ...
from
B b inner join
C c on c.id2 = b.id2 right outer join -- now they can be done in order
A a on a.id = b.id
You query is a little more complicated but ultimately the same issues came into play which is where the odd stuff came from. SQL has evolved and you have to remember that platforms didn't always have the fancy things like derived tables, scalar subqueries, CTEs so sometimes people had to write things this way. And then there were graphical query builders with a lot of limitations in older versions of tools like Crystal Report that didn't allow for complex join conditions...

Two inner joins with same tables

I have this problem I've been working on for a while, and I just haven't been able to figure out what's wrong with my query. Today, I finally got a bit closer, and now I think I'm pretty close and not too far from succeeding, but I need your help to spot the mistake in this query, because I think I've stared blind on it. This is the error I get:
"The objects "webpages_UsersInRoles" and "webpages_UsersInRoles" in the FROM clause have the same exposed names. Use correlation names to distinguish them."
SELECT * FROM Users
INNER JOIN webpages_UsersInRoles
ON webpages_UsersInRoles.UserId = Users.UserId
INNER JOIN webpages_UsersInRoles
ON webpages_UsersInRoles.RoleId = webpages_Roles.RoleId
WHERE Users.UserId = 1
Thank you in advance!
You have to use an alisas for the tables:
SELECT * FROM Users
INNER JOIN webpages_UsersInRoles w1 ON w1.webpages_UsersInRoles.UserId = Users.UserId
INNER JOIN webpages_UsersInRoles w2 ON w1.webpages_UsersInRoles.RoleId = w2.webpages_Roles.RoleId
WHERE Users.UserId = 1
I think below is the final query that you need. I have used alias as TAB1,TAB2 and TAB3 for clarity and to avoid confusion. Change it as required.
SELECT * FROM Users TAB1
INNER JOIN webpages_UsersInRoles TAB2
ON TAB2.UserId = TAB1.UserId
INNER JOIN webpages_Roles TAB3
ON TAB2.RoleId = TAB3.RoleId
WHERE TAB1.UserId = 1
Btw,JUST FOR INFO.... probably you get "The objects "webpages_UsersInRoles" and "webpages_UsersInRoles" in the FROM clause have the same exposed names. Use correlation names to distinguish them." error in the line mentioned below from your original query:
SELECT * FROM Users
INNER JOIN webpages_UsersInRoles
ON webpages_UsersInRoles.UserId = Users.UserId
INNER JOIN webpages_UsersInRoles
ON webpages_UsersInRoles.RoleId -----> Not able to identify which webpages_UsersInRoles is being called (First inner join or second inner join)
= webpages_Roles.RoleId
WHERE Users.UserId = 1
I see 3 tables ... Users, webpages_UsersInRoles and webpages_Roles
Only 2 are called.
webpages_Roles ??
Maybe with webpages_Roles in the second join :
SELECT * FROM Users
INNER JOIN webpages_UsersInRoles
ON webpages_UsersInRoles.UserId = Users.UserId
INNER JOIN webpages_Roles
ON webpages_UsersInRoles.RoleId = webpages_Roles.RoleId
WHERE Users.UserId = 1

How to using Left Outer Join or Right Outer Join in Oracle 11g

I have a query using "=" in where clause, but it is long time to execute when many datas. How to use the Left Outer Join or Right Outer Join or something like that to increase performance
This is query:
select sum(op.quantity * op.unit_amount) into paid_money
from tableA op , tableB ssl, tableC ss, tableD pl, tableE p
where (op.id = ssl.id and ssL.id = ss.id and ss.type='A')
or
(op.id = pl.id and pl.id = p.id and p.type='B');
Your problem is not left or right joins. It is cross joins. You are doing many unnecessary cartesian products. I'm guessing this query will never finish. If it did, you'd get the wrong answer anyway.
Split this into two separate joins and then bring the results together. Only use the tables you need for each set of joins:
select SUM(val) into paid_money
from (select sum(op.quantity * op.unit_amount) as val
from tableA op , tableB ssl
where (op.id = ssl.id and ssL.id = ss.id and ss.type='A')
union all
select sum(op.quantity * op.unit_amount) as val
from tableA op , tableD pl, tableD p
where (op.id = pl.id and pl.id = p.id and p.type='B')
) t
I haven't fixed your join syntax. But, you should learn to use the join keyword and to put the join conditions in an on clause rather than the where clause.
Are you sure that this query is returning the required data? To me it looks like it will be returning the cartesian product of op, ssl & ss for each op, pl, p match and vice versa.
I would advise that you split it into two seperate queries, union them together, and then sum over the top.

SQL Multi Innerjoin

Hello in my system i have a search page for a student that the admin will be able to view
the students history i have a problem with showing the last name of his/her adviser which is lname_A. This is the code i currently us so far everything is ok except i cant manage to get the lname_a.
$qry_display = "SELECT
a.student_id, a.section_id, a.level, a.photo, a.address, a.father_occupation, a.father_phone, a.father_company, a.mother_occupation, a.mother_phone, a.mother_company,a.gpa,
b.fname, b.sex, b.lname, b.mname, b.birth_date, b.birth_place, b.address, b.father, b.father_degree, b.mother, b.mother_degree,
c.section_name, d.adviser_id , d.lname_a
FROM tbl_er AS a
LEFT OUTER JOIN tbl_enroll AS b ON a.student_id = b.student_id
LEFT OUTER JOIN tbl_section AS c ON a.section_id = c.section_id
LEFT OUTER JOIN tbl_adviser AS d ON a.section_id = d.adviser_id
WHERE a.student_id=".$id." AND a.level='Grade 2'";
Would gladly appreciate any help.
Are you sure you are joining both tables or correct columns?
a.section_id = d.adviser_id
If every student has adviser then you should use inner join rather than left outer join.
When you use left outer join there is chance lname_a is empty when the student doesn't has adviser.