interesting behaviour of order by with union all clause - sql

I encountered just an interesting behaviour of order by clause when query contains union all.
For example I have following query:
select * from dual order by 1
union all
select * from dual
It fails, what the?
Ok it seems that oracle just does not like order by followed by union all. Lets rewrite query to following:
select * from (select * from dual order by 1)
union all
select * from dual
It is fixed!
It will also work if I just swap two queries, so one with order by goes to the end:
select * from dual
union all
select * from dual order by 1
That seems to be inconsistent. So what is the cause of such behaviour? Is it some kind of bug or it is done on purpose?

The statement
select * from (select * from dual order by 1)
has no defined order at all. Only the outermost ORDER BY takes effect in SQL (except if there is a row limit set).
If you still happen to observe order in the query results this is a coincidence that can go away at any time.
In the statement
select * from dual
union all
select * from dual order by 1
The order by is attached to the union all, not the the 2nd select. It is therefore top-level and well-defined.
Use the last form. And put the order by into a new line to make this easier to read.
How can I then sort just single select with union all?
The output order of union all is undefined without order-by clause. Certainly the two inputs are not guaranteed to be concatenated.
select *, 1 as Tag from dual
union all
select *, 2 as Tag from dual
order by Tag, 1 --simulate ordered concatenation of inputs

Related

ORA-00904 with UNION and ORDER BY alias [duplicate]

The following query is perfectly valid in pretty much every database (give or take a dual dummy table), including Oracle:
select 'A' as x from dual union all
select 'B' from dual
order by x asc
Returning:
| X |
|---|
| A |
| B |
Now this query is still quite standard SQL, but doesn't work on Oracle
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
order by x asc
I'm getting
ORA-00904: "X": invalid identifier
This, however, works:
select 'A' as x from dual union all
select 'B' as x from dual union all
select 'C' from dual
order by x asc
I've been playing around with this issue and figured out that apparently, at least the first subselect and the second-last (??) subselect need to have a column called x. In the first example, the two subselects seemed to simply coincide. Working example:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' from dual union all
select 'F' as x from dual union all
select 'G' from dual
order by x asc
As you may have guessed, this wouldn't work:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' as x from dual union all
select 'F' from dual union all
select 'G' from dual
order by x asc
Interesting side-note:
Derived tables seem not to suffer from this limitation. This works:
select * from (
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
)
order by x asc
Question:
Is this a (known?) bug in the Oracle SQL parser, or is there any very subtle detail in the language syntax that absolutely requires the first and the second-last subselect to hold a column of the name as referenced from the ORDER BY clause?
This doesn't really answer the question, but it seems to be a parser bug (or 'feature') rather than a language requirement.
According to My Oracle Support, this seems to have been raised as bug 14196463 but closed with no resolution. It's also mentioned in community thread 3561546. You need a MOS account, or at least an Oracle account, to see either of those though.
It's also been discussed in an OTN thread which requires a basic Oracle login rather than a MOS account, as far as I can tell. That also doesn't have much information but repeats your findings, and also suggests the behaviour has existed back at least to 9.2.0.8 and perhaps much earlier.
The documentation is a bit vague but doesn't indicate this is expected to be a problem:
For compound queries containing set operators UNION, INTERSECT, MINUS, or UNION ALL, the ORDER BY clause must specify positions or aliases rather than explicit expressions. Also, the ORDER BY clause can appear only in the last component query. The ORDER BY clause orders all rows returned by the entire compound query.
You are aliasing your expression and using that, and it doesn't say you have to alias particular components (although of course it doesn't say you don't have to either).
The behaviour seems inconsistent with the alias being valid for the final projection, and the usual rule about the alias only being valid in the order by clause - this seems to be falling down somewhere in between.
This doesn't answer why you are getting inconsistent behavior from your current query, but in Oracle you can easily rewrite as the following (which should never fail with an invalid identifier error):
with t(x) as (
select 'A' from dual
union all
select 'B' from dual
union all
select 'C' from dual
)
select * from t
order by x asc
With the added bonus that you only specify the column alias (x) once.

ORDER BY in UNION does not work [duplicate]

