0

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.

Not a bug
  • 4,286
  • 2
  • 40
  • 80
  • 1
    Check the answer to this [question](http://stackoverflow.com/questions/8200462/how-to-display-a-default-value-when-no-match-found-in-a-query) , hope it helps. – Prabhat Apr 16 '17 at 14:45
  • @mohanrathour . . . `COALESCE()` versus `NVL()` has nothing to do with this question. – Gordon Linoff Apr 16 '17 at 15:45

3 Answers3

3

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>
APC
  • 144,005
  • 19
  • 170
  • 281
  • Unfortunately I can not handle NO_DATA_FOUND since I am appending this query as sub-query. Can you suggest any way to get the default value if no record found? – Not a bug Apr 16 '17 at 14:45
  • As it happens I was working on a kludge that used sub-queries, so maybe it will work for you. – APC Apr 16 '17 at 14:55
  • Answer is correct but the approach of using `with` is not feasible in my case. The query I am looking for will be appended as sub-query in select clause of some other query. – Not a bug Apr 17 '17 at 06:36
  • 1
    Several people have invested their time to help you. I suggest you spend some​time yourself framing a decent question which explains what you have so far and what it is you want. – APC Apr 17 '17 at 07:54
  • @Notabug - simply googling `oracle with` and I think you understood what `with` means here. – Oto Shavadze Apr 17 '17 at 08:33
0

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)
Oto Shavadze
  • 40,603
  • 55
  • 152
  • 236
-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.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • 1
    The OP is not using aggregation in their query; this would change the meaning of the query. – MT0 Apr 16 '17 at 15:01
  • 1
    @MT0 . . . This is probably the simplest way to guarantee that exactly one row is returned from a query. You don't seem to understand this. I have used this technique multiple times in different databases when I need to ensure that one row is returned. – Gordon Linoff Apr 16 '17 at 15:45
  • The OP wishes to return one row if "if query does not return anything" - they are not asking to return exactly one row, they want to return at least one row. – MT0 Apr 16 '17 at 16:23
  • @MT0 . . . The query in the question returns at most one row, as does any query on `dual`. – Gordon Linoff Apr 16 '17 at 17:45
  • This looks like answer I am looking for. but what is the difference max() making here? – Not a bug Apr 17 '17 at 06:34
  • 1
    The MAX() applies an aggregation to your your query. It guarantees one row is returned but, as the second part of my answer shows, it can change the non-default result set. So I could back to my recent point: you need to provide a realistic version of what you have and what you're expecting. – APC Apr 17 '17 at 08:14