PDA

Ver la Versión Completa : no se puede agregar la clave foranea


giulichajari
24-06-2014, 22:05:23
Tengo un datamodule con 3 sqlquery (sqlquery1, sqlquery2, y aux).
Y en la bd tengo una tabla persona y una tabla cliente. En la tabla cliente tengo el idpersona, pero algo falla al querer agregar un cliente, mne agrega los datos de la tabla madre solamente.

Los procedimientos son:

procedure Tferreteria.inserpersona(persona:Tpersona);
begin
dm:=Tconsultas.Create(nil);
dm.SQLQuery1.Close;
dm.SQLQuery1.SQL.Add('insert into persona (nombreyape, domicilio)values (:nombreyape,:domicilio);');

with dm.SQLQuery1 do
begin
ParamByName('nombreyape').AsString:=persona.nombreyape;
ParamByName('domicilio').AsString:=persona.domicilio;


end;
dm.SQLQuery1.ExecSQL();
dm.SQLQuery1.Close;
end;


procedure TFerreteria.insercliente(cliente:Tcliente);
begin
dm:=Tconsultas.Create(nil);
dm.SQLQuery2.Close;


dm.SQLQuery2.SQL.Add ('insert into clientes(idpersona,cuit,cuil) values (:idpersona,:cuit,:cuil);');
with dm.SQLQuery2 do
begin
dm.aux.SQL.Add('select max(idpersona) from persona as idp;');
dm.aux.ExecSQL();
ParamByName('idpersona').Value:=dm.aux.ParamByName('idp').AsInteger;
ParamByName('cuit').Value:=cliente.cuit;
ParamByName('cuil').Value:=cliente.cuil;
end;
dm.SQLQuery2.ExecSQL();
dm.SQLQuery2.Close;
end;

Espero alguien pueda ayudarme, los sqlquery tienen los parametros en la propiedad TParams.. agregados.

Gracias

duilioisola
26-06-2014, 18:12:31
En tu código ejecutas el query de aux. En su lugar deberías abrirlo.
Si lo ejecutas no obtendrás resultados. Esta instrucción está pensada para ejecutar cosas como un insert/update/delete que no devuelvan nada.
No recuerdo si la instrucción es Open u OpenSQL.

procedure TFerreteria.insercliente(cliente:Tcliente);
begin
dm:=Tconsultas.Create(nil);
dm.SQLQuery2.Close;

dm.SQLQuery2.SQL.Add ('insert into clientes(idpersona,cuit,cuil) values (:idpersona,:cuit,:cuil);');
with dm.SQLQuery2 do
begin
dm.aux.SQL.Add('select max(idpersona) from persona as idp;');

// Aquí debes abrir el dataset.
dm.aux.Open;

ParamByName('idpersona').Value:=dm.aux.ParamByName('idp').AsInteger;
ParamByName('cuit').Value:=cliente.cuit;
ParamByName('cuil').Value:=cliente.cuil;
end;
dm.SQLQuery2.ExecSQL();
dm.SQLQuery2.Close;
end;


De todos modos yo organizaría un poco mejor el código y creo además que te falta liberar el objeto creado (Tconsultas).
Si me permites:

procedure TFerreteria.insercliente(cliente:Tcliente);
begin
dm := Tconsultas.Create(nil);
try
// Obtengo el Id de la persona
dm.aux.SQL.Add('select max(idpersona) from persona as idp;');
// Aquí debes abrir el dataset.
dm.aux.Open;

// Inserto el cliente
with dm.SQLQuery2 do
begin
Close;
SQL.Add ('insert into clientes(idpersona,cuit,cuil) values (:idpersona,:cuit,:cuil);');
ParamByName('idpersona').Value:=dm.aux.ParamByName('idp').AsInteger;
ParamByName('cuit').Value:=cliente.cuit;
ParamByName('cuil').Value:=cliente.cuil;
ExecSQL();
Close;
end;
dm.aux.Close;
finally
// Creo que aquí deberías liberar el objeto TConsultas
dm.Free;
end;
end;

giulichajari
26-06-2014, 19:04:13
Gracias por responder, voy a probar el codigo.

Lo que me han dicho otros colegas, es que select max(id) sirve solo si un solo usuario puede insertar en una tabla.

Porque si varios acceden, por concurrencia, no devolvera exactamente el ultimo id insertado.

Para esto se debe usar mas bien Select last_insert_id(); que devuelve el de la sesion. Pero no se me ocurre como resolver esto, ya que deberia usar el mismo TSQLQuery?

Saludos

duilioisola
26-06-2014, 21:51:38
Yo utilizo IDs para cada registro de la tabla y luego otros campos para los números de orden, pedido, factura, etc.
El ID es un campo obtenido de un generador y el número del documento se calcula en los triggers.
Todo lo hecho en un trigger está dentro de una misma transacción, por lo que si no hay errores al insertar/modificar, la base de datos se ha encargado de que no haya duplicados.

Yo trabajo con Firebird. Esta base tiene generadores que devuelven el número siguiente del actual.
En los eventos BeforePost es donde obtengo un número para mi ID y se lo asigno al parámetro del dataset. Este ID es imposible que se duplique, por lo solo queda esperar a que el SQL se complete y refrescar los datos que sean necesarios (NRO_DOCUMENTO, TOTALES, etc.)


