Oracle SQL Language Reference Merge/Subquery Documentation Error? - sql

When using the merge::= MERGE INTO ... USING subquery t_alias it appears one must enclose the subquery in parenthesis like MERGE INTO ... USING (subquery) t_alias. However, the Oracle SQL Language Reference documentation's syntax diagram for subquery::= appears to show that an optional path (via query_block) does not require parenthesis. That is the documentation appears to allow both versions.
For example:
This following is invalid, but allowable per the documentation.
MERGE INTO tblA A
USING SELECT col FROM tblB B -- ORA-00903 invalid table name and "SELECT" is highlighted.
ON (A.id = B.id)
...
The following is valid (and also allowable per the documentation).
MERGE INTO tblA A
USING (SELECT col FROM tblB) B
ON (A.id = B.id)
...
According to How to Read Syntax Diagrams
If the syntax diagram has more than one path, then you can choose any path.
Is there an Oracle SQL Language Reference documentation error?
Thanks in advance.

In MERGE query, the operand used after USING clause is not exactly a sub query. It actually defines a table. For example: if you want to update a table from the values from another table.
MERGE INTO tblA A
USING tblB B
ON (A.id = B.id)
...
In case tblB has very huge amount of data and you want to optimise the
perfomance, you can use a query to fetch required columns from the table.
MERGE INTO tblA A
USING (SELECT col1, col2, col3 FROM tblB) B -- here we are not using complete table,
instead we are using only required columns from the table
ON (A.id = B.id)
...
Merge is also similar to join, below is the better way of understanding
select * from tblA A
inner join tblB B
ON (A.id = B.id);
or
select * from tblA A
inner join (SELECT col1, col2, col3 FROM tblB) B
ON (A.id = B.id);
The reason you get invalid table name is, when you do not enclose the select statement in brackets, Oracle will not know the aliases of the table you are using. So always enclose a bracket when you have a certain conditions in the query.

Related

error after migration from oracle 11g to 19c

could anyone explain how the below select works:
select A.B.Column1 Col1
FROM TableA A LEFT JOIN TableB B
ON B.id=A.id;
What does A.B.Column1 mean? After migration from oracle 11g to 19c my package throws an error:
PL/SQL: ORA-00904: "A"."B"."Col1":
invalid identifier
It shouldn't have worked in 11g either, but ANSI syntax sometimes has issues - particularly in older versions - and you happened to hit a confusing, but fairly benign one.
In this case the parser/optimiser is apparently ignoring what could be, but isn't, a reference to the table's schema. That seems to be happening during the internal translation of the ANSI syntax to native Oracle syntax. It doesn't matter what that first part is; you've shown A matching another table's alias, but that's a coincidence; both of these work, at least in 11.2.0.2, even though Z is never mentioned anywhere else:
select A.B.Column1 Col1
FROM TableA A LEFT JOIN TableB B
ON B.id=A.id;
select Z.B.Column1 Col1
FROM TableA A LEFT JOIN TableB B
ON B.id=A.id;
I've said it's an ANSI issue because (a) it errors with an inner join as you mentioned, and (b) it also errors with old-fashioned outer-join syntax:
select A.B.Column1 Col1
FROM TableA A, TableB B
WHERE B.id = A.id (+);
ORA-00904: "A"."B"."COLUMN1": invalid identifier
SQL Fiddle demo
There isn't much point worrying about how it worked before, or (for example) raising a service request against such an old version. It's a bug in your old version of Oracle, and it's fixed now. It shouldn't have worked, and since it - correctly - doesn't work in 19c, you have to fix it, by removing the stray A..
The SQL is invalid.
Knowing this there are only 2 possibilities:
11g likewise raised an error; or
11g has a "more relaxed" error tolerance than 19c and has made an assumption as to whether A or B and applied the selection in your output.
Either way, given that it is your package, the solution should be fairly trivial:
If you know which table you are expecting the result to be from then remove the incorrect table's reference from the alias
if A has "Column1" and B doesn't then remove "B." from the alias
if B has "Column1" and A doesn't then remove "A." from the alias
If both A and B have "Column1" then find an 11g database an create a
dummy query where A.id = B.id and A.Column1 differs in value from
B.Column1. Then run the query and if the result is A.Column1 value
then remove "B." from the alias but if the result is B.Column1,
remove "A." from the alias.
if you are trying to implement the logic "if B.Column1 is null then return the value of A.Column1 otherwise return the value of B.Column1", then correct your SQL to read something like:
select case
when B.Column1 is null then
A.Column1
else
B.Column1
end as Col1
from TableA A left join TableB B
on B.id = A.id;