The following query is perfectly valid in pretty much every database (give or take a dual dummy table), including Oracle:
select 'A' as x from dual union all
select 'B' from dual
order by x asc
Returning:
| X |
|---|
| A |
| B |
Now this query is still quite standard SQL, but doesn't work on Oracle
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
order by x asc
I'm getting
ORA-00904: "X": invalid identifier
This, however, works:
select 'A' as x from dual union all
select 'B' as x from dual union all
select 'C' from dual
order by x asc
I've been playing around with this issue and figured out that apparently, at least the first subselect and the second-last (??) subselect need to have a column called x. In the first example, the two subselects seemed to simply coincide. Working example:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' from dual union all
select 'F' as x from dual union all
select 'G' from dual
order by x asc
As you may have guessed, this wouldn't work:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' as x from dual union all
select 'F' from dual union all
select 'G' from dual
order by x asc
Interesting side-note:
Derived tables seem not to suffer from this limitation. This works:
select * from (
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
)
order by x asc
Question:
Is this a (known?) bug in the Oracle SQL parser, or is there any very subtle detail in the language syntax that absolutely requires the first and the second-last subselect to hold a column of the name as referenced from the ORDER BY clause?
This doesn't really answer the question, but it seems to be a parser bug (or 'feature') rather than a language requirement.
According to My Oracle Support, this seems to have been raised as bug 14196463 but closed with no resolution. It's also mentioned in community thread 3561546. You need a MOS account, or at least an Oracle account, to see either of those though.
It's also been discussed in an OTN thread which requires a basic Oracle login rather than a MOS account, as far as I can tell. That also doesn't have much information but repeats your findings, and also suggests the behaviour has existed back at least to 9.2.0.8 and perhaps much earlier.
The documentation is a bit vague but doesn't indicate this is expected to be a problem:
For compound queries containing set operators UNION, INTERSECT, MINUS, or UNION ALL, the ORDER BY clause must specify positions or aliases rather than explicit expressions. Also, the ORDER BY clause can appear only in the last component query. The ORDER BY clause orders all rows returned by the entire compound query.
You are aliasing your expression and using that, and it doesn't say you have to alias particular components (although of course it doesn't say you don't have to either).
The behaviour seems inconsistent with the alias being valid for the final projection, and the usual rule about the alias only being valid in the order by clause - this seems to be falling down somewhere in between.
This doesn't answer why you are getting inconsistent behavior from your current query, but in Oracle you can easily rewrite as the following (which should never fail with an invalid identifier error):
with t(x) as (
select 'A' from dual
union all
select 'B' from dual
union all
select 'C' from dual
)
select * from t
order by x asc
With the added bonus that you only specify the column alias (x) once.

Curious issue with Oracle UNION and ORDER BY

The following query is perfectly valid in pretty much every database (give or take a dual dummy table), including Oracle:
select 'A' as x from dual union all
select 'B' from dual
order by x asc
Returning:
| X |
|---|
| A |
| B |
Now this query is still quite standard SQL, but doesn't work on Oracle
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
order by x asc
I'm getting
ORA-00904: "X": invalid identifier
This, however, works:
select 'A' as x from dual union all
select 'B' as x from dual union all
select 'C' from dual
order by x asc
I've been playing around with this issue and figured out that apparently, at least the first subselect and the second-last (??) subselect need to have a column called x. In the first example, the two subselects seemed to simply coincide. Working example:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' from dual union all
select 'F' as x from dual union all
select 'G' from dual
order by x asc
As you may have guessed, this wouldn't work:
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual union all
select 'E' as x from dual union all
select 'F' from dual union all
select 'G' from dual
order by x asc
Interesting side-note:
Derived tables seem not to suffer from this limitation. This works:
select * from (
select 'A' as x from dual union all
select 'B' from dual union all
select 'C' from dual
)
order by x asc
Question:
Is this a (known?) bug in the Oracle SQL parser, or is there any very subtle detail in the language syntax that absolutely requires the first and the second-last subselect to hold a column of the name as referenced from the ORDER BY clause?
This doesn't really answer the question, but it seems to be a parser bug (or 'feature') rather than a language requirement.
According to My Oracle Support, this seems to have been raised as bug 14196463 but closed with no resolution. It's also mentioned in community thread 3561546. You need a MOS account, or at least an Oracle account, to see either of those though.
It's also been discussed in an OTN thread which requires a basic Oracle login rather than a MOS account, as far as I can tell. That also doesn't have much information but repeats your findings, and also suggests the behaviour has existed back at least to 9.2.0.8 and perhaps much earlier.
The documentation is a bit vague but doesn't indicate this is expected to be a problem:
For compound queries containing set operators UNION, INTERSECT, MINUS, or UNION ALL, the ORDER BY clause must specify positions or aliases rather than explicit expressions. Also, the ORDER BY clause can appear only in the last component query. The ORDER BY clause orders all rows returned by the entire compound query.
You are aliasing your expression and using that, and it doesn't say you have to alias particular components (although of course it doesn't say you don't have to either).
The behaviour seems inconsistent with the alias being valid for the final projection, and the usual rule about the alias only being valid in the order by clause - this seems to be falling down somewhere in between.
This doesn't answer why you are getting inconsistent behavior from your current query, but in Oracle you can easily rewrite as the following (which should never fail with an invalid identifier error):
with t(x) as (
select 'A' from dual
union all
select 'B' from dual
union all
select 'C' from dual
)
select * from t
order by x asc
With the added bonus that you only specify the column alias (x) once.

