Ir al contenido


Foto

Recorrer 1 Query para insertar datos de otra base de datos


  • Por favor identifícate para responder
5 respuestas en este tema

#1 coruxito

coruxito

    Advanced Member

  • Miembros
  • PipPipPip
  • 55 mensajes

Escrito 11 junio 2012 - 03:46

Hola amigos, como tengo problemas para trabajar con Firebird con Tablas de Bases de Datos distintas, pues mientras no soy capaz de hacerlo he decidido importar los datos que deseo para una tabla de mi base de datos.
Estoy probando y cree un botón donde puse la rutina abajo:



delphi
  1. procedure TFormPrincipal.Button1Click(Sender: TObject);
  2. begin
  3.   DataModule1.IBQueryCargaClientes2.Open;
  4.   DataModule1.IBQueryCargaClientes.Open;
  5.   DataModule1.IBQueryCargaClientes2.Last;
  6.   DataModule1.IBQueryCargaClientes.Last;
  7.  
  8.   if (DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsInteger<DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').AsInteger) then
  9.   begin
  10.     DataModule1.IBQueryCargaClientes2.First;
  11.     while (DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').AsInteger<=DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsInteger) do
  12.     begin
  13.       DataModule1.IBQueryCargaClientes2.Next;
  14.     end;
  15.     while not (DataModule1.IBQueryCargaClientes2.Eof) do
  16.     begin
  17.       DataModule1.IBQueryInsertarClientes.Close;
  18.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_id').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').Value;
  19.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_nif').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_nif').Value;
  20.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_nombre').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_nombre').Value;
  21.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_email').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_email').Value;
  22.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_codigobanco').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_codigobanco').Value;
  23.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_codigosucursal').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_codigosucursal').Value;
  24.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_numerocuenta').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_numerocuenta').Value;
  25.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_digitocontrol').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_digitocontrol').Value;
  26.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_contacto').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('cli_contacto').Value;
  27.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_direccion').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('dir_direccion').Value;
  28.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_cp').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('dir_cp').Value;
  29.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_poblacion').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('dir_poblacion').Value;
  30.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_telefono1').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('dir_telefono1').Value;
  31.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_telefono2').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('dir_telefono2').Value;
  32.       DataModule1.IBQueryInsertarClientes.ParamByName('cli_provincia').Value:=DataModule1.IBQueryCargaClientes2.FieldByName('prv_nombre').Value;
  33.       DataModule1.IBQueryInsertarClientes.ExecSQL;
  34.       DataModule1.IBTransactionINSERT.Commit;
  35.  
  36.       DataModule1.IBQueryCargaClientes2.Next;
  37.     end;
  38.   end;
  39. end;



Me funciona perfecto pero cuando a veces con 20 valores, otra 10, otras 5, etc. importados me salta la excepción de la PK violada, voy mirar la base y está todo bien, executo de nuevo y vuelve a hacer, pero vuelve a saltar, miro y ha importado correctamente del punto que había parado.
Utilizo Firebird 2.5, Delphi 7 las Querys citadas en el código tiene las siguintes SQL:

IBQueryCargaClientes (sobre la tabla de mi base de datos)

select CLI_ID, CLI_NIF, CLI_NOMBRE, CLI_EMAIL, CLI_CODIGOBANCO, CLI_CODIGOSUCURSAL, CLI_NUMEROCUENTA, CLI_DIGITOCONTROL, CLI_CONTACTO, CLI_DIRECCION, CLI_CP, CLI_POBLACION, CLI_TELEFONO1, CLI_TELEFONO2, CLI_PROVINCIA
from CLIENTES
order by CLI_ID;


IBQueryCargaClientes2 (sobre la tabla de la base de datos a ser importada)

select CLI_ID, CLI_NIF, CLI_NOMBRE, CLI_EMAIL, CLI_CODIGOBANCO, CLI_CODIGOSUCURSAL,
        CLI_NUMEROCUENTA, CLI_DIGITOCONTROL, CLI_CONTACTO, DIR_DIRECCION,
        DIR_CP, DIR_POBLACION, DIR_TELEFONO1, DIR_TELEFONO2, PRV_NOMBRE
