Quiero insertar algunos valores en dos tablas en un bucle, pero parece que no puedo hacerlo debido a la clave externa. Sin embargo, el mismo código funciona en SQL Server.

Esto funciona perfectamente en SQL Server:

declare @i int = 1

while @i<1200
BEGIN

INSERT INTO DOKUMENTY VALUES(null,null);
INSERT INTO POZYCJE VALUES
( null,@i,RAND()*(10000) ) ,
( null,@i,RAND()*(10000) ),
( null,@i,RAND()*(10000) ),
( null,@i,RAND()*(10000) )

SET @i+=1

END

Estoy tratando de hacer lo mismo en Firebird:

SET TERM #;
execute block 
as
declare variable cnt2 integer = 0;
begin
    while (cnt2 < 1200) do
    begin
        insert into DOKUMENTY(DATA_KSIEGOWANIA) values(current_date);

        insert INTO POZYCJE(ID_DOKUMENTU, KWOTA)
        select (:cnt2), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:cnt2), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:cnt2), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:cnt2), MOD(RAND(),1000) FROM RDB$DATABASE ;
        cnt2 = cnt2 + 1;
    end
end#

SET TERM;#

Sin embargo, recibo este error:

Error: *** IBPP::SQLException ***
Message: isc_dsql_execute2 failed

SQL Message : -530
can't format message 13:470 -- message file C:\WINDOWS\SYSTEM32\firebird.msg not found

Engine Code    : 335544466
Engine Message :
violation of FOREIGN KEY constraint "INTEG_44" on table "POZYCJE"
Foreign key reference target does not exist
Problematic key value is ("ID_DOKUMENTU" = 0)
At block line: 10, col: 9

Así es como creo las tablas:

CREATE TABLE DOKUMENTY(
ID_DOKUMENTU integer generated by default as identity primary key,
DATA_KSIEGOWANIA DATE
);

CREATE TABLE POZYCJE(
ID_POZYCJI integer generated by default as identity primary key,
ID_DOKUMENTU integer,
FOREIGN KEY(ID_DOKUMENTU) references DOKUMENTY(ID_DOKUMENTU),
KWOTA decimal(18,2)
);

¿Cómo lo arreglo?

1
Andrzej Szafarczyk 13 mar. 2021 a las 12:37

1 respuesta

La mejor respuesta

Su código de Firebird no es equivalente a su código de SQL Server. Su código de SQL Server comienza en 1, mientras que su código de Firebird comienza en 0. El primer valor generado para una columna de identidad es 1, no 0, por lo que su inserción falla en la restricción de clave externa porque no hay ningún registro en DOKUMENTY con ID_DOKUMENTU = 0. Entonces, la solución inmediata es usar declare variable cnt2 integer = 1; en lugar de declare variable cnt2 integer = 0;

Sin embargo, su código actualmente se basa en la columna de identidad que en realidad comienza en un valor específico (1) y siempre se incrementa en 1. Esa no es una solución segura, por ejemplo, si intenta ejecutarlo ahora con este cambiar, fallará porque la inserción del registro con ID_DOKUMENTU = 1 se deshizo cuando su bloque de ejecución falló, y el siguiente registro insertado tendrá una identificación más alta.

En su lugar, modifique su código para usar la identificación generada real:

execute block 
as
declare variable cnt2 integer = 1;
declare variable ID_DOKUMENTU type of column DOKUMENTY.ID_DOKUMENTU;
begin
    while (cnt2 < 1200) do
    begin
        insert into DOKUMENTY(DATA_KSIEGOWANIA) values(current_date) 
           returning ID_DOKUMENTU into ID_DOKUMENTU;

        insert INTO POZYCJE(ID_DOKUMENTU, KWOTA)
        select (:ID_DOKUMENTU), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:ID_DOKUMENTU), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:ID_DOKUMENTU), MOD(RAND(),1000) FROM RDB$DATABASE UNION ALL
        select (:ID_DOKUMENTU), MOD(RAND(),1000) FROM RDB$DATABASE ;
        cnt2 = cnt2 + 1;
    end
end

Aparte, MOD(RAND(),1000) no hace lo mismo que RAND()*(10000) en su código de SQL Server.

3
Mark Rotteveel 13 mar. 2021 a las 10:06