Select default in case of no value returned - oracle

I am trying to get some default value in my resultset if query does not return anything. I am trying nvl for the same but it is not returning the expected default value. To simulate, Consider following query,
select nvl(null, '10') from dual where 1=0;
I want to get 10 in case of given condition is not true and query does not return any value. However above query not returning any row.

Your query returns zero rows. NVL() isn't going to change that (*).
The correct solution is for the program which executes the query to handle NO_DATA_FOUND exception rather than fiddling the query.
However, you need a workaround so here is one using two sub-queries, one for your actual query, one to for the default.
When your_query returns an empty set you get this:
SQL> with your_qry as
2 ( select col1 from t42 where 1=0 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
10
SQL>
And when it returns a row you get this:
SQL> with your_qry as
2 ( select col1 from t42 )
3 , dflt as
4 ( select 10 as col1 from dual )
5 select col1
6 from your_qry
7 union all
8 select col1
9 from dflt
10 where not exists (select * from your_qry );
COL1
----------
12
13
SQL>
The WITH clause is optional here, it just makes it easier to write the query without duplication. This would have the same outcome:
select col1
from t42
where col0 is null
union all
select 10
from dual
where not exists (select col1
from t42
where col0 is null)
;
(*) Okay, there are solutions which use NVL() or COALESCE() with aggregations to do this. They work with single column projections in a single row as this question poses, but break down when the real query has more than one row and/or more than one column. Aggregations change the results.
So this looks alright ...
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual where 1=0 union all
3 select 'X' as col0, 13 as col1 from dual where 1=0 )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Y 10
SQL>
... but this not so much:
SQL> with cte as (
2 select 'Z' as col0, 12 as col1 from dual union all
3 select 'X' as col0, 13 as col1 from dual )
4 select
5 nvl(max(col0), 'Y') as col0, nvl(max( col1), 10) as col1
6 from cte;
COL0 COL1
---------- ----------
Z 13
SQL>

May be something like this is what you need
You could change WHERE clause (in this case WHERE COL > 1) similarly in both places.
WITH T(COL) AS(
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL
)
SELECT COL FROM T WHERE COL > 1
UNION ALL
SELECT 10 AS COL FROM DUAL WHERE NOT EXISTS( SELECT 1 FROM T WHERE COL > 1)

You can use aggregation. An aggregation query always returns one row:
select coalesce(max(null), '10')
from dual
where 1 = 0;
I prefer coalesce() to nvl() because coalesce() is the ANSI standard function. But, nvl() would work here just as well.

Related

How to insert values to newly added column

I am new to oracle thus this question.
There is a table already existed and I have added a new column to it.
There are 5 rows and I do not want to use update table with where clause to insert the values one by one in the new column. Is there a statement like INSERT ALL to insert the values into the new column in one shot ?
Thanks
You can also use something like below which in-effect I would say is multiple update only, wrapped in single statement.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1
2
3
4
SQL> update test_upd a set a.id2 =
2 (select
3 case
4 when id1=1 then 100
5 when id1=2 then 200
6 when id1=3 then 300
7 else 5000 end
8 from test_upd b
9 where a.id1=b.id1);
4 rows updated.
SQL> select * from test_upd;
ID1 ID2
---------- ----------
1 100
2 200
3 300
4 5000
Use a MERGE statement:
MERGE INTO your_table dst
USING (
SELECT 1 AS id, 'aaa' AS newvalue FROM DUAL UNION ALL
SELECT 2, 'bbb' FROM DUAL UNION ALL
SELECT 3, 'ccc' FROM DUAL UNION ALL
SELECT 4, 'ddd' FROM DUAL UNION ALL
SELECT 5, 'eee' FROM DUAL
) src
ON (dst.id = src.id)
WHEN MATCHED THEN
UPDATE SET value = src.newvalue;
Which, if you have the table:
CREATE TABLE your_table (id, value) AS
SELECT 1, CAST(NULL AS VARCHAR2(3)) FROM DUAL UNION ALL
SELECT 2, NULL FROM DUAL UNION ALL
SELECT 3, NULL FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Then, after the MERGE, the table contains:
ID
VALUE
1
aaa
2
bbb
3
ccc
4
ddd
5
eee
db<>fiddle here

