oracle ignores invalid identifier error in subquery - sql

I do not understand why the following query works, although the subquery gives an "invalid identifier" error.
SELECT *
FROM aircraft
WHERE airc_manufact IN (SELECT airc_manufact FROM flight);
My tables look the following (abbreviated):
AIRCRAFT (airc_model (PK), airc_manufact)
FLIGHT (flt_no (PK), airc_model (FK))
If I run the subquery on its own, then I receive an "invalid identifier" error like it should since the airc_manufact is not a column in the flight table.
If I run the whole query, then I do not receive an error. Oracle seems to ignore the subquery and thus give me all row in the aircraft table.
To me, this seems to be a bug because there is an obvious error in the query. Why does the query run? My understanding is that Oracle would first run or evaluate the subquery, and then run the outer query.

You have not qualified your column names. So, you think you are running:
SELECT a.*
FROM aircraft a
WHERE a.airc_manufact IN (SELECT f.airc_manufact FROM flight f);
If f.airc_manufact doesn't exist, then the scoping rules say to look in the outer query. So, what you are really running is:
SELECT a.*
FROM aircraft a
WHERE a.airc_manufact IN (SELECT a.airc_manufact FROM flight f);
That is pretty useless as a filtering clause.
Moral: Always qualify column names in a query, particularly if the query refers to more than one table.

Related

Error when using join and join produces error

I am trying to make groups and make joins with the below tables but I get an
ORA-00918: column ambiguously defined
error.
Any ideas how to fix?
SELECT staffn, job, COUNT(*)"staffcount", AVG(sal)"AverageSal"
FROM staff, shop
WHERE staff.shopno= shop.shopno
GROUP BY shopno, job;
You should use proper alias and your group by clause must include all the unaggregated columns as follows:
SELECT s.staffn, sh.job,
COUNT(*)"staffcount",
AVG(s.sal)"AverageSal"
FROM staff s join shop sh
WHERE s.shopno= sh.shopn
Group by s.staffn, sh.job
Did you mean to include staffn In your select? I would guess that this was unique to a row in staff and would make selecting the average (or any other aggregation) sal a bit useless (and if you did want to do that, you’d need to include it in the group by). I think you really meant to select the same column in your group by.
Your error is telling you that Oracle doesn’t know where a column should be taken from, multiple row sources in your query could provide it. The complete error message will also make it clear which column this is referring to, but we can already see that at least shopno is shared, we can arbitrarily take it from staff.
SELECT staff.shopno, job, COUNT(*)"staffcount", AVG(sal)"AverageSal"
FROM staff, shop
WHERE staff.shopno= shop.shopno
GROUP BY staff.shopno, job;
In both tables you used, there is at least a field with the same name. You must specify which field used which table.
for more information
Never use commas in the FROM clause. Always use proper, explicit, standard, readable JOIN syntax.
In a query that references multiple tables, qualify all column references.
You haven't shown the layout of your tables, but you presumably want something like this:
SELECT st.staffn, st.job, COUNT(*) as staffcount,
AVG(st.sal) as AverageSal
FROM staff st JOIN
shop sh
ON st.shopno = sh.shopno
GROUP BY st.staffn, st.job;
This assumes that all the columns come from the staff table, which seems reasonable enough in the absence of other information.

SQL and find records that don't match a criteria

