7
SELECT Value1 INTO lValue
FROM   Table1
WHERE  Field1 = lTempValue;

This works fine when the match is true. But if the match isn't true, I receive an error.

ORA-01403: no data found

Ideally, that's fine with me because I'm going to check that value next to see if it's above 0 and if it is, use that value in an insert query. I don't want to check for the value and then have to run the same query to retrieve it essentially, I want to do it in one query if possible, but I can't figure out how that is done.

If there's a value, then I want that value to go into lValue. If there is no value, then I want 0 to go into lValue. Anyone got any ideas? I've only done a quick google check, but it came up dry. Figured I'd post this while looking. Thanks for the help.

XstreamINsanity
  • 3,748
  • 9
  • 42
  • 59
  • 2
    While the below comments will definitely avoid the exception, why not just do it in one statement instead of procedurally? as such `insert into BLAH(a,b) SELECT a,b FROM blah2 WHERE AND EXISTS( SELECT Value1 from table1 where field1 = lTempValue and Value1 > 0 )` – Harrison Aug 22 '12 at 21:00
  • It's not that kind of insert query, just going to be part of a text description and as a field in a parameter table, but I get what you were saying. – XstreamINsanity Aug 24 '12 at 12:33

5 Answers5

16

Normally, you'd simply catch the exception

BEGIN
  SELECT value1
    INTO lValue
    FROM table1
   WHERE field1 = lTempValue;
EXCEPTION
  WHEN no_data_found
  THEN
    lvalue := 0;
END;

You can write less code by using NVL and an aggregate function (either MIN or MAX) but that tends to be a bit less obvious (note, for example, that those answers had to get revised a couple of times). And it requires whoever comes after you to pause for a moment to understand what you are doing (and whether you are doing it correctly or not). A simple nested PL/SQL block is pretty common and pretty self-explanatory.

More than that, however, it doesn't hide bugs due to duplicate rows. If you happen to get two rows in table1 where field1 is lTempValue, catching just the no_data_found exception allows the unexpected too_many_rows exception to propagate up to the caller. Since you don't expect to have multiple rows, that is exactly the behavior that you want. Using aggregate functions hides the fact that the underlying data has problems causing you to return potentially incorrect results and making it impossible to detect that there is a problem. I would always rather get an error as soon as something is causing duplicate rows to appear-- allowing me to fix the problem before it gets out of hand-- rather than finding out years later that we've got millions of duplicate rows, that the code has been occasionally returning incorrect results, and that we have a huge data cleansing effort after addressing the root cause.

Alex Poole
  • 161,851
  • 8
  • 150
  • 257
Justin Cave
  • 212,688
  • 21
  • 342
  • 361
  • That works. Now I just need to make sure the DBA's are ok with this. :) Thanks. – XstreamINsanity Aug 24 '12 at 12:36
  • Your duplicate row argument doesn't make sense in the user's situation. He wants to check a condition to find an ID to insert a row with that ID (where don't even know if field1 is a unique key), the MAX or MIN solutions just help to find an id and that's what he wants. His insertion of the new row is handled elsewhere in code and irrelevant to finding what he needs and Requiring him to make a check here on data that is already entered (and probably stable since it's there) just overcomplicates what he's trying to do by producing unnecessary errors if there are duplicates that already exist. – Reimius Aug 24 '12 at 22:08
  • @Reimius - I see nothing in the user's question that indicates that he expects there to be two rows with the same `field1` value. If he does expect there to be two rows with the same `field1` value, we'd need to know which of the many possible `value1` values to return. Arbitrarily picking the `MIN` or the `MAX` is unlikely to produce the correct results. If the expectation is that there will not be multiple rows, which is the premise of a `SELECT ... INTO`, then the presence of multiple rows is an error that should result in an exception being thrown. – Justin Cave Aug 24 '12 at 22:15
  • I must warn that this solution can have a problem that you should check for. A pair of years ago, on Oracle 10g2, if a statement like this was was called in a loop and the `no_data_found` was frequent the performance would degrade. We've never found the origin of that as it was counter intuitive. The solution was to avoid the `no_data_found` exception. No code other than setting the default value was used in exception part. – Fábio Oliveira Aug 27 '12 at 22:49
  • There will not be multiple rows as I'm looking for a row with the value of `field1` and a specific status, and only one record for that `field1` can have that status. @FábioOliveira - No loops, no worry. :) Thanks though. – XstreamINsanity Sep 06 '12 at 18:08
3

As an alternative to Justin Cave's suggestion, you can rewrite the query slightly so that it always returns a row — something like this:

SELECT NVL(Value1, 0) INTO lValue
FROM   Table1
RIGHT
JOIN   dual
ON     Field1 = lTempValue
ruakh
  • 156,364
  • 23
  • 244
  • 282
2

It seems that everyone is really overcomplicating this, do this assuming your values aren't weird types like record or clobs:

SELECT NVL(MIN(Value1), 0) INTO lValue
FROM   Table1
WHERE  Field1 = lTempValue;
Reimius
  • 5,614
  • 4
  • 22
  • 41
1

I would do it as a cursor- just to be safe (since, I don't like the idea of stray PLSQL blocks like BEGIN ....END; inside my stored procedure), something like

CREATE OR REPLACE .....
...

 CURSOR c_get_val IS
 SELECT Value1 
 FROM   Table1
 WHERE  Field1 = lTempValue;

 lValue    Table1.Value1%TYPE;
 lTempValue  Table1.Table1%TYPE;

and then,

BEGIN
...
....
 /* populate lTempValue */

 OPEN c_get_val;
 FETCH c_get_val INTO lValue;
 if c_get_val%NOTFOUND    --this is where you handle ORA-01403: no data found
 then
   lValue := 0;
   /*or call a function, do some other stuff*/
 end if;
 CLOSE c_get_val; 
...
...
EXCEPTION
  /*do some smart exception handling here*/
END;

Some info on cursors, and more, and some more.

AnBisw
  • 7,081
  • 5
  • 45
  • 71
-1
BEGIN
  SELECT value1
    INTO lValue
    FROM table1
   WHERE field1 = lTempValue;
EXCEPTION
  WHEN no_data_found
  THEN
    lvalue := 0;
EXCEPTION
  WHEN too_many_rows
  THEN
    lvalue := 0;
END;
Kirill Muratov
  • 266
  • 2
  • 8
  • Why would you catch the `too_many_rows` exception? If that is not an expected condition, you shouldn't be catching it with an exception handler, you should be allowing the exception to propagate up. Nothing in the original question indicates that @XStreamINsnity expects multiple rows with the same `field1` value. – Justin Cave Aug 24 '12 at 21:10
  • 1
    we don't know table structure: It can be so: id field1 value1 ---1 10 100500 ---2 10 100500 ---3 12 200600 – Kirill Muratov Aug 25 '12 at 06:28