Oracle Apex Pivot sorting

I have an interactive report, which using Pivot, but I was trying to custom sort the report by row column of pivot "Age Group", but I couldn't.
I need this report in such this sorting:
How I can do that?
Thanks
Looks like you'd want to sort by the first digit(s), and then by age_group itself (as a string). Something like this (sample data in lines #1 - 10; query you might be interested in begins at line #11):
SQL> with test (age_group) as
2 (select 'wahine 45-64' from dual union all
3 select 'wahine 25-44' from dual union all
4 select 'rangtahi 15-24' from dual union all
5 select 'pepis 0_4' from dual union all
6 select 'pakeke 45-64' from dual union all
7 select 'pakeke 25-44' from dual union all
8 select 'N/A' from dual union all
9 select 'kuia 65+' from dual
10 )
11 select age_group
12 from test
13 order by to_number(regexp_substr(age_group, '\d+')), age_group;
AGE_GROUP
--------------
pepis 0_4
rangtahi 15-24
pakeke 25-44
wahine 25-44
pakeke 45-64
wahine 45-64
kuia 65+
N/A
8 rows selected.
SQL>

How to sort texts with '_' in Oracle exactly like EXCEL?

In Excel When I sort the texts in ascending order, results shows as below. Text with the underscore character precedes the others. And in Excel cell, when I type in ="_" < "A", then "True" shows as expected.
C10_
C10A
C20_
C20A
But, In Oracle, when I sort in ascending order, results shows as below.
(I guess, Oracle treats '_' < 'A' False)
C10A
C10_
C20A
C20_
How can I make Oracle sort the list exactly as Excel does? I have changed ASC to DESC, but the result was not what I expect.
My sorting code is as below,
WITH DATAA AS (
SELECT *
FROM
(
SELECT 'C10_'rr FROM DUAL
UNION
SELECT 'C10A' rr FROM DUAL
UNION
SELECT 'C20_' rr FROM DUAL
UNION
SELECT 'C20A' rr FROM DUAL
)
)
SELECT *
FROM DATAA
ORDER BY rr ASC;
You can achieve this by altering the sorting method using NLS_SORT as following:
According to ORACLE Documentation:
NLS_SORT specifies the type of sort for character data. It overrides
the default value that is derived from NLS_LANGUAGE.
NLS_SORT contains either of the following values:
NLS_SORT = BINARY | sort_name
BINARY specifies a binary sort. sort_name specifies a linguistic sort
sequence.
Here is how you can achieve the result.
SQL> -- Your original query
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10A
C10_
C20A
C20_
--
SQL> -- Now altering the sorting method
SQL> --
SQL> --
SQL> alter session set NLS_SORT = German;
Session altered.
SQL> --
SQL> --
SQL> -- Now see the result
SQL> --
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10_
C10A
C20_
C20A
SQL>
Cheers!!

Convert single row result into two column result. Unpivot error 'expression must have same datatype as corresponding expression'