Update using Inner Join throwing syntax error

I would like to update columns in Table A based on values in Table B. Using below format, but getting syntax error.
update TableA
set
TableA.MOdule_id =TableB.MOdule_id
from TableA
inner join
TableB
on TableA.end_Slot_id =TableB.Slot_Id
where TableA.Slot_Id = 'AAA';
It would be great help, if anyone can help on this.
A quick search for "informix sql update" returns two reference manual pages that describe the syntax for the UPDATE command. Neither one indicates support for the nonstandard FROM clause.
Standard SQL uses a correlated subquery for the purpose. Your query should look something like
update TableA
set MOdule_id =
(select TableB.MOdule_id
from TableB
on TableA.end_Slot_id = Slot_Id)
where Slot_Id = 'AAA'
and exists (
select 1
from TableB
on TableA.end_Slot_id = Slot_Id
and TableA.Slot_Id = 'AAA'
);
The EXISTS clause ensures that only rows that exist in B are applied to A. Without it, any missing rows would be updated to NULL.

SQL JOIN that uses OR in the ON statement

I’m running a SQL query on Google BigQuery and want to do this kind of SQL command:
SELECT ... FROM A JOIN B
ON A.col1=B.col1 AND (A.col2=B.col2 OR A.col3=B.col3)
This fails though with the error:
Error: ON clause must be AND of = comparisons of one field name from each table, with all field names prefixed with table name.
Is there a way to rewrite the SQL to get this kind of functionality?
Turns out this works, which is equivalent to a UNION ALL statement in Google BigQuery. Not sure how to do it if you just want a UNION, since DISTINCT is actually not supported in BigQuery. Luckily it's enough for me as is.
SELECT ... FROM
(SELECT ... FROM A JOIN B ON A.col1=B.col1 AND A.col2=B.col2),
(SELECT ... FROM A JOIN B ON A.col1=B.col1 AND A.col3=B.col3)
This should work:
SELECT ... FROM A CROSS JOIN B
WHERE A.col1=B.col1 AND (A.col2=B.col2 OR A.col3=B.col3)

Few doubts on writing SQL queries

Few questions i have here
I always see some SQL written like below (not sure if i get it right)
SELECT a.column_1, a.column_2 FROM table_name WHERE b.column_a = 'some value'
i don't quite understand the SQL written in such way. Is it similar to using object in programming, where you can define an object and variables within the object? If it is, where is the definition of a and b for the SQL above (assuming i got the query right)?
I want to make comparisons between 3 columns (say C1 C2 C3) in 3 different tables, say T1 T2 and T3. The condition is to get the values from the C1 in T1, that exists in C2 in T2, but not exists in C3 in T3. Both columns are practically the same, just that some might different or lesser records than the other columns in the other 2 tables, and i want to know what the differences are. Is the query below the right way to do it?
select distinct C1 from T1
and (C1) not in (select C2 from T2)
and (C1) in (select C3 from T3)
order by C1;
And is it possible to extend the condition if i want to include more tables into comparison using the query above?
If i were to customize the query above into something similar to the first question, is the query below the right way to do it?
select a.C1 from T1 a
and (a.C1) not in (select b.C2 from T2 b)
and (a.C1) in (select c.C3 from T3 c)
order by a.C1;
What are the advantanges of writing query in object way (like above), compared to writing it in traditional way? I feel like even if you define a table name as a variable, the variable only can be used within the query where it is defined, and cannot be extended to the other queries.
Thanks
the first point is a and b are "table aliases" (shortcut reference to the table(s) involved in THAT query) e.g.
SELECT a.column_1, a.column_2
FROM table_name_a a ------------------------------- table alias a defined here
INNER JOIN table_name_b b -------------------------- table alias b defined here
ON a.id = b.id
WHERE b.column_a = 'some value'
Your second query has a syntax issue: You need WHERE as shown in uppercase. It also has and performance implications. Distinct adds effort to a query, using IN() is really a syntax shortcut for a series of ORs (it might not scale well). But with the syntax it is valid.
select distinct C1
from T1
WHERE (C1) not in (select C2 from T2)
and (C1) in (select C3 from T3)
order by C1;
Yes (with performance reservations) you could add more tables into that comparison.
You introduce table aliases, done correctly, into your third query - but there is no real advantage in that query structure. Aside from just making code more convenient, aliases serve to distinguish between items that would be ambiguous. In my first query above ON a.id = b.id shows possible ambiguity in that 2 tables both have a field of the same name. Prefixing the field name by a table or table alias solves that ambiguity.
For your first point.
I always see some SQL written like below (not sure if i get it right)
SELECT a.column_1, a.column_2 FROM table_name WHERE b.column_a = 'some value'
This query is wrong. It should be like this -
SELECT a.column_1, a.column_2
FROM table_name a INNER JOIN --(There might be another join also like left join etc..)
table_name b
ON a.id = b.id WHERE b.column_a = 'some value'
so you noted in the above query that a and b are just table alias. Well, there are some cases you must use them, like when you need to join to the same table twice in one query.
For the second point. you can also do it like this
SELECT DISTINCT C1 FROM T1 t1
WHERE NOT EXISTS (
SELECT C2 FROM T2 t2 where t2.C2 = t1.C1)
AND WHERE EXISTS (
SELECT C3 FROM T3 t3 where t3.C3 = t1.C1)
ORDER BY C1;
Personally I prefer aliases, and unless I have a lot of tables they tend to be single letter ones.
I'm not 100% sure on this one so don't quote me, but ill give it my best shot.
I think that when alias' are used it is because if you don't use them your statement can he huge and hard to understand, here is a comparison with two SQL query's, one using alias' and the other without:
SELECT o.OrderID, o.OrderDate, c.CustomerName
FROM Customers AS c, Orders AS o
WHERE c.CustomerName="Around the Horn" AND c.CustomerID=o.CustomerID;
Without:
SELECT Orders.OrderID, Orders.OrderDate, Customers.CustomerName
FROM Customers, Orders
WHERE Customers.CustomerName="Around the Horn" AND Customers.CustomerID=Orders.CustomerID;
As you can see one looks much easier to understand and read than the other and makes your query's much smaller.
Aliases can be useful when:
There are more than one table involved in a query
Functions are used in the query
Column names are big or not very readable
Two or more columns are combined together
http://www.w3schools.com/sql/sql_alias.asp
You have to define the table aliases in the FROM clause, so:
SELECT a.column_1, b.column_1
FROM table1 a, table2 b
WHERE a.id = b.id
It should be possible to make your comparisons in the way you have written your query above, however it is also possible to make aliasing easier by using subqueries in the FROM clause ie:
SELECT tab1.id, tab2.id, tab3.id
FROM table1 as tab1,
(select * from table2)as tab2,
(select * from table3)as tab3
This way you can choose any columns from any of the tables using tab1.xxx etc. and then use the WHERE clause to say NOT IN tab2.column_1 etc...

why is postgresql considering this subquery column-name not fully qualified and ambiguous

Select distinct A.col1, B.col2, col3
from A inner join B on A.id = B.id
and B.id in
(select distinct col2 from B where ..... )
PostgreSQL's plpgsql parser does not like the unqualified "col2" in the parenthetic subquery. Since there's only one table mentioned in the subquery, isn't the parser manufacturing an ambiguity where there is none?
An SQL query like you display would just work.
Since you mention plpgsql, my shot in the dark is that you are only showing fragments from what is actually a plpgsql function and you run into naming conflicts with function parameters, which are visible anywhere in the function body (except dynamic SQL).
No. Outer query columns are available in the subquery too, otherwise you couldn't bind the inner and outer queries together. It's an easy thing to forget and can catch you out if you're not expecting it.