Search code examples
oracle-databaseplsqloracle-sqldeveloperplsqldeveloperplsql-package

Procedure to insert in unique in values into tables not working


I'm a beginner in this language and i'm being asked to insert via procedure some unique variables into the tables aeropuerto and terminal (the variables have to be unique compared to the rest in the tables) and after thinking, this is as far as i have come. Do you have any suggestions on how to edit the code or how i can insert with simple instructions? (we havent come that far in the course yet :) )

create or replace PROCEDURE EX3 (
    IDAE IN NUMBER, 
    NOMA IN VARCHAR2, 
    PLAC IN VARCHAR2, 
    IDTER IN NUMBER, 
    NUMT IN NUMBER)
AS 
BEGIN
  
    IF IDAE!=AEROPUERTO.ID AND NOMA!=AEROPUERTO.NOMBRE AND PLAC!=AEROPUERTO.LUGAR THEN
            INSERT INTO AEROPUERTO(ID, NOMBRE, LUGAR) 
            VALUES (ID_AAUX, NOMA, PLAC);
    END IF;
    IF IDTER!=TERMINAL.ID AND NUMT!=TERMINAL.NUMERO AND IDAE!=TERMINAL.ID_AEROPUERTO THEN
            INSERT INTO TERMINAL(ID, NUMERO, ID_AEROPUERTO) 
            VALUES (ID_TAUX, NUMT, IDAE);
    END IF;
END EX3;

Solution

  • The simplest approach would probably to use a NOT EXISTS clause in your INSERT statement.

    create or replace PROCEDURE EX3 (
      IDAE IN NUMBER, 
      NOMA IN VARCHAR2, 
      PLAC IN VARCHAR2, 
      IDTER IN NUMBER, 
      NUMT IN NUMBER )
    AS 
    BEGIN
      INSERT INTO AEROPUERTO(ID, NOMBRE, LUGAR) 
        SELECT idae, noma, plac
          FROM dual
         WHERE NOT EXISTS( SELECT 1
                             FROM AEROPUERTO a
                            WHERE a.id = idae
                              AND a.nombre = noma
                              AND a.lugar = plac );
    
      INSERT INTO TERMINAL(ID, NUMERO, ID_AEROPUERTO) 
        SELECT idter, numt, idae
          FROM dual
         WHERE NOT EXISTS( SELECT 1
                             FROM terminal t
                            WHERE t.id = idter
                              AND t.numero = numt
                              AND t.id_aeropuerto = idae );
    END EX3;
    

    Depending on how common it would be to actually get duplicate values, however, and assuming there is a unique constraint in place, it may be more efficient to just try the insert and catch the exception

    create or replace PROCEDURE EX3 (
      IDAE IN NUMBER, 
      NOMA IN VARCHAR2, 
      PLAC IN VARCHAR2, 
      IDTER IN NUMBER, 
      NUMT IN NUMBER )
    AS 
    BEGIN
      BEGIN
        INSERT INTO AEROPUERTO(ID, NOMBRE, LUGAR) 
          VALUES( idae, noma, plac );
      EXCEPTION
        WHEN dup_val_on_index
        THEN
          null;  -- Ignore the error if the row already exists
      END;
    
      BEGIN
        INSERT INTO TERMINAL(ID, NUMERO, ID_AEROPUERTO) 
          VALUES( idter, numt, idae );
      EXCEPTION
        WHEN dup_val_on_index
        THEN
          null;  -- Ignore the error if the row already exists
      END;
    END EX3;
    

    As an aside, your life will generally be easier if you use a naming convention for parameters and local variables that ensures that it is clear which column they reference but which can't conflict with the name of an actual column. And your life will be easier if you use anchored types to make it extra clear which columns particular parameters are supposed to match up with.

    If your procedure was declared

    create or replace PROCEDURE EX3 (
      p_id_aeropuerto IN aeropuerto.id%type, 
      p_nombre        IN aeropuerto.nombre%type, 
      p_lugar         IN aeropuerto.lugar%type, 
      p_id_terminal   IN terminal.id%type,
      p_numero        IN terminal.numero%type )
    

    it would likely be easier to read and it would be clearer which parameters map to which table. It will also mean less maintenance since when someone decides they need to increase the length of a column, you don't have to adjust your PL/SQL code (assuming you always use anchored types).

    I would also tend to suggest using id_aeropuerto and id_terminal in the definition of the aeropuerto and terminal tables rather than having generic id columns in every table. That's more a matter of style but I always find it easier to read code where you're joining on the same column name and where columns with the same name mean the same thing. Having the same id column in a bunch of tables that each mean something different makes me spend more time looking at the code to make sure that it's not joining to the wrong identifier.