Please read only EDIT section which is more relevant now.
I have table in oracle 12c database, say table_1.
I have to run a simple SQL which returns only 1 row and all columns from from table_1. Say like
select * from table_1 where col_1 = 65
But the result I want is like:
I did some search and found this link which is very similar to what i want.
But I was only able to get the first column in the expected result by folowing it. I am unable to get actual data in the second column of the expected result.
So far I was able to write only:
select v
from (
select 'col_1' as col_1, 'col_2' as col_2, 'col_3' as col_3, 'col_4' as col_4 from dual
) t
unpivot
(
v for val in (col_1,col_2,col_3,col_4)
) u;
How can I add the second column and the condition where col_1 = 65?
EDIT: (Above part is less relevant now)
I fount this link where there us unpivot example which I could use.
select * from olympic_medal_tables
Gives:
desc olympic_medal_tables
Name Null? Type
------------- ----- -----------
NOC VARCHAR2(3)
GOLD_MEDALS NUMBER
SILVER_MEDALS NUMBER
BRONZE_MEDALS NUMBER
Following SQL gives me what I could use, but I don't want the NOC column in the result:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE'
));
Result:
So when I add NOC as well (so it is not added as a column in the result) like:
select * from olympic_medal_tables
unpivot (medal_count for medal_colour in (
gold_medals as 'GOLD',
silver_medals as 'SILVER',
bronze_medals as 'BRONZE',
noc as 'NOC'
));
I get error: ORA-01790: expression must have same datatype as corresponding expression on line 6 Error at Line: 44 Column: 3
I have tried using TO_NUMBER and TO_CHAR as well but then I get various syntax errors.
Question: How can I get expected result with just 2 columns. Is it possible with this approach using unpivot?
Here's one option, which requires you to first select one row, and then apply UNPIVOT to it:
SQL> with test (col1, col2, col3, col4) as
2 (select '12', 'hfkds' , 'hk435k' , '32' from dual union
3 select '34', 'ldkfgj', 'fsjd4653', '324' from dual union
4 select '65', 'ifd' , 'dkfjs345', '23' from dual union
5 select '87', 'dg' , '345jh' , '65' from dual
6 ),
7 one_row as
8 (select * From test
9 where col1 = '65'
10 )
11 select *
12 from one_row
13 unpivot (col_value for col_name in
14 (col1 as 'col1', col2 as 'col2', col3 as 'col3', col4 as 'col4'));
COL_ COL_VALU
---- --------
col1 65
col2 ifd
col3 dkfjs345
col4 23
SQL>

remove a varchar2 string from the middle of table data values

Data in the file_name field of the generation table should be an assigned number, then _01, _02, or _03, etc. and then .pdf (example 82617_01.pdf).
Somewhere, the program is putting a state name and sometimes a date/time stamp, between the assigned number and the 01, 02, etc. (82617_ALABAMA_01.pdf or 19998_MAINE_07-31-2010_11-05-59_AM.pdf or 5485325_OREGON_01.pdf for example).
We would like to develop a SQL statement to find the bad file names and fix them. In theory it seems rather simple to find file names that include a varchar2 data type and remove it, but putting the statement together is beyond me.
Any help or suggestions appreciated.
Something like:
UPDATE GENERATION
SET FILE_NAME (?)
WHERE FILE_NAME (?...LIKE '%STRING%');?
You can find the problem rows like this:
select *
from Files
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
You can fix them like this:
update Files
set FILE_NAME = SUBSTR(FILE_NAME, 1, instr(FILE_NAME, '_') -1) ||
SUBSTR(FILE_NAME, instr(FILE_NAME, '_', 1, 2))
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
SQL Fiddle Example
You can also use Regexp_replace function:
SQL> with t1(col) as(
2 select '82617_mm_01.pdf' from dual union all
3 select '456546_khkjh_89kjh_67_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col
8 , regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3') res
9 from t1;
COL RES
-------------------------------------- -----------------------------------------
82617_mm_01.pdf 82617_01.pdf
456546_khkjh_89kjh_67_01.pdf 456546_01.pdf
19998_MAINE_07-31-2010_11-05-59_AM.pdf 19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf 5485325_01.pdf
To display good or bad data regexp_like function will come in handy:
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col bad_data
8 from t1
9 where not regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
BAD_DATA
--------------------------------------
19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col good_data
8 from t1
9 where regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
GOOD_DATA
--------------------------------------
826170_01.pdf
456546_01.pdf
To that end your update statement might look like this:
update your_table
set col = regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3');
--where clause if needed

Resources