Should order-by-clause be able to deduce implicit column names? - oracle

Question:
Can order by clause obtain column names when we specify asterisk * instead of column names in a select query?
The following code works, so the answer seems to be "yes":
select * from dual
order by dummy
The following code does not work, so the answer seems to be "no":
select * from dual
union all
select * from dual
order by dummy
Is this behaviour documented?

In the SQL Reference Manual, under "Sorting Query Results":
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.
So in your case
select * from dual
union all
select * from dual
order by 1
works as expected, as does
select dummy from dual
union all
select dummy from dual
order by dummy

Related

Using a CTE table as part of `START WITH` clause in recursive query

I don't seem to be able to use a CTE table as part of the below recursive query?
I have two CTE tables. One that gets a bunch of grouper codes and another that explodes those grouper codes out into the "child codes" (exploded_codes) that I want to use in the below query. When I add the EXPLODED_CODES CTE table into the IN() clause of my main query (shown below), no data is returned (no error though). However, if I hard code the values that are returned from a simple select statement on the EXPLODED_CODES CTE table, into the IN() clause, the query returns as expected! Any clue why this may be?
Here is my recursive query. Is there any reason anyone can think of that using the CTE table wouldnt work, but hard coding the values that the CTE table contains would? I know it has something to do with the recursion logic because when I change starts with to where and comment out the connect by clause, it returns some data (although its not the full parent-child hierarchy that the recursive query returns using the hard coded values.
select *
from events
start with
events.event_cd in (
select event_cd from exploded_codes --This code returns no data
--22600750,135148330,107919568 --These are the values in the CTE and returns correct data
)
connect by nocycle ((prior parent_event_id = event_id and prior event_id <> event_id)
or (prior event_id = parent_event_id) )
This is essentially all I am trying to do. However, it works fine in my mock ups like this:
with mytable as (
select 1 as pk1, 123 as event_id, 123 as parent_event_id, 777 as event_cd from dual
union select 2, 456, 123, 777 as event_cd from dual
union select 3, 756, 423, 999 as event_cd from dual
)
--select * from mytable
, codes as (
select 777 as event_cd from dual union select 88 as event_cd from dual union
select 111 as event_cd from dual
)
select myTable.*--, level, SYS_CONNECT_BY_PATH(event_id, '/') as path
from
myTable
start with event_cd in (select event_cd from codes)
connect by nocycle (prior parent_event_id = event_id and prior event_id <> event_id)
or (prior event_id = parent_event_id)
However, when I query my actual database table, it returns no rows depending on how I populate the CTE table (event though my table ends up with the same values each way.
What I cant seem to understand is that when I uncomment the below line from my CODES table, is when my recursive query doesnt return anything. However, when I hard code the values in like I am below, my recursive query returns fine. I have tried casting the datatype to NUMBER (which is how event_cd is stored in the EVENT table) and that has no affect.
with codes as (
--This method of populating the table causes my recursive query to not return anything
--select xcode from xr_template_codes where xtype = 'event-set' and SECTION_DISPLAY = 'Coding Summary'
--So does this method (which is the equivalent of the above).
--select xcode from xr_template_codes where xcode = 107919402
--union
--select xcode from xr_template_codes where xcode = 101320306
--This method seems to work fine. These are just the two values returned from the XR_TEMPLATES_CODES table.
select 101320306 as event_cd from dual union select 107919402 as event_cd from dual
)
select *
from
event
start with
event.event_cd in (select to_number(event_cd) from codes)
and event.id = 15169295
connect by nocycle (prior parent_event_id = event_id and prior event_id <> event_id)
or (prior event_id = parent_event_id)

BULK COLLECT INTO a UNION query into a table of objects

How can I collect into a table of objects, the values produced by a query that has a union in it as shown below
Select customer_name
from customer
where customer_id = 'xxx'
BULK COLLECT INTO customer_obj
UNION
Select customer_name
from customer
where customer_name like '%adam%'
the constraints above are completely made up.
The bulk collect clause comes right after the (first) select clause, before the (first) from clause. You have it in the wrong place.
It is not clear why you are using a union (although that by itself will not result in an error). Perhaps as an unintended consequence, you will get a list of distinct names, because that is what union does (as opposed to union all).
Other than that, as has been pointed out in a Comment already, you don't need union - you need an or in the where clause. But even if you modify your query that way, you still must move bulk collect to its proper place.
Another option would be to put your UNION into an inline view. For example,
SELECT cust.customer_name
BULK COLLECT
INTO customer_obj
FROM (
SELECT customer_name
FROM customer
WHERE customer_id = 'xxx'
UNION
SELECT customer_name
FROM customer
WHERE customer_name LIKE '%adam%'
) cust

How to order by case insensitive ASC or DESC, with DISTINCT and UNION

How to order by case insensitive ASC or DESC for P/L sql 11g. this p/l sql basic question but i can't find good answer in Google please tell how to sort the select result case insensitive
this what i tried
SELECT DISTINCT
asssss,
saas_acc
FROM DUAL
UNION SELECT '--ALL--','ALL' FROM DUAL
ORDER BY upper(asssss) ASC ;
that gave to me ORA-01785: ORDER BY item must be the number of a SELECT-list expression
The simplest option would be to sort by the upper- (or lower-) case column data
ORDER BY UPPER( column_name )
DISTINCT actually filtered the UNIQUE content in the result set, with whatever expressions given in the SELECT clause.
We cannot order it using a Different expression or column name. Please see the example here.
SQL> l
1 SELECT DISTINCT (col1),(col2)
2 FROM
3 ( SELECT 'Hello' col1,'World' col2 FROM DUAL
4 UNION ALL
5 SELECT 'HELLO','WORLD' FROM DUAL
6* )
SQL> /
COL1 COL2
----- -----
HELLO WORLD
Hello World
You can see that DISTINCT is CASE SENSITIVE here.(2 rows displayed)
So, let me Do a UPPER() on both columns.
SQL> l
1 SELECT DISTINCT UPPER (col1),UPPER(col2)
2 FROM
3 ( SELECT 'Hello' col1,'World' col2 FROM DUAL
4 UNION ALL
5 SELECT 'HELLO','WORLD' FROM DUAL
6* )
SQL> /
UPPER UPPER
----- -----
HELLO WORLD
Just 1 row is Displayed, ignoring the case.
Coming back to the actual problem. To order something on a DISTINCT Resultset, it has to be a part of DISTINCT clause's expression/column.
So, When you issue DISTINCT COL1,COl2, the order by may be by COL1 or COL2/.. it cannot be COL3 or even UPPER(COL1) because UPPER() makes a different expression conflicting the expression over DISTINCT.
Finally, Answer for your Question would be
if you want your ORDER to be case-insensitive, DISTINCT also has to the same way! As given below
SELECT DISTINCT
UPPER(asssss),
saas_acc
FROM DUAL
ORDER BY upper(asssss) ASC ;
OR if UNION has to be used, better do this, or same as above one.
SELECT * FROM
(
SELECT DISTINCT asssss as asssss,
saas_acc
FROM DUAL
UNION
SELECT '--ALL--','ALL' FROM DUAL
)
ORDER BY upper(asssss) ASC ;
Out of my own Experience, I had always felt, what ever expression/column is specified in the ORDER BY, it is implicitly taken to final SELECT as well. Ordering is just based on the column number(position) in the result actually . In this situation, DISTINCT COL1,COl2 is already there. When you give ORDER BY UPPER(COL1), it will be tried to append into the SELECT expression, which is NOT possible at all. So, Semantic check itself, would disqualify this query with an Error!
To sort case insensitive you need to set the NLS_COMP to ANSI
NLS_COMP=ANSI
Details: http://www.orafaq.com/node/999
You can use upper or lower functions.
order by upper(columnName)
Update1
Try removing order-by clause from your query which will give you correct error, which is ORA-00904: "SAAS_ACC": invalid identifier. So you can search on google for this error or ask another question on SO.
Also have a look at how to use order by in union.

Oracle Date and Time Conversion

I have a problem with the SELECT statement below:
SELECT SO FROM ORDERS
UNION ALL
SELECT SO FROM QUOTES
When I run the query I get the error Fields must have same data types. I understand that there is data conflict between the field SO. In the Orders table it is of NVARCHAR2(50) whereas the Quotes table is composed of the Integer datatype. Is there any workaround to make a union with the field SO as shown in the above statement without changing the data type of the field SO?
Any help here is greatly appreciated.
It is just like the query shown below
select 1 from dual --integer datatype
union all
select '1' from dual --varchar2 datatype
In order to make this work ,you need to change the INTEGER DATATYPE to VARCHAR
select to_char(1) from dual --change the datatype to CHAR in Quotes table
union all
select '1' from dual -- this remain unchanged
Your final Query will be like
SELECT SO FROM ORDERS
UNION ALL
SELECT TO_CHAR(SO) FROM QUOTES

Oracle ROWNUM pseudocolumn

I have a complex query with group by and order by clause and I need a sorted row number (1...2...(n-1)...n) returned with every row. Using a ROWNUM (value is assigned to a row after it passes the predicate phase of the query but before the query does any sorting or aggregation) gives me a non-sorted list (4...567...123...45...). I cannot use application for counting and assigning numbers to each row.
Is there a reason that you can't just do
SELECT rownum, a.*
FROM (<<your complex query including GROUP BY and ORDER BY>>) a
You could do it as a subquery, so have:
select q.*, rownum from (select... group by etc..) q
That would probably work... don't know if there is anything better than that.
Can you use an in-line query? ie
SELECT cols, ROWNUM
FROM (your query)
Assuming that you're query is already ordered in the manner you desire and you just want a number to indicate what row in the order it is:
SELECT ROWNUM AS RowOrderNumber, Col1, Col2,Col3...
FROM (
[Your Original Query Here]
)
and replace "Colx" with the names of the columns in your query.
I also sometimes do something like:
SELECT * FROM
(SELECT X,Y FROM MY_TABLE WHERE Z=16 ORDER BY MY_DATE DESC)
WHERE ROWNUM=1
If you want to use ROWNUM to do anything more than limit the total number of rows returned in a query (e.g. AND ROWNUM < 10) you'll need to alias ROWNUM:
select *
(select rownum rn, a.* from
(<sorted query>) a))
where rn between 500 and 1000

Resources