Can anyone help me with the following statement. I'm new to SQL and maybe overlooking the obvious.
I am trying to run the following query
There is a shipments table with the corresponding columns (PROJECTNO, SUPPLIERNO)
I know I don't the to put SHIPMENTS. in front of the names, but this will eventually end up in a multi table query.
SELECT sup1.SHIPMENTS.PROJECTNO, sup2.SHIPMENTS.PROJECTNO
FROM SHIPMENTS sup1, SHIPMENTS sup2
WHERE sup1.SHIPMENTS.PROJECTNO = sup2.SHIPMENTS.PROJECTNO
AND sup1.SHIPMENTS.SUPPLIERNO <> sup2.SHIPMENTS.SUPPLIERNO;
I keep getting the following error.
ORA-00904: "SUP2"."SHIPMENTS"."SUPPLIERNO": invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 160 Column: 34
THanks in advance
Your query should be written as:
SELECT sup1.PROJECTNO, sup2.PROJECTNO
FROM SHIPMENTS sup1 JOIN
SHIPMENTS sup2
on sup1.PROJECTNO = sup2.SHIPMENTS.PROJECTNO AND
sup1.SUPPLIERNO <> sup2.SUPPLIERNO;
First, when using an alias there is no need to specify the table name again. Also, you should use explicit join syntax, with an on condition. The implicit syntax, with the condition in the where clause, is supported, but it is less powerful and more difficult to read.
Change all sup2.SHIPMENTS.SUPPLIERNO to sup2.SUPPLIERNO, and do the same for sup1 alias, and for the other colummns.
You must understand aliasing. Once you alias table TAB as T then you should refer to it as simply T.
In your FROM clause, when you say
FROM SHIPMENTS sup1, SHIPMENTS sup2
you are aliasing the SHIPMENTS table as sup1 and sup2 (as 2 instances of same table). So you need to use sup1 and sup2 as the table name, and the real name (SHIPMENTS) isn't valid, and would be ambiguous anyway, so use:
sup1.SUPPLIERNO --refers to SUPPLIERNO column in SHIPMENTS table aliased as sup1
sup2.PRODUCTNO --refers to PRODUCTNO column in SHIPMENTS table aliased as sup2
there is no such thing as sup2.SHIPMENTS.SUPPLIERNO because there is no such thing as a sup2.SHIPMENTS table
Also, a general rule to keep in mind, in Oracle you can use notation like table.column, schema.table or schema.table.column to refer to tables/views or columns. In this case what you've written amounts to table.table.column which doesn't make sense (unless you were using a nested table).
The reason you must use aliasing here is that it is the only way to join a table to itself. If you use the full table name twice, you'll get ambiguity errors. When you self-join a table, Oracle treats each instance (alias) as a distinct table in the query. I prefer to use aliases like a and b to reduce typos.
SELECT a.PROJECTNO, b.PROJECTNO
FROM SHIPMENTS a JOIN SHIPMENTS b USING(PROJECTNO)
WHERE a.SUPPLIERNO <> b.SUPPLIERNO;

Create new table from average of multiple columns in multiple tables

I have the following query:
CREATE TABLE Professor_Average
SELECT Instructor, SUM( + instreffective_avg + howmuchlearned_avg + instrrespect_avg)/5
FROM instreffective_average, howmuchlearned_average, instrrespect_average
GROUP BY Instructor;
It is telling me that Instructor is ambiguous. How do I fix this?
Qualify instructor with the name of the table it came from.
For example: instreffective_average.Instructor
If you don't do this, SQL will guess which table of the query it came from, but if there are 2 or more possibilities it doesn't try to guess and tells you it needs help deciding.
Your query most likely fails in more than one way.
In addition to what #Patashu told you about table-qualifying column names, you need to JOIN your tables properly. Since Instructor is ambiguous in your query I am guessing (for lack of information) it could look like this:
SELECT ie.Instructor
,SUM(ie.instreffective_avg + h.howmuchlearned_avg + ir.instrrespect_avg)/5
FROM instreffective_average ie
JOIN howmuchlearned_average h USING (Instructor)
JOIN instrrespect_average ir USING (Instructor)
GROUP BY Instructor
I added table aliases to make it easier to read.
This assumes that the three tables each have a column Instructor by which they can be joined. Without JOIN conditions you get a CROSS JOIN, meaning that every row of every table will be combined with every row of every other table. Very expensive nonsense in most cases.
USING (Instructor) is short syntax for ON ie.Instructor = h.Instructor. It also collapses the joined (necessarily identical) columns into one. Therefore, you would get away without table-qualifying Instructor in the SELECT list in my example. Not every RDBMS supports this standard-SQL feature, but you failed to provide more information.

Why is selecting specified columns, and all, wrong in Oracle SQL?