function TDMMain.DameGenerador(NomGen: string): integer;
var
Q : THYFIBQuery;
begin
Result := 0;
if (Trim(NomGen) > '') then
begin
Q := THYFIBQuery.Create(nil);
try
with Q do
begin
Close;
DataBase := Self.DataBase;
SQL.Text := 'SELECT GEN_ID(' + NomGen + ',1) FROM RDB$DATABASE';
ExecQuery;
Result := Fields[0].AsInteger;
Close;
FreeHandle;
end;
finally
FreeAndNil(Q);
end;
end;
end;

procedure TDMFacturas.QMCabeceraBeforePost(DataSet: TDataSet);
var
existe : smallint;
begin
// Solo asigno ID si estoy insertando
if (DataSet.State = dsInsert) then
DataSet.FieldByName('ID_CAB_FACTURA_CLI').AsInteger := DMMain.DameGenerador('GEN_CAB_FACTURA_CLI');
[...]
end;


El trigger sería algo así:

créate trigger CAB_FACTURA_CLI_BI0 for FACTURA before insert 0
as
begin
/* Calculo el ID si vino vacio */
if ((new.ID_CAB_FACTURA_CLI = 0) or (new.ID_CAB_FACTURA_CLI is null)) then
newID_CAB_FACTURA_CLI := GEN_ID(GEN_CAB_FACTURA_CLI, 1);

/* Calculo el siguiente numero de factura */
select max(numero) from CAB_FACTURA_CLI
where
empresa = new.empresa and
ejercicio = new.ejercicio and
serie = new.serie
into new.numero;

/* Si es la primera factura no me habrá devuelto nada util */
if ((numero =0) or (new.numero is null)) then
new.numero = 1;

[...]
end

giulichajari
27-06-2014, 17:19:13
entiendo tu forma de hacerlo. Y esta buena, lo que si es que obtengo el error

Aux:Parameter idp not found, y SI esta declarado en el value de la propiedad PARAMS del componente, es como que no lo esta cargando.

Saludos..

duilioisola
27-06-2014, 18:52:04
[QUOTE=giulichajari;478382]lo que si es que obtengo el error Aux:Parameter idp not found, y SI esta declarado en el value de la propiedad PARAMS del componente, es como que no lo esta cargando.
QUOTE]

Creo que esto se debe a que la sintaxis es incorrecta


/* Tu sintaxis */
select max(idpersona) from persona as idp
/* Esta creo que es la forma correcta */
select max(idpersona) as idp from persona

giulichajari
28-06-2014, 22:16:03
[quote=giulichajari;478382]lo que si es que obtengo el error Aux:Parameter idp not found, y SI esta declarado en el value de la propiedad PARAMS del componente, es como que no lo esta cargando.
QUOTE]

Creo que esto se debe a que la sintaxis es incorrecta

Código SQL [-] (http://clubdelphi.com/foros/#)/* Tu sintaxis */ select max(idpersona) from persona as idp /* Esta creo que es la forma correcta */ select max(idpersona) as idp from persona



Y si quiero insertar luego el telefono de la persona? He insertado una persona, un cliente con el id de persona, pero al insertar un telefono el id sera el de cliente...

Saludos

Casimiro Notevi
28-06-2014, 22:32:05
La cara oculta de delphi (http://terawiki.clubdelphi.com/Delphi/Manuales/?download=La_Cara_Oculta_De_Delphi_4_pdf_.zip)

giulichajari
28-06-2014, 23:23:30
Logre hacerlo con componentes SQLQuery. Si quieren dejen su opinion.

procedure Tferreteria.insercliente(cliente:Tcliente;persona:Tpersona;telefono:Ttelefono);



begin
dm := Tconsultas.Create(nil);
// dm.SQLQuery1.Close;
with dm.qpersona do
begin
Close;
SQL.Clear;
SQL.Add('insert into persona (nombreyape, domicilio) values (:nombreyape, :domicilio);');

ParamByName('nombreyape').AsString := persona.nombreyape;
ParamByName('domicilio').AsString := persona.domicilio;


ExecSQL();
end;

with dm.qcliente do
begin
Close;
SQL.Clear;
SQL.Add('insert into clientes (idpersona, cuit, cuil) values (last_insert_id(), :cuit, :cuil);');

ParamByName('cuit').AsInteger := cliente.cuit;
ParamByName('cuil').AsInteger := cliente.cuil;
ExecSQL();
end;
with dm.qtelefono do
begin
Close;
SQL.Clear;
SQL.Add('insert into telefono (idtipotel, numero)values (:idtipotel,:numero);');
ParamByName('idtipotel').AsInteger := telefono.tipo;
ParamByName('numero').AsInteger := telefono.numero;
ExecSQL();
end;
with dm.aux do
begin
Close;
SQL.Clear;
SQL.Add('select last_insert_id() from persona;');

end;
with dm.aux2 do
begin
Close;
SQL.Clear;
SQL.Add('select last_insert_id() from telefono;');
end;
with dm.qpt do
begin
Close;
SQL.Clear;
SQL.Add('insert into personatel (idpersona,idtelefono) values (:idp,:idt);');
ParamByName('idp').AsInteger:=dm.aux.ExecSQL();
ParamByName('idt').AsInteger:=dm.aux2.ExecSQL();
ExecSQL();
end;

// dm.SQLQuery1.ExecSQL();
end;