how to sort by case insensitive alphabetical order using COLLATE NOCASE

I am trying to sort alphabetically case insensitive using COLLATE NOCASE
but getting error
ORA - 00933 SQL command not properly ended.
below is the query I am firing:
SELECT LPN.LPN_ID,
LPN.TC_ORDER_ID,
ORDERS.D_NAME,
ORDERS.PURCHASE_ORDER_NUMBER AS ORDER_PURCHASE_ORDER_NUMBER,
ORDERS.D_NAME AS D_NAME_2, LPN.LPN_NBR_X_OF_Y
FROM ORDERS ORDERS,
LPN LPN
WHERE ORDERS.ORDER_ID=LPN.ORDER_ID
ORDER BY ORDERS.D_NAME COLLATE NOCASE DESC
I checked here to try this but still getting error
How to use SQL Order By statement to sort results case insensitive?
Any suggestions please ?
Oracle does not support COLLATE NOCASE option of the order by clause. To be able to perform case-insensitive ordering you have two options:
Set NLS_COMP='ANSI' and 'NLS_SORT=BINARY_CI', CI suffix means case-insensitive, session or system wide by using alter session or alter system statement:
alter session set nls_comp='ANSI';
alter session set nls_sort='BINARY_CI';
with t1(col) as(
select 'A' from dual union all
select 'a' from dual union all
select 'b' from dual union all
select 'B' from dual
)
select *
from t1
order by col
Result:
COL
---
A
a
b
B
Change case of the character literal by using either upper() or lower() function.
with t1(col) as(
select 'A' from dual union all
select 'a' from dual union all
select 'b' from dual union all
select 'B' from dual
)
select *
from t1
order by upper(col)
result:
COL
---
A
a
b
B
Edit
but i need the UpperCase to preceed any LowerCase eg. Alan, alan, Brian, brian, Cris
This is not the case-insensitive ordering, rather quite contrary in some sense. As one of the options you could do the following to produce desired result:
with t1(col) as(
select 'alan' from dual union all
select 'Alan' from dual union all
select 'brian' from dual union all
select 'Brian' from dual union all
select 'Cris' from dual
)
select col
from ( select col
, case
when row_number() over(partition by lower(col)
order by col) = 1
then 1
else 0
end as rn_grp
from t1
)
order by sum(rn_grp) over(order by lower(col))
Result:
COL
-----
Alan
alan
Brian
brian
Cris
COLLATE NOCASE does not work with Oracle, Try this:
SELECT LPN.LPN_ID,
LPN.TC_ORDER_ID,
ORDERS.D_NAME,
ORDERS.PURCHASE_ORDER_NUMBER AS ORDER_PURCHASE_ORDER_NUMBER,
ORDERS.D_NAME AS D_NAME_2,
LPN.LPN_NBR_X_OF_Y
FROM orders orders,
lpn lpn
where orders.order_id=lpn.order_id
ORDER BY lower(orders.d_name) DESC;
Since 10g there is a function NLSSORT which does pretty much what Nicholas Krasnov described but doesn't require altering the system or session.
so you can try something like this:
SELECT LPN.LPN_ID, LPN.TC_ORDER_ID, ORDERS.D_NAME, ORDERS.PURCHASE_ORDER_NUMBER
AS ORDER_PURCHASE_ORDER_NUMBER, ORDERS.D_NAME AS D_NAME_2, LPN.LPN_NBR_X_OF_Y
FROM ORDERS ORDERS, LPN LPN
WHERE ORDERS.ORDER_ID=LPN.ORDER_ID
ORDER BY nlssort(ORDERS.D_NAME, 'NLS_SORT = binary_ci') desc
Note you can't use this directly in a UNION or you'll get the following error:
ORA-01785: ORDER BY item must be the number of a SELECT-list
expression.
Instead, you need to wrap it:
SELECT * FROM (SELECT a, b FROM x, y UNION SELECT c, d FROM m, n)
ORDER BY nlssort(a, 'nls_sort=binary_ci') DESC