from CLIENTE
left join DIRECCION on CLI_ID=DIR_IDPROPIETARIO
left join PROVINCIA on PRV_ID=DIR_IDPROVINCIA
order by CLI_ID;


IBQueryInsertarClientes (sobre la tabla de mi base de datos

insert into CLIENTES (CLI_ID, CLI_NIF, CLI_NOMBRE, CLI_EMAIL,
CLI_CODIGOBANCO, CLI_CODIGOSUCURSAL, CLI_NUMEROCUENTA,
CLI_DIGITOCONTROL, CLI_CONTACTO, CLI_DIRECCION, CLI_CP,
CLI_POBLACION, CLI_TELEFONO1, CLI_TELEFONO2, CLI_PROVINCIA
)
Values(:cli_id,
:cli_nif,
:cli_nombre,
:cli_email,
:cli_codigobanco,
:cli_codigosucursal,
:cli_numerocuenta,
:cli_digitocontrol,
:cli_contacto,
:cli_direccion,
:cli_cp,
:cli_poblacion,
:cli_telefono1,
:cli_telefono2,
:cli_provincia
);


A mí me dá que Firebird hace algo antes de lo que debería y por eso acaba intentando insertar el mismo valor. no sé por lo que será ese tipo de comportamiento ni si es eso.

Alguien tiene idea de porque ocurre ese comportamiento tan ilógico ?
  • 0

#2 coruxito

coruxito

    Advanced Member

  • Miembros
  • PipPipPip
  • 55 mensajes

Escrito 11 junio 2012 - 05:54

Acabo de solucionar, no se porque la instrucción .Next sencillamente no se hacia correctamente y mantenía el valor anterior, por eso saltaba el error.

Hice un:


delphi
  1. if(DataModule1.IBQueryInsertarClientes.ParamByName('cli_id').Value<>DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').Value) then



Antes del código que pasaba los valores a los parámetros e ejecutaba la inserción.

Ahora me importa los casi 1.000 valores sin problema alguno.

Que raro eso sí me pareció ese comportamiento ilógico de la instrucción .Next, que a veces lo hacía y a veces no.
  • 0

#3 TiammatMX

TiammatMX

    Advanced Member

  • Miembros
  • PipPipPip
  • 1.750 mensajes
  • LocationUniverso Curvo\Vía Láctea\Sistema Solar\Planeta Tierra\América\México\Ciudad de México\Xochimilco\San Gregorio Atlapulco\Home

Escrito 11 junio 2012 - 06:07

Lo ideal sería usar algo así...

INSERT TablaUno (columnas) values (select * from otratabla where condiciones);


Es más elegante, eficaz y rápido.
  • 0

#4 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 11 junio 2012 - 09:27

Creo que el problema te viene porque la lógica de tu código no es correcta.

Me explico: das por supuestas varias cosas que pueden no ser ciertas, por ejemplo mira este código:



delphi
  1.     while (DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').AsInteger<=DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsInteger) do
  2.     begin
  3.       DataModule1.IBQueryCargaClientes2.Next;
  4.     end;



Si ambas tablas están vacía, los dos AsInteger valdrán cero, y el bucle nunca terminará... el programa se queda colgado!

Pero si ya existen los Ids 1,2,6,7 y los que quieres importar son los 4,5,6,7 (por ejemplo) en ese caso, tu buscas a ver si tienes algun Id superior al primero a importar (4),  para eso recorres la lista de existentes (1,2,6,7) y lo encuetras al llegar al 6, así que pasas ese primera comprobación y comienzas la importación indiscriminada de todos los registros... conseguiras importar el 4 y el 5, pero al intentar importar el 6, te fallará.

El enfoque correcto creo que sería así:

Por cada Id a importar, ejecuto un select para localizarlo en mi tabla "buena", y si tras eso obtengo un eof, es que no existe y lo importo.



delphi
  1. DataModule1.IBQueryCargaClientes.First;
  2. while not DataModule1.IBQueryCargaClientes.eof do begin
  3.   //Existe ya ese cliente en mi tabla?
  4.   DataModule1.QueryCheckIdCliente.SQL.Text:= 'select * from cliente where  id_cliente=' + DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsString;
  5.   DataModule1.QueryCheckIdCliente.ExecSQL;
  6.   if DataModule1.QueryCheckIdCliente.eof then begin
  7.     //No existe, procedo a insercion del registro
  8.   end;
  9.   //Paso al siguiente registro a importar
  10.   DataModule1.IBQueryCargaClientes.Next;
  11. end;


  • 0

#5 coruxito

coruxito

    Advanced Member

  • Miembros
  • PipPipPip
  • 55 mensajes

Escrito 12 junio 2012 - 01:21

Lo ideal sería usar algo así...

INSERT TablaUno (columnas) values (select * from otratabla where condiciones);


Es más elegante, eficaz y rápido.


Pero puedo hacer eso en una sola Query para dos bases de datos distintas ?
Era lo que yo quería hacer desde un principio pero Firebird 2.5 sólo permite trabajar con bases de datos distintas con el Execute Statement, como no le pillé el punto (tengo otro post abierto sobre como hacerlo sin aún saber el como).
Si existe alguna manera agradezco sobremanera.
  • 0

#6 coruxito

coruxito

    Advanced Member

  • Miembros
  • PipPipPip
  • 55 mensajes

Escrito 12 junio 2012 - 01:35

Creo que el problema te viene porque la lógica de tu código no es correcta.

Me explico: das por supuestas varias cosas que pueden no ser ciertas, por ejemplo mira este código:



delphi
  1.     while (DataModule1.IBQueryCargaClientes2.FieldByName('cli_id').AsInteger<=DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsInteger) do
  2.     begin
  3.       DataModule1.IBQueryCargaClientes2.Next;
  4.     end;



Si ambas tablas están vacía, los dos AsInteger valdrán cero, y el bucle nunca terminará... el programa se queda colgado!

Pero si ya existen los Ids 1,2,6,7 y los que quieres importar son los 4,5,6,7 (por ejemplo) en ese caso, tu buscas a ver si tienes algun Id superior al primero a importar (4),  para eso recorres la lista de existentes (1,2,6,7) y lo encuetras al llegar al 6, así que pasas ese primera comprobación y comienzas la importación indiscriminada de todos los registros... conseguiras importar el 4 y el 5, pero al intentar importar el 6, te fallará.

El enfoque correcto creo que sería así:

Por cada Id a importar, ejecuto un select para localizarlo en mi tabla "buena", y si tras eso obtengo un eof, es que no existe y lo importo.



delphi
  1. DataModule1.IBQueryCargaClientes.First;
  2. while not DataModule1.IBQueryCargaClientes.eof do begin
  3.   //Existe ya ese cliente en mi tabla?
  4.   DataModule1.QueryCheckIdCliente.SQL.Text:= 'select * from cliente where  id_cliente=' + DataModule1.IBQueryCargaClientes.FieldByName('cli_id').AsString;
  5.   DataModule1.QueryCheckIdCliente.ExecSQL;
  6.   if DataModule1.QueryCheckIdCliente.eof then begin
  7.     //No existe, procedo a insercion del registro
  8.   end;
  9.   //Paso al siguiente registro a importar
  10.   DataModule1.IBQueryCargaClientes.Next;
  11. end;



Gracias Sergio. Sí llevas razón sobre la lógica, es mejorable, de hecho era una aproximación a lo quería hacer (de momento lo tengo en un botón). No lo quería hacer de esa forma la verdad, pero al ser dos bases de datos distintas y no tener ni idea de como Execute Statement lo hace, pues me puse a buscar soluciones y hice esa manera a lo bruto.
El que más raro me ha parecido es que tengo la base destino vacía, hacía la rutina y sencillamente la instrucción Next hacía cosas raras, sencillamente a veces se negando a dar el salto a la siguiente. De hecho puse la el if que ya cité y me fue de perla sin problemas, eso sí, como bien as comentado sin control alguno sobre la importación.
  • 0




IP.Board spam blocked by CleanTalk.