Say I have a select statement that goes..
select * from animals
That gives a a query result of all the columns in the table.
Now, if the 42nd column of the table animals is is_parent, and I want to return that in my results, just after gender, so I can see it more easily. But I also want all the other columns.
select is_parent, * from animals
This returns ORA-00936: missing expression.
The same statement will work fine in Sybase, and I know that you need to add a table alias to the animals table to get it to work ( select is_parent, a.* from animals ani), but why must Oracle need a table alias to be able to work out the select?
Actually, it's easy to solve the original problem. You just have to qualify the *.
select is_parent, animals.* from animals;
should work just fine. Aliases for the table names also work.
There is no merit in doing this in production code. We should explicitly name the columns we want rather than using the SELECT * construct.
As for ad hoc querying, get yourself an IDE - SQL Developer, TOAD, PL/SQL Developer, etc - which allows us to manipulate queries and result sets without needing extensions to SQL.
Good question, I've often wondered this myself but have then accepted it as one of those things...
Similar problem is this:
sql>select geometrie.SDO_GTYPE from ngg_basiscomponent
ORA-00904: "GEOMETRIE"."SDO_GTYPE": invalid identifier
where geometrie is a column of type mdsys.sdo_geometry.
Add an alias and the thing works.
sql>select a.geometrie.SDO_GTYPE from ngg_basiscomponent a;
Lots of good answers so far on why select * shouldn't be used and they're all perfectly correct. However, don't think any of them answer the original question on why the particular syntax fails.
Sadly, I think the reason is... "because it doesn't".
I don't think it's anything to do with single-table vs. multi-table queries:
This works fine:
select *
from
person p inner join user u on u.person_id = p.person_id
But this fails:
select p.person_id, *
from
person p inner join user u on u.person_id = p.person_id
While this works:
select p.person_id, p.*, u.*
from
person p inner join user u on u.person_id = p.person_id
It might be some historical compatibility thing with 20-year old legacy code.
Another for the "buy why!!!" bucket, along with why can't you group by an alias?
The use case for the alias.* format is as follows
select parent.*, child.col
from parent join child on parent.parent_id = child.parent_id
That is, selecting all the columns from one table in a join, plus (optionally) one or more columns from other tables.
The fact that you can use it to select the same column twice is just a side-effect. There is no real point to selecting the same column twice and I don't think laziness is a real justification.
Select * in the real world is only dangerous when referring to columns by index number after retrieval rather than by name, the bigger problem is inefficiency when not all columns are required in the resultset (network traffic, cpu and memory load).
Of course if you're adding columns from other tables (as is the case in this example it can be dangerous as these tables may over time have columns with matching names, select *, x in that case would fail if a column x is added to the table that previously didn't have it.
why must Oracle need a table alias to be able to work out the select
Teradata is requiring the same. As both are quite old (maybe better call it mature :-) DBMSes this might be historical reasons.
My usual explanation is: an unqualified * means everything/all columns and the parser/optimizer is simply confused because you request more than everything.

Ambiguity in Left joins (oracle only?)

My boss found a bug in a query I created, and I don't understand the reasoning behind the bug, although the query results prove he's correct. Here's the query (simplified version) before the fix:
select PTNO,PTNM,CATCD
from PARTS
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD);
and here it is after the fix:
select PTNO,PTNM,PARTS.CATCD
from PARTS
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD);
The bug was, that null values were being shown for column CATCD, i.e. the query results included results from table CATEGORIES instead of PARTS.
Here's what I don't understand: if there was ambiguity in the original query, why didn't Oracle throw an error? As far as I understood, in the case of left joins, the "main" table in the query (PARTS) has precedence in ambiguity.
Am I wrong, or just not thinking about this problem correctly?
Update:
Here's a revised example, where the ambiguity error is not thrown:
CREATE TABLE PARTS (PTNO NUMBER, CATCD NUMBER, SECCD NUMBER);
CREATE TABLE CATEGORIES(CATCD NUMBER);
CREATE TABLE SECTIONS(SECCD NUMBER, CATCD NUMBER);
select PTNO,CATCD
from PARTS
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD)
left join SECTIONS on (SECTIONS.SECCD=PARTS.SECCD) ;
Anybody have a clue?
Here's the query (simplified version)
I think by simplifying the query you removed the real cause of the bug :-)
What oracle version are you using? Oracle 10g ( 10.2.0.1.0 ) gives:
create table parts (ptno number , ptnm number , catcd number);
create table CATEGORIES (catcd number);
select PTNO,PTNM,CATCD from PARTS
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD);
I get ORA-00918: column ambiguously defined
Interesting in SQL server that throws an error (as it should)
select id
from sysobjects s
left join syscolumns c on s.id = c.id
Server: Msg 209, Level 16, State 1, Line 1
Ambiguous column name 'id'.
select id
from sysobjects
left join syscolumns on sysobjects.id = syscolumns.id
Server: Msg 209, Level 16, State 1, Line 1
Ambiguous column name 'id'.
From my experience if you create a query like this the data result will pull CATCD from the right side of the join not the left when there is a field overlap like this.
So since this join will have all records from PARTS with only some pull through from CATEGORIES you will have NULL in the CATCD field any time there is no data on the right side.
By explicitly defining the column as from PARTS (ie left side) you will get a non null value assuming that the field has data in PARTS.
Remember that with LEFT JOIN you are only guarantied data in fields from the left table, there may well be empty columns to the right.
This may be a bug in the Oracle optimizer. I can reproduce the same behavior on the query with 3 tables. Intuitively it does seem that it should produce an error. If I rewrite it in either of the following ways, it does generate an error:
(1) Using old-style outer join
select ptno, catcd
from parts, categories, sections
where categories.catcd (+) = parts.catcd
and sections.seccd (+) = parts.seccd
(2) Explicitly isolating the two joins
select ptno, catcd
from (
select ptno, seccd, catcd
from parts
left join categories on (categories.CATCD=parts.CATCD)
)
left join sections on (sections.SECCD=parts.SECCD)
I used DBMS_XPLAN to get details on the execution of the query, which did show something interesting. The plan is basically to outer join PARTS and CATEGORIES, project that result set, then outer join it to SECTIONS. The interesting part is that in the projection of the first outer join, it is only including PTNO and SECCD -- it is NOT including the CATCD from either of the first two tables. Therefore the final result is getting CATCD from the third table.
But I don't know whether this is a cause or an effect.
I'm afraid I can't tell you why you're not getting an exception, but I can postulate as to why it chose CATEGORIES' version of the column over PARTS' version.
As far as I understood, in the case of left joins, the "main" table in the query (PARTS) has precedence in ambiguity
It's not clear whether by "main" you mean simply the left table in a left join, or the "driving" table, as you see the query conceptually... But in either case, what you see as the "main" table in the query as you've written it will not necessarily be the "main" table in the actual execution of that query.
My guess is that Oracle is simply using the column from the first table it hits in executing the query. And since most individual operations in SQL do not require one table to be hit before the other, the DBMS will decide at parse time which is the most efficient one to scan first. Try getting an execution plan for the query. I suspect it may reveal that it's hitting CATEGORIES first and then PARTS.
I am using Oracle 9.2.0.8.0. and it does give the error "ORA-00918: column ambiguously defined".
This is a known bug with some Oracle versions when using ANSI-style joins. The correct behavior would be to get an ORA-00918 error.
It's always best to specify your table names anyway; that way your queries don't break when you happen to add a new column with a name that is also used in another table.
It is generally advised to be specific and fully qualify all column names anyway, as it saves the optimizer a little work. Certainly in SQL Server.
From what I can gleen from the Oracle docs, it seems it will only throw if you select the column name twice in the select list, or once in the select list and then again elsewhere like an order by clause.
Perhaps you have uncovered an 'undocumented feature' :)
Like HollyStyles, I cannot find anything in the Oracle docs which can explain what you are seeing.
PostgreSQL, DB2, MySQL and MSSQL all refuse to run the first query, as it's ambiguous.
#Pat: I get the same error here for your query. My query is just a little bit more complicated than what I originally posted. I'm working on a reproducible simple example now.
A bigger question you should be asking yourself is - why do I have a category code in the parts table that doesn't exist in the categories table?
This is a bug in Oracle 9i. If you join more than 2 tables using ANSI notation, it will not detect ambiguities in column names, and can return the wrong column if an alias isn't used.
As has been mentioned already, it is fixed in 10g, so if an alias isn't used, an error will be returned.