Oracle Order by not working for Subquery from DUAL - sql

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).

Related

Oracle missing FROM clause

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

Oracle order by query using select case

in Oracle Live SQL i was trying to use simple order by sql using select (case when) query
i tried to get to same result select * from tt order by 1
replace 1 with (select (case when 1=1 then 1 else 2 end) from dual)
but two result completely different.
i want table ordered by column 1 however the query using select case when query doesn't sort by column 1.
I don't know why and want to know how this query works in oracle db
Compare
...
order by 2
and
...
order by 1+1
At "compile" time the first 2 is an integer constant so it is a position of the column, the db engine sorts by the specified column. The second 1+1 is an integer expression and the db engine sorts by this value '2'. Same, (select (case when 1=1 then 1 else 2 end) from dual) is an expression, not a column specification.
When you specify a number in the ORDER BY clause, Oracle will sort by that column of the resulting select. As an example, ORDER BY 1,2 will sort by the first column, then the second column. If there is no second column, then you will get an error.
In the ORDER BY of the outermost query, there is essentially no sorting happening in your query because 1 is always returned from your subquery. This is sorting by the value 1 and not the first column.
If you explain the logic you are hoping to achieve, then we may be able to assist, but that is what is happening with your existing queries.

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.

SQL Server - Pagination Without Order By Clause

My situation is that a SQL statement which is not predictable, is given to the program and I need to do pagination on top of it. The final SQL statement would be similar to the following one:
SELECT * FROM (*Given SQL Statement*) b
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY;
The problem here is that the *Given SQL Statement* is unpredictable. It may or may not contain order by clause. I am not able to change the query result of this SQL Statement and I need to do pagination on it.
I searched for solution on the Internet, but all of them suggested to use an arbitrary column, like primary key, in order by clause. But it will change the original order.
The short answer is that it can't be done, or at least can't be done properly.
The problem is that SQL Server (or any RDBMS) does not and can not guarantee the order of the records returned from a query without an order by clause.
This means that you can't use paging on such queries.
Further more, if you use an order by clause on a column that appears multiple times in your resultset, the order of the result set is still not guaranteed inside groups of values in said column - quick example:
;WITH cte (a, b)
AS
(
SELECT 1, 'a'
UNION ALL
SELECT 1, 'b'
UNION ALL
SELECT 2, 'a'
UNION ALL
SELECT 2, 'b'
)
SELECT *
FROM cte
ORDER BY a
Both result sets are valid, and you can't know in advance what will you get:
a b
-----
1 b
1 a
2 b
2 a
a b
-----
1 a
1 b
2 a
2 b
(and of course, you might get other sorts)
The problem here is that the *Given SQL Statement" is unpredictable. It may or may not contain order by clause.
your inner query(unpredictable sql statement) should not contain order by,even if it contains,order is not guaranteed.
To get guaranteed order,you have to order by some column.for the results to be deterministic,the ordered column/columns should be unique
Please note: what I'm about to suggest is probably horribly inefficient and should really only be used to help you go back to the project leader and tell them that pagination of an unordered query should not be done. Having said that...
From your comments you say you are able to change the SQL statement before it is executed.
You could write the results of the original query to a temporary table, adding row count field to be used for subsequent pagination ordering.
Therefore any original ordering is preserved and you can now paginate.
But of course the reason for needing pagination in the first place is to avoid sending large amounts of data to the client application. Although this does prevent that, you will still be copying data to a temp table which, depending on the row size and count, could be very slow.
You also have the problem that the page size is coming from the client as part of the SQL statement. Parsing the statement to pick that out could be tricky.
As other notified using anyway without using a sorted query will not be safe, But as you know about it and search about it, I can suggest using a query like this (But not recommended as a good way)
;with cte as (
select *,
row_number() over (order by (select 0)) rn
from (
-- Your query
) t
)
select *
from cte
where rn between (#pageNumber-1)*#pageSize+1 and #pageNumber*#pageSize
[SQL Fiddle Demo]
I finally found a simple way to do it without any order by on a specific column:
declare #start AS INTEGER = 1, #count AS INTEGER = 5;
select * from (SELECT *,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS fakeCounter
FROM (select * from mytable) AS t) AS t2 order by fakeCounter OFFSET #start ROWS
FETCH NEXT #count ROWS ONLY
where select * from mytable can be any query

Combining two SQL SELECT statements on the same table

I would like to combine these two SQL queries:
SELECT * FROM "Contracts" WHERE
"productType" = 'RINsell' AND
"clearTime" IS NULL AND
"holdTime" IS NOT NULL
ORDER BY "generationTime";
and
SELECT * FROM "Contracts" WHERE
"productType" = 'RINsell' AND
"clearTime" IS NULL AND
"holdTime" IS NULL
ORDER BY "contractLimitPrice";
When I run each statement, I get exactly the results I want, I would just like both results sequentially. My first thought was to use UNION ALL since this selections will be disjoint but I found that you can't use a UNION after an ORDER BY. I've searched quite a bit and most people suggest doing the ORDER BY after the UNION but each query has different ORDER BY conditions.
If you want the results of the first query before the results of the second, you can remove holdtime from the where clause, and use an order by like
order by
case when holdTime is not null then 0 else 1 end, --first query comes first
case when holdTime is not null --different orders for queries
then generationTime
else contractLimitPrice
end
... but I found that you can't use a UNION after an ORDER BY.
Well, you didn't look hard enough:
(
SELECT *
FROM "Contracts"
WHERE "productType" = 'RINsell'
AND "clearTime" IS NULL
AND "holdTime" IS NOT NULL
ORDER BY "generationTime"
)
UNION ALL
)
SELECT *
FROM "Contracts"
WHERE "productType" = 'RINsell'
AND "clearTime" IS NULL
AND "holdTime" IS NULL
ORDER BY "contractLimitPrice"
)
Note the parentheses. Per documentation:
(ORDER BY and LIMIT can be attached to a subexpression if it is
enclosed in parentheses. Without parentheses, these clauses will be
taken to apply to the result of the UNION, not to its right-hand input
expression.)
Closely related answer:
Sum results of a few queries and then find top 5 in SQL
Aside: I really would get rid of those CaMeL case identifiers. Your life is much easier with all-lower case legal identifiers in Postgres.