Oracle missing FROM clause - sql

Is it possible to create a CTE without a FROM, and if not isn't that the whole point of a CTE in the first place?
WITH cte AS
(
SELECT 1 AS col1, 2 AS col2
)
SELECT col1, col2 FROM cte;
> ORA-00923: FROM keyword not found where expected
It seems a quick-fix for this is just adding FROM DUAL whenever needed. Is that what's supposed to be done?

Yes, that's exactly what dual is supposed to be used for.
Selecting from the DUAL table is useful for computing a constant
expression with the SELECT statement. Because DUAL has only one row,
the constant is returned only once.
https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries009.htm

Related

Counter-intuitive behavior of SUM( ) of UNION in Sqlite3

I ran this code in an sqlite3 terminal (version 3.29.0), and the result is a little strange:
sqlite> select sum((select 4 union all select 2 ));
4
sqlite> select sum((select 4 union select 2 ));
2
I understand why union reverses the table, but why does sum choose the first element?
Meanwhile, this code works just as expected:
sqlite> select sum(x) from (select 4 as x union all select 2 as x);
6
Is this the intended behavior, or is this a bug in sqlite? And if it's intended, what's the logic (and semantics) behind it?
That's expected behavior.
From the documentation (emphasis added)
A SELECT statement enclosed in parentheses is a subquery. All types of SELECT statement, including aggregate and compound SELECT queries (queries with keywords like UNION or EXCEPT) are allowed as scalar subqueries. The value of a subquery expression is the first row of the result from the enclosed SELECT statement. The value of a subquery expression is NULL if the enclosed SELECT statement returns no rows.
The UNION example just happened to end up returning.rows in a different order than the UNION ALL one. Without an ORDER BY neither one is guaranteed to use a particular order, though.

TABLE/CAST/MULTISET vs subquery in FROM clause

The following query doesn't work. It is expected to fail since temp.col references something that is unavailable in that context.
with temp as (
select 'A' col from dual
union all
select 'B' col from dual
)
select *
from temp,
(select level || temp.col from dual connect by level < 3);
The error message from Oracle is : ORA-00904: "TEMP"."COL": invalid identifier
But why is the next query working ? I see CAST/MULTISET as a way to go from a SQL table to a collection type and TABLE to go back to a SQL table. Why do we use such round-trip ? I guess to make the query work, but how ?
with temp as (
select 'A' col from dual
union all
select 'B' col from dual
)
select *
from temp,
table(
cast(
multiset(
select level || temp.col from dual connect by level < 3
) as sys.odcivarchar2list
)
) t;
The result is :
COL COLUMN_VALUE
--- ------------
A 1A
A 2A
B 1B
B 2B
Look how the second column is named COLUMN_VALUE. Looks like a generated name by one of the construct CAST/MULTISET or TABLE.
EDIT
With the accepted answer below, I checked the documentation and found that the TABLE mechanism is a table collection expression. The expression between rounded brackets is the collection expression. The documentations defines a mechanism called left correlation :
The collection_expression can reference columns of tables defined to
its left in the FROM clause. This is called left correlation. Left
correlation can occur only in table_collection_expression. Other
subqueries cannot contains references to columns defined outside the
subquery.
So this is like LATERAL in 12c.
Oracle allows lateral inline views to reference other tables inside the inline view.
In old versions this feature was mostly used for optimizations, as discussed in the Oracle optimizer blog here. Explicit lateral joins were added in 12c. Your first query only needs a small change to work in 12c:
with temp as (
select 'A' col from dual
union all
select 'B' col from dual
)
select *
from temp,
lateral(select level || temp.col from dual connect by level < 3);
Apparently Oracle also silently uses lateral joins for collection unnesting. There are a few cases where SQL uses a logical cross join, but the tables are obviously closely related; such as XMLTable, JSON_table, and queries like your second example. In those cases it makes sense to execute the two tables together. I assume the lateral mechanism is used there, although neither the execution plan nor the 10053 optimizer trace uses the word "lateral". The documentation even has an example very similar to yours in the Collection Unnesting: Examples. However, this "feature" is still not well documented.
On a side note, in general you should avoid SQL features that increase the context. Features like lateral joins, common table expressions, and correlated subqueries can be useful, but they can also make SQL statements more difficult to understand. A regular inline view can be run and understood all by itself and has a very simple interface - its projected columns. That simplicity makes it easier to assemble small components into a large statement.
I suggest you re-write your query like below. Treat each inline view like you would a function or procedure - give them good names and comments. It will help you later when you assemble them into large, realistic statements.
select col, the_level||col
from
(
--Good comment 1.
select 'A' col from dual union all
select 'B' col from dual
) good_name_1
cross join
(
--Good comment 2.
select level the_level
from dual
connect by level < 3
) good_name_2

Oracle Order by not working for Subquery from DUAL

Hi all when I executed this query somehow its throwing the following error -
ORA-00907: missing right parenthesis.
But if you remove the order by 1 from SELECT 2 FROM DUAL order by 1 its working.
Did I miss something out here or its ORACLE limitation
SELECT (CASE
WHEN EXISTS
(SELECT 1 FROM DUAL) THEN
(SELECT 4
FROM dual)
ELSE
(SELECT 2 FROM DUAL order by 1 )
END) AS DELEGATOR FROM dual
Below is a working code with order by 1 removed
SELECT (CASE
WHEN EXISTS
(SELECT 1 FROM DUAL) THEN
(SELECT 4
FROM dual)
ELSE
(SELECT 2 FROM DUAL )
END) AS DELEGATOR FROM dual
Somehow I already give up but when I change the code to this
it somehow works. I applied to my actual query and the result
are the expected outcome.
SELECT (CASE
WHEN EXISTS
(SELECT 1 FROM DUAL) THEN
(SELECT 4
FROM dual)
ELSE
(select * from (SELECT 2 FROM DUAL order by 1 )
where rownum = 1)
END) AS DELEGATOR FROM dual
SELECT 2 FROM DUAL order by 1
Firstly, an ORDER BY makes no sense here the query returns just one row.
Secondly, you cannot use an order by in a Scalar Subquery Expression when there is no sense of ordering the rows. Order by would be allowed in certain cases where the outer query expects an ordered set of rows. In your case, there is no question of getting more than a single-row.
Thirdly, it is not a good practice coding as order by 1,2 etc. Instead, use the appropriate column name. You might not come to know if the columns in the select list changes, your result set would be then ordered differently and you will need to make changes at both the places.
A scalar subquery expression returns exactly one column value from one row. If the expression returned more than one row you would get an error, "ORA-01427: single-row subquery returns more than one row". As it can only have a single value, ordering that value would be meaningless.
The missing-right-parenthesis error doesn't necessarily mean you have unbalanced parentheses, it can indicate other errors that have made the parser give up at the point it expected to see one. Here the parser expected the subquery's closing parenthesis to have come after the FROM DUAL, so it's stopping when it doesn't see one - effectively it doesn't know how to interpret the rest of the statement so it doesn't try (hugely oversimplifying).
An order by clause isn't meaningful in a subquery, and generally isn't allowed; though it is tolerated in some places, such as an inline view, despite still having no effect.
Obviously your example is very contrived, but there is a scenario where you might think you want an order by clause, and that is to get the first value from a result set which you need to be ordered. Based on how limit works in other databases you might try to do something like:
select (
select object_name from user_objects
where rownum = 1
order by created desc
)
from dual
... but that isn't how rownum works, and would also get ORA-00907. If that is what you're doing you would need another layer of subquery (as an inline view now) which can be ordered, and apply the rownum filter to that:
select (
select object_name from (
select object_name from user_objects
order by created desc
)
where rownum = 1
)
from dual
... which is now valid.
(Whether you actually need to be doing that in a subquery, rather than through a join, is another matter - your query is hopefully complicated enough to warrant it).

How is WITH used in Oracle SQL (example code shown )?

I found this example code online when I searched for "how to do an Exclusive Between oracle sql"
Someone was proving that, in Oracle, BETWEEN is by default inclusive.
So they used such code :
with x as (
select 1 col1 from dual
union
select 2 col1 from dual
union
select 3 col1 from dual
UNION
select 4 col1 from dual
)
select *
from x
where col1 between 2 and 3
I've never seen such an example, what is going on with the WITH ?
In short, WITH clause is an inline view, or subquery. It is useful when you will refer to something multiple times, or when you want to abstract parts of a complex query to make it easier to read.
If you are from SQL Server world, you can also think of it like a temporary table.
So:
WITH foo as (select * from tab);
select * from foo;
is like
select * from (select * from tab);
Though it may be more efficient since x is resolved to a single dataset, even if queried multiple times.
It also reduces repetition. If you use a subquery more than once in a statement, you can consider factoring it out using WITH.
It has nothing to do with the BETWEEN example, it is just the author's choice of approach for demonstrating a concept.

tsql UNION between 2 select statements

I have 2 complex sql statements but they both have the same column name.
I am trying to do a union between both but the
UNION
says Incorrect syntex near UNION.
not sure if there is anything else necessary to make it work.
Because I always terminate my SQL statements with a semicolon (), I sometimes see this error e.g.
SELECT c
FROM T1; <-- forgot to remove the terminator!
UNION
SELECT c
FROM T2;
The syntax that I usually use for unions is:
select *
from
(
(<subquery 1>)
union all
(<subquery 2>)
) t
UNION ALL is more efficient than UNION, since it doesn't check for an eliminate duplicates.