简体   繁体   中英

Can't create a table that does not exist with PL/SQL Dynamic SQL

I was trying to check if a table exists, if it doesn't then it must be created BUT i'm getting an error below when I try to insert data into it when supposedly it is already created. Here is my script:

PROCEDURE CREAR_CLAVES
AS
  VERIFACION INTEGER;
BEGIN
  SELECT COUNT(TNAME) INTO VERIFACION FROM TAB WHERE TNAME = 'CLAVES';
  IF VERIFACION = 0 THEN
    EXECUTE IMMEDIATE 'CREATE TABLE CLAVES (IDESTUDIANTE NUMBER(5) PRIMARY KEY, USUARIO VARCHAR2(1000), CONTRASENA VARCHAR2(1000))';
    EXECUTE IMMEDIATE 'ALTER TABLE CLAVES ADD CONSTRAINT FK_IDESTUDIANTE FOREIGN KEY (IDESTUDIANTE) REFERENCES ESTUDIANTES(ID)';
  ELSE
    DBMS_OUTPUT.PUT_LINE('LA TABLA YA EXISTE');
  END IF;
END CREAR_CLAVES;


PROCEDURE IDENTIFICAR_ESTUDIANTES
AS
  VESTUDIANTES SYS_REFCURSOR;
  ACTUAL ESTUDIANTES%ROWTYPE;
  CONSULTA VARCHAR2(1000);
BEGIN
  CONSULTA := 'SELECT ID, NOMBRE, APELLIDO FROM ESTUDIANTES';
  OPEN VESTUDIANTES FOR CONSULTA;
  FETCH VESTUDIANTES INTO ACTUAL;
  WHILE VESTUDIANTES%FOUND
  LOOP
    INSERT
    INTO CLAVES VALUES
      (
        ACTUAL.ID,
        LOWER(ACTUAL.APELLIDO)
        ||LOWER(ACTUAL.NOMBRE),
        LOWER(ACTUAL.NOMBRE)
        ||DBMS_RANDOM.VALUE(100, 999)
      );
    FETCH VESTUDIANTES INTO ACTUAL;
  END LOOP;
  CLOSE VESTUDIANTES;
END IDENTIFICAR_ESTUDIANTES;

I get the table or view doesn't exist error. Any idea how can I solve it?

Most probably the problem is that the CLAVES does not exist when the procedure is compiled. It does not even get to the point where it checks its existence and creates it.

You can solve it by putting the 'INSERT INTO CLAVES...' statement into an execute immediate. Use bind variables there to avoid any possibility of sql injection:

CREATE or replace PROCEDURE IDENTIFICAR_ESTUDIANTES 
AS
    VESTUDIANTES SYS_REFCURSOR;
    ACTUAL ESTUDIANTES%ROWTYPE;
    CONSULTA VARCHAR2(1000);
BEGIN
    CONSULTA := 'SELECT ID, NOMBRE, APELLIDO FROM ESTUDIANTES';
    OPEN VESTUDIANTES FOR CONSULTA;
    FETCH VESTUDIANTES
    INTO ACTUAL;
    WHILE VESTUDIANTES%FOUND LOOP
        execute immediate 'INSERT INTO CLAVES VALUES (:IDESTUDIANTE, :USUARIO, :CONTRASENA)'
          using ACTUAL.ID, LOWER(ACTUAL.APELLIDO)||LOWER(ACTUAL.NOMBRE), LOWER(ACTUAL.NOMBRE)||DBMS_RANDOM.VALUE(100, 999);

        FETCH VESTUDIANTES
        INTO ACTUAL;
    END LOOP;
    CLOSE VESTUDIANTES;
END IDENTIFICAR_ESTUDIANTES;
/

BTW, I also disourage the creation of the CLAVES on-the-fly. Moreover, it would be better to implement the cursor loop and row-by-row (slow-by-slow) insert in a single 'insert as select' statment, unless you have a very specific, procedural-heavy logic here that transforms the ESTUDIATES rows into CLAVES.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM