Query throws ORA-00904 Invalid Identifier when using joins - sql

I've come across some weird behavior that's preventing me from setting up a query in the way I'd like to. Would appreciate any ideas. If there's any information about the tables that would be helpful, please let me know. I looked through them and nothing jumped out at me that might cause this but nor did I know what I was looking for. Here is the behavior.
This works fine:
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_C.TABLE_C c,
SCHEMA_A.TABLE_D d
Where
b.friend_id = c.friend_id
AND a.group_id = d.group_id
AND b.group_cd = d.group_cd
But this returns ORA-00904: b.friend_id = c.friend_id: invalid identifier
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_A.TABLE_D d
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Where
a.group_id = d.group_id
AND b.group_cd = d.group_cd
This returns ORA-00904: b.group_cd = d.group_cd: invalid identifier
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Join
SCHEMA_A.TABLE_D d
On
a.group_id = d.group_id
AND b.group_cd = d.group_cd
And this works again:
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Join
SCHEMA_A.TABLE_D d
On
b.group_cd = d.group_cd
Where
a.group_id = d.group_id

Try with the using keyword which is designed for join on same column name
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C USING(friend_id)
Join
SCHEMA_A.TABLE_D d using(group_id)
where b.group_cd = d.group_cd;
Also make sure that you execute the other query with the right users, as an user that does not have correct permission will throw invalid identifier.
Edit : The actual problem is that you are joining TABLE_C with TABLE_D but the join condition refer to TABLE_B change it to
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_A.TABLE_D d,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c using(friend_id)
Where
a.group_id = d.group_id
AND b.group_cd = d.group_cd;
Apply the same logic for other queries, when doing a join, you do not join to a group of table, but to the last table mentionned in the from clause therefore it did not have access to TABLE_B.
For other people that might have an invalid identifier error, make sure your database is not case sensitive, else make sure to use the right case.

I believe that you have run into the intricacy in SQL dealing with join scoping/precedence. Since I am not myself a database programmer, I can't really tell you why it works the way it does, but I do see some logic in the way the query parser is behaving.
In your first query, all the joins are cross joins. As a result, they should have the same precedence. So all the joined columns are known when the join is evaluated.
In the second query, you have a combination of cross joins and inner joins. If we assume that inner join has precedence over cross joins, then table c and d are joined before any other tables are added to the mix. So when you reference b.friend_id in the inner join, which is outside of the inner join, the query parser isn't able to use that column in evaluating the join.
In the third query, the inner join between tables b, c and d take precedence over the cross join. So column a.group_id isn't available in evaluating the inner join condition. When you take table a out of the inner join condition in the final query, the query no longer has a conflicting precedence.

The problem here is the mixing of join types. When you use comma-separated table in the FROM clause, each term is evaluated individually. Take the following example:
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_A.TABLE_D d
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
The database is trying to resolve SCHEMA_A.TABLE_D d Join SCHEMA_C.TABLE_C c On b.friend_id = c.friend_id. Within this scope, b has no meaning (it's a separate term).
For your third version, I get "A"."GROUP_ID": invalid identifier, which is also in-line with this explanation.
Ultimately the lesson you should take from this is not to mix join types.

Related

How to join tables using CASE WHEN in postgresql?

I have a PostgreSQL query like below and I want to join a new table to that when rateid>100
CASE WHEN rateid<100 Then
Select * FROM rate as A, plan AS B, room AS C
WHERE A.id=B.rateid
AND B.roomid=C.id
ELSE
Select * FROM rate as A, plan AS B, room AS C, hotel AS D
WHERE A.id=B.rateid
AND B.roomid=C.id
AND C.hotelid=D.id
END
Can I know is there any way to join hotel table when rateid>100?
In your question you say "when rateid>100", I assume you mean "rateid>=100" because in the code you use "rateid<100"
I also changed the old-style to new-style joins.
The on-clause tells SQL what to join, so adding "rateid>=100" should solve the problem (When I understand your question correctly)
select
*
from
rate as A,
inner join plan as B on
A.id = B.rateid
inner join room as C on
B.roomid = C.id
and rateid >= 100

Adding From statement to Join statements

So the code has multiple join statements, and all I need to add is a where clause that checks one value. The table I need to reference is not used in the From statement which includes multiple joins. Is there anyway to add a regular FROM statement without disrupting the JOINS
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
WHERE
C.Value = x --This is what I want to do. Can I add another from statement that doesn't affect the join statement
Not possible without including C somewhere.
What you're saying is equivalent to "List all the Countries where the Person is called Tom"
You can add C table, but it has to be with a JOIN. Maybe you want a CROSS JOIN, which is the cartesian product of the joining tables against this new table C and doesn't require any condition (you can filter the results with a WHERE condition).
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
CROSS JOIN C
WHERE
C.Value = x
In this particular, case it would be the same as:
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
JOIN C
ON C.Value = x
If you want to filter the first JOIN depending on a value from C then you need to put the condition on a subquery on the WHERE, like an IN or EXISTS. There has to be some correlation (a column or expression that both share) between your first JOIN and this table C, otherwise there is no point in filtering.
For example:
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
WHERE EXISTS (SELECT 'value exists on table C' FROM C WHERE C.Value = D.data)

Same ID field on multiple tables

So I've been given a task where i should present the following:
ID, LNAME, FNAME, MNAME, BIRTH_DATE, RELG_CODE, NAT_CODE, PT_STATUS, RM_NO, DTTM_ADM
The tables are:
HISR_CODES, PASR_NAMES, PASR_PROFILE, PAST_PATIENT_ADM
--Viewing them using DESC--
So while I viewed them, I was told that the ID on these tables are the same. So what I did so far in the coding (I'll finish the rest but I need to make sure this works first):
SELECT
A.ID,
A.LNAME,
A.FNAME,
A.MNAME,
A.BIRTH_DATE,
C.RELG_CODE,
C.NAT_CODE,
B.PT_STATUS,
B.RM_NO,
B.DTTM_ADM
FROM
PASR_NAMES A,
PASR_PROFILE B,
PAST_PATIENT_ADM C,
HISR_CODES D
WHERE
A.ID = B.ID
AND
B.ID = C.ID
AND
C.ID = D.ID
Is there a way to tell that all of the ID's from the tables are the same? A simpler code than going on like this:
WHERE
A.ID = B.ID
AND
B.ID = C.ID
AND
C.ID = D.ID
Or is JOIN - ON the only option for this?
You can use NATURAL JOIN as below:
SELECT
A.ID,
A.LNAME,
A.FNAME,
A.MNAME,
A.BIRTH_DATE,
C.RELG_CODE,
C.NAT_CODE,
B.PT_STATUS,
B.RM_NO,
B.DTTM_ADM
FROM
PASR_NAMES A
NATURAL JOIN PASR_PROFILE B
NATURAL JOIN PAST_PATIENT_ADM C
NATURAL JOIN HISR_CODES D;
From Oracle Reference, "A natural join is based on all columns in the two tables that have the same name." So, there is a chance that the joins happen based on other columns as well. Therefore, it is recommended that you still use the INNER JOIN syntax and explicitly specify the JOIN columns.
References:
NATURAL JOIN on Oracle® Database SQL Language Reference
Related SO question
Use the proper join syntax:
FROM PASR_NAMES A JOIN
PASR_PROFILE B
ON A.ID = B.ID JOIN
PAST_PATIENT_ADM C
ON B.ID = C.ID JOIn
HISR_CODES D
ON C.ID = D.ID
Or:
FROM PASR_NAMES A JOIN
PASR_PROFILE B
USING (ID) JOIN
PAST_PATIENT_ADM C
USING (ID)
HISR_CODES D
USING (ID)
I would discourage you from using the natural join. It might seem like the right thing at first. However, the semantics of the query are highly dependent on the structure of the tables. If columns are renamed, removed, or added, the query might still work but produce highly unexpected results.

Joining One table to many using Joins

In this question it's finally clicked how to write joins between multiple tables, where they link in a line e.g.
Table A - Table B - Table C
Where Table A references Table B, and Table B references Table C and so on.
What I still don't understand is how to reference the situation where Table A references Table B as above and also reference Table D.
In implicit Joins I can get the following to work, but want to move it to explicits...
SELECT a.name, b.office, c.firm, d.status
FROM job a, depts b, firms c, statuses d
WHERE a.office = b.ref
AND b.firm = c.ref
AND a.status = d.ref
Any tips?
SELECT
a.name,
b.office,
c.firm,
d.status
FROM
job a
JOIN depts b ON a.office = b.ref
JOIN firms c ON b.firm = c.ref
JOIN statuses d ON a.status = d.ref
That's as detailed as I could get on such an obscure question. You didn't describe what exactly does "link" mean in your case. So I don't know, maybe you need left join.

SQL joining three tables, join precedence

I have three tables: R, S and P.
Table R Joins with S through a foreign key; there should be at least one record in S, so I can JOIN:
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
If there's no record in S then I get no rows, that's fine.
Then table S joins with P, where records is P may or may not be present and joined with S.
So I do
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
LEFT JOIN P ON (P.id = S.fkp)
What if I wanted the second JOIN to be tied to S not to R, like if I could use parentheses:
SELECT
*
FROM
R
JOIN (S ON (S.id = R.fks) JOIN P ON (P.id = S.fkp))
Or is that already a natural behaviour of the cartesian product between R, S and P?
All kinds of outer and normal joins are in the same precedence class and operators take effect left-to-right at a given nesting level of the query. You can put the join expression on the right side in parentheses to cause it to take effect first. Remember that you will have to move the ON clauses around so that they stay with their joins—the join in parentheses takes its ON clause with it into the parentheses, so it now comes textually before the other ON clause which will be after the parentheses in the outer join statement.
(PostgreSQL example)
In
SELECT * FROM a LEFT JOIN b ON (a.id = b.id) JOIN c ON (b.ref = c.id);
the a-b join takes effect first, but we can force the b-c join to take effect first by putting it in parentheses, which looks like:
SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
Often you can express the same thing without extra parentheses by moving the joins around and changing the direction of the outer joins, e.g.
SELECT * FROM b JOIN c ON (b.ref = c.id) RIGHT JOIN a ON (a.id = b.id);
When you join the third table, your first query
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
is like a derived table to which you're joining the third table. So if R JOIN S produces no rows, then joining P will never yield any rows (because you're trying to join to an empty table).
So, if you're looking for precedence rules then in this case it's just set by using LEFT JOIN as opposed to JOIN.
However, I may be misunderstanding your question, because if I were writing the query, I would swap S and R around. eg.
SELECT
*
FROM
S
JOIN R ON (S.id = R.fks)
The second join is tied to S as you explicity state JOIN P ON (P.id = S.fkp) - no column from R is referenced in the join.
with a as (select 1 as test union select 2)
select * from a left join
a as b on a.test=b.test and b.test=1 inner join
a as c on b.test=c.test
go
with a as (select 1 as test union select 2)
select * from a inner join
a as b on a.test=b.test right join
a as c on b.test=c.test and b.test=1
Ideally, we would hope that the above two queries are the same. However, they are not - so anybody that says a right join can be replaced with a left join in all cases is wrong. Only by using the right join can we get the required result.