Search code examples
oracle-databaseif-statementplsqlcursorprocedure

Why do I have an error "exact fetch returns more than requested number of rows"?


    1. create or replace procedure cities (
    2. vname in varchar2,
    3. vuni out varchar2
    4. ) as
    5. cursor c is
    6. select city_name, uni_name from uni ;
    7. cx c%rowtype;
    8. begin
    9. select uni_name into vuni from uni; 
    10. open c;
    11. loop
    12. fetch c into cx;
    13. exit when c%notfound;
    14. if (vname = 'Almaty') then
    15. select distinct cx.uni_name into vuni from uni where  cx.city_name = vname;
    16. elsif (vname = 'Nur-Sultan') then
    17. select distinct cx.uni_name into vuni from uni where cx.city_name = vname;
    18. elsif (vname = 'Aktau') then
    19. select distinct cx.uni_name into vuni from uni where cx.city_name = vname;
    20. else
    21. select distinct cx.uni_name into vuni from uni where cx.city_name = vname;
    22. end if;
    23. end loop;
    24. close c;
    25. end;

Solution

  • Check all SELECT statement outside of cursor.

    The most suspicious looks the first one at line #9:

    select uni_name into vuni from uni; 
    

    Unless UNI table contains only one row, this will return too_many_rows.

    Other than that, there are several SELECT DISTINCT statements. If DISTINCT doesn't do its job, then either your data is wrong, or your code is wrong. Maybe you need to add yet another condition into the WHERE clause.

    Whichever statement returns the error, the simplest option is to use one of aggregate functions, such as

    select max(cx.uni_name) ...
    

    as it'll return only one value, but - that's most probably the last option you should use.


    [EDIT]

    Wait a minute; yes, you got various errors, but - looking more closely to your code, it doesn't make much sense. First of all, it should have been a function, not a procedure:

    create or replace function f_cities (par_vname in varchar2)
      return uni.uni_name%type
    is
      retval uni.uni_name%type;
    begin
      select u.uni_name
        into retval
        from u.uni
        where u.city_name = par_vname;
    
      return retval;
    end;
    

    If it has to be a procedure, then

    create or replace procedure cities
      (par_vname in varchar2,
       par_vuni out varchar2
      )
    is
    begin
      select u.uni_name
        into par_vuni
        from u.uni
        where u.city_name = par_vname;
    end;
    

    As of your code:

    • cursor you used is not necessary
    • select statements are so strange; you're selecting a cursor variable value into the OUT parameter, from the same table cursor's select is based on
    • ifs are useless. It is the in parameter that says with city it is; you don't have to hardcode those values, as that procedure might become a real monster if there were many more cites in the table

    Shortly, I suggest you use a function I posted above.


    [EDIT #2]

    Based on your comment: there can be several universities in a city. Therefore, you can't return a scalar value but something else, such as ref cursor or an array.

    Suppose this is the table you have:

    SQL> create table uni
      2    (city_name    varchar2(20),
      3     uni_name     varchar2(20));
    
    Table created.
    
    SQL> insert into uni (city_name, uni_name)
      2    select 'Almaty', 'Uni 1'     from dual union all
      3    select 'Almaty', 'Uni2'      from dual union all
      4    select 'Nur-Sultan', 'Uni 4' from dual union all
      5    select 'Aktau', 'Uni 3'      from dual union all
      6    select 'Aktau', 'Uni 9'      from dual;
    
    5 rows created.
    
    SQL>
    

    Now you could do the following:

    SQL> create or replace function f_uni (par_city_name in varchar2)
      2    return sys.odcivarchar2list
      3  as
      4    rc sys.odcivarchar2list;
      5  begin
      6    select uni_name
      7      bulk collect into rc
      8      from uni
      9      where city_name = par_city_name;
     10    return rc;
     11  end;
     12  /
    
    Function created.
    
    SQL> select f_uni('Almaty') from dual;
    
    F_UNI('ALMATY')
    --------------------------------------------------------------------------------
    ODCIVARCHAR2LIST('Uni 1', 'Uni2')
    

    or

    SQL> create or replace function f_uni (par_city_name in varchar2)
      2    return sys_refcursor
      3  as
      4    rc sys_refcursor;
      5  begin
      6    open rc for select uni_name
      7          from uni
      8      where city_name = par_city_name;
      9    return rc;
     10  end;
     11  /
    
    Function created.
    
    SQL> select f_uni('Almaty') from dual;
    
    F_UNI('ALMATY')
    --------------------
    CURSOR STATEMENT : 1
    
    CURSOR STATEMENT : 1
    
    UNI_NAME
    --------------------
    Uni 1
    Uni2
    
    
    SQL>