What's wrong with my UNION SELECT

SELECT *
FROM
(SELECT Campus_ID AS A_Campus, * FROM A_Campuses
UNION
SELECT Campus_ID AS H_Campus, * FROM H_Campuses
UNION
SELECT Campus_ID AS B_Campus, * FROM B_Campuses)
ORDER BY Campus_Name ASC
It gives the sql error
#1064 - You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server
version for the right syntax to use
near '* FROM A_Campuses UNION
SELECT Campus_ID AS H_Campus, * FROM
H_Campuses' at line 3
Step 1
You have two syntax errors
not aliasing the derived table.
* cannot be used AFTER a column name, unless you alias the source table
SELECT *
FROM
(SELECT Campus_ID AS A_Campus, A.* FROM A_Campuses A
UNION
SELECT Campus_ID AS H_Campus, H.* FROM H_Campuses H
UNION
SELECT Campus_ID AS B_Campus, B.* FROM B_Campuses B) AS X
ORDER BY Campus_Name ASC
Step 2
But as Phil points out, since you are sub-querying only to do an order by, there is no need to subquery at all. ORDER BY applies to the entire UNION-ed result.
SELECT Campus_ID AS A_Campus, A.* FROM A_Campuses A
UNION
SELECT Campus_ID AS H_Campus, H.* FROM H_Campuses H
UNION
SELECT Campus_ID AS B_Campus, B.* FROM B_Campuses B
ORDER BY Campus_Name ASC
Step 3
The next thing to point out is that A_, H_ and B_ must ALL have compatible structures for the UNION to align properly. It is also worth mentioning that aliasing Campus_ID as different column names has no value. The column names of the resultant result of a UNION is the FIRST name encountered across the UNION parts - in this case all the column names will come from A_Campuses, as well as the additional column A_Campus. In actual fact, you will have two columns A_Campus and Campus_ID which will always hold EXACTLY the same values. What you probably wanted was to indicate the SOURCE of the data: (notice that I have not even bothered to alias the columns for the 2nd and 3rd parts of the UNION)
SELECT 'A' AS Source, A.* FROM A_Campuses A
UNION ALL
SELECT 'H', H.* FROM H_Campuses H
UNION ALL
SELECT 'B', B.* FROM B_Campuses B
ORDER BY Campus_Name ASC
Note
For performance reasons, use UNION ALL instead of UNION, which performs a DISTINCT against the final result. If you had duplicate Campus_ID across different tables, as well as exactly the same record data, UNION results in one of them being removed, whereas UNION ALL keeps both (or all 3) copies. Given the addition of the Source column, this is not a possibility, so using UNION ALL will result in a faster query.
The columns in each branch of the UNION normally need the same name, or will end up with a single name. Also, a sub-select needs an alias (the 'AS C' below), at least in standard SQL; even if you don't mention the alias anywhere else in the query, as below.
I think what you're after is likely:
SELECT *
FROM (SELECT "A" AS Campus_ID, * FROM A_Campuses
UNION
SELECT "H" AS Campus_ID, * FROM H_Campuses
UNION
SELECT "B" AS Campus_ID, * FROM B_Campuses) AS C
ORDER BY Campus_Name ASC
That isn't how you write a union query.
Any ORDER BY clause applies to the union so you don't need to sub-query it. Also, you should aim to have the same column names and aliases across all parts of the union. Your A_Campus, H_Campus and B_Campus aliases will be lost (not sure which one will win out). For example
SELECT Campus_ID, Campus_Name FROM A_Campuses
UNION
SELECT Campus_ID, Campus_Name FROM H_Campuses
UNION
SELECT Campus_ID, Campus_Name FROM B_Campuses
ORDER BY Campus_Name ASC
I'd also refrain from using SELECT * in a union as you need to be specific about what you're selecting.
perhaps more parenthesis are needed
SELECT *
FROM
(
(SELECT Campus_ID AS A_Campus, * FROM A_Campuses)
UNION
(SELECT Campus_ID AS H_Campus, * FROM H_Campuses)
UNION
(SELECT Campus_ID AS B_Campus, * FROM B_Campuses)
)
ORDER BY Campus_Name ASC
Also can you UNION with different column names like that? You might need a fake A_Campus and B_Campus in the H_Campus subquery to get identical columns.