Mis a jour de table : erreur " violation de clé primaire"

Signaler
Messages postés
5
Date d'inscription
jeudi 28 juillet 2005
Statut
Membre
Dernière intervention
16 août 2005
-
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
-
Bonjour,
Je suis nouveau en programmation et delphi.

j'ai un bouton qui permet de mettre à jour la table 'lieu2' à partir de la table 'lieu1'
Les tables 'lieu2'et 'lieu1' sont les mêmes( même champs, même enrégistrement ....)



procedure TForm1.Button1Click(Sender: TObject);
begin
qrlieu1.Open;
while not (qrlieu1.eof) do
begin
qrInsertlieu2.ParamByName('li_id').AsInteger := qrlieu1.fieldbyname('li_id').AsInteger;
qrInsertlieu2.ParamByName('vi_id').AsInteger := qrlieu1.fieldbyname('vi_id').AsInteger;
qrInsertlieu2.ParamByName('li_adr1').Asstring := qrlieu1.fieldbyname('li_adr1').Asstring;
qrInsertlieu2.ExecSQL;
qrcommit.ExecSQL;
qrlieu1.Next;
end;
end;



Explication :qrlieu1 est un Query: ( select * from lieu1)
qrInsertlieu2 est le bouton dont le query
est : insert into lieu2 (li_id,vi_id,li_adr1)
values(:li_id, :vi_id, :li_adr1).


Ce programme marche bien seulement si la table lieu2 est vide.
Si la table Lieu2 contient des enregistrements, le programme plante: " violation de clé primaire"


Donc je cherche à faire un test pour contôler les enregistrements: Il y aura insertion seulement si il ya des nouveaux enrégistrements ou modification des enrégistrements dans la table Lieu1.

Je n'arrive pas à faire ce test qui marche.

Merci pour l'aide.

10 réponses

Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Le mieux ne serait-il pas de rechercher les enregistrements présents dans Lieu1 mais pas dans lieu2, puis d'ajouter les nouveaux enregistrements trouvés ?

Dans ce cas, la requête pour qrLieu1 serait celle-ci :
/* Sélectionner tous les enregistrements de la table lieu */

SELECT * FROM Lieu1 L
/*qui n'existent pas */
WHERE NOT EXISTS
/* dans lieu2*/
(SELECT * FROM lieu2 M
/* quand on compare les champs li_id*/
WHERE L.li_id = M.li_id)

Il ne reste plus qu'à ajouter ces enregistrements à lieu2 par une instruction INSERT.

Et pour les enregistrements modifiés :
/* selectionner dans la table lieu1 les enregistrements*/
SELECT * FROM lieu1 L
/* pour lesquels*/
WHERE EXISTS
/*dans la table lieu2 */
(SELECT * FROM lieu2 M
/* le champ li_id est identique*/
WHERE L.li_id = M.li_id
/* et les champs vi_id OU li_adr1 sont différents*/
AND ((L.vi_id <> M.vi_id) or (L.li_adr1 <> M.li_adr1))

NB : les commentaires peuvent être laissés dans la requête. Ca peut servir pour plus tard si tu as besoin de t'y replonger.

Ces requêtes ne sont pas forcément optimisées mais elles ont au moins le mérite de fonctionner avec le SQL local qui est un sous-ensemble de la norme.

Rappel : si tu utilises la BDE, un composant TBatchMove peut effectuer les deux opérations en une passe si l'on met son mode de fonctionnement en bmAppendUpdate.


<HR color=#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
jeudi 28 juillet 2005
Statut
Membre
Dernière intervention
16 août 2005

Merci pour le conseil.
Mais en fait j'avais résumé le problème en disant lieu1 et lieu2.

Mais Lieu1 et lieu2 sont les mêmes tables dans 2 bases différentes.
La mis à jour se fait à partir de la table 'Lieu' d'une base distante 'X' vers la table 'Lieu' dans une base Locale 'Y'. Dans ce cas j'ai du mal à utiliser votre requête sous delphi.


Merci d'avance .
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Question subsidiaire : quelles bases de données utilisez-vous ?


<HR color=#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
jeudi 28 juillet 2005
Statut
Membre
Dernière intervention
16 août 2005

j'utilise le base INTERBASE 7 de borland avec BDE.
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Dans l'aide en ligne (\Program Files\Fichiers communs\Borland Shared\BDE\LocalSql.hlp), à l arubrique Table Names, on peut lire ceci :
Local SQL also supports BDE aliases in table references. To do this, enclose the BDE alias in colons, prefix the table name with that colon-enclosed alias name, and then enclose the whole reference in quotation marks. For example:


SELECT * FROM ":DBDEMOS:Customer.db"

Donc, il suffit de faire précéder le nom d'une table par le nom de l'alias entouré du charactère ":" et le tout (alias+nom de la table) par des guillemets doubles.



<HR color=#008000>



Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
jeudi 28 juillet 2005
Statut
Membre
Dernière intervention
16 août 2005

Bonjour,
De retour sur mon probrème de mis à jour :
Finalement j'ai choisi la méthode de BatchMove "TableDestination.BatchMove(TableSource Mode)"pour faire le transfère.
Ici TableDestination='lieu2' et tableSource='Lieu1'
Mais mon souci est que la TableSource n'est pas une seule table (plusieurs table de même nom)c'est à dire plusieur Alias de base de donnée dans le quel je dois selectionner le tablesource(lieu1) et faire la mis à jour vers la table 'lieu2' avec BacthMove.
Comment faire ? utiliser une variable ou paramètre pour la table'lieu1' mais BatchMove va t' il le prendre ?
Merci encore si vous avez quelque suggestion.
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
Personnellement, j'opterais plutôt pour l'utilisation du composant TBacthMove qui permet un controle plus fin des opérations.
Dans les deux cas, il conviendra de réaliser une boucle qui examinera chaque composant du type TTable et devra traiter uniquement celles qui en ont besoin.
Pour cela, il suffit de "marquer" chaque composant TTable concerné en utilisant sa propriété Tag par exemple.

Voici le code que je propose comme exemple à l'appui. Dans un premier temps, il faut indiquer quelle table doit mettre à jour telle autre table

procedure TForm1.FormCreate(Sender: TObject);
begin
{La propri&#233;t&#233; Tag des tables &#224; mettre &#224; jour doit contenir 
   l'adresse de la table source.
   Ex: Table2 sera mis &#224; jour &#224; l'aide de Table1}
  Table2.Tag :=  Longint(Table1);
  Table4.Tag := Longint(Table3);
 end ;

Enuite, au moment du traitement :

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  T: TTable;
begin
//pour chaque composant sur la fiche
for i : = 0to ComponentCount - 1do
//si c'est un TTable
if Components[i] is TTable then
begin
//pour faciliter le code qui suit
      T :=  Components[i]  as  TTable;
      //si la propri&#233;t&#233; Tag est diff&#233;rente de z&#233;ro
if T.Tag <> 0then
{ on met &#224; jour avec la table situ&#233; &#224; l'adresse contenue
          dans la propri&#233;t&#233; tag }
         T.BatchMove(TTable(T.Tag), batAppendUpdate);
    end;
end;

Je pense que cela r&#233;pond mieux &#224; tes exigences.



<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
5
Date d'inscription
jeudi 28 juillet 2005
Statut
Membre
Dernière intervention
16 août 2005

Bonjour,
Merci toujours pour votre aide.
J'aimerais vous signaler que je n'ai toujours pas bien compris votre exemple ci-dessous.( paut être parce que je suis débutant en programation)
Vous avez dit que la table "Table2 seras mis à jour par le table1"
mais vous encore fait mention de la table4 "Table4.Tag:= Longint(table3)" ??? est-ce un oublie ??
D'autre part mon problème est que la table de destination est seule et unique (table fixe)
Elle subit plusieurs mis à jours des plusieurs tables sources de même carctéristique.
Du coup je n'ai pas bien compris cette ligne:
" T.BatchMove(TTable(T.Tag), batAppendUpdate"
Puis que T doit être une table de destination statique mais elle intervient encore dans le parametre source de BacthMove.

procedure TForm1.FormCreate(Sender: TObject);
begin
{La propriété Tag des tables à mettre à jour doit contenir
l'adresse de la table source.
Ex: Table2 sera mis à jour à l'aide de Table1}
Table2.Tag : = Longint(Table1);
Table4.Tag := Longint(Table3);
end;

Merci encore
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
L'exemple de code que j'ai donné est purement fictif et uniquement destiné à des fins d'explication des principes.
D'autre part, il n'a jamais été mentionné que plusieurs tables devaient en alimenter une seule autre.
"Du coup je n'ai pas bien compris cette ligne:
"T.BatchMove(TTable(T.Tag), batAppendUpdate"
Puis que T doit être une table de destination statique mais elle intervient encore dans le parametre source de BacthMove.
"

La table en cours d'examen dans la boucle contient la référence (transtypée en longint pour être conforme à ce type de donnée) de la table qui doit servir à la mise à jour.
Pour améliorer la lisibilité du code, j'ai pris auparavant une référence sur la table à mettre à jour et j'ai affecté cette référence à T.
Donc TTable(T.Tag) correspond à l'opération inverse du transtypage effectué dans FormCreate (voir ci-dessus) et est destiné à transformer l'entier long en un objet de type TTable situé à l'adresse T.Tag.
Or, si vous avez suivi (je sais que ce n'est pas facile), TTable(T.Tag) correspond donc à la table Table1 dans le cas de Table2, Table4 dans le cas de Table3 dans le code que j'ai donné. Et comme la méthode BatchMove attend un TDataSet en premier argument, on réalise ainsi de manière dynamique le parcours de l'ensemble des TDataSet recensés sur la fiche.

Mais, compte tenu que plusieurs tables vont en alimenter une seule, alors la technique indiquée n'est pas la bonne.
Pourquoi ne pas faire la démarche inverse ? A savoir, renseigner la propriété Tag des tables servant à la mise à jour et laisser à zéro la propriété Tag de la table de destination ?
Cette fois-ci, vous pouvez même réaliser cette opération en mode conception.
Imaginons que toutes les tables servant à mettre à jour se nomment Table2..TableN et la table de destination se nomme Table1.
En modifiant la propriété Tag des composants Table2..TableN et en affectant 1, le code suggéré est celui-ci :
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  T: TTable;
begin
{ parcourir tous les composants de la fiche
    (ou du module de donn&#233;es) }
for i :=  0 to  ComponentCount - 1do
begin
{ si le composant courant est une table
    et que sa propri&#233;t&#233; Tag est &#233;gale &#224; 1 (par convention)}
if (Components[i] is TTable) and (Components[i].Tag  = 1) then
begin
{ on prend une r&#233;f&#233;rence sur le composant actuel }
       T :=  Components[i]  as  TTable;
       { On met &#224; jour Table1 avec la table r&#233;f&#233;renc&#233;e par T }
       Table1.BatchMove(T, batAppendUpdate);
    end;
  end;
end;

J'espère que, cette fois-ci, la description du problème était la bonne


<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
4297
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
31
L'exemple de code que j'ai donné était purement fictif et uniquement destiné à des fins d'explication des principes.
D'autre part, il n'a jamais été mentionné que plusieurs tables devaient en alimenter une seule autre ou alors, je n'ai pas lu vos commentaires dans le bon sens. Mais peu importe...

"Du coup je n'ai pas bien compris cette ligne:
"T.BatchMove(TTable(T.Tag), batAppendUpdate"
Puis que T doit être une table de destination statique mais elle intervient encore dans le parametre source de BacthMove.
"

La table en cours d'examen dans la boucle contient la référence (transtypée en longint pour être conforme à ce type de donnée) de la table qui doit servir à la mise à jour.
Pour améliorer la lisibilité du code, j'ai pris auparavant une référence sur la table à mettre à jour et j'ai affecté cette référence à T.
Donc TTable(T.Tag) correspond à l'opération inverse du transtypage effectué dans FormCreate (voir ci-dessus) et est destiné à transformer l'entier long en un objet de type TTable situé à l'adresse T.Tag.
Or, si vous avez suivi (je sais que ce n'est pas facile), TTable(T.Tag) correspond donc à la table Table1 dans le cas de Table2, Table4 dans le cas de Table3 dans le code que j'ai donné. Et comme la méthode BatchMove attend un TDataSet en premier argument, on réalise ainsi de manière dynamique le parcours de l'ensemble des TDataSet recensés sur la fiche.

Mais, compte tenu que plusieurs tables vont en alimenter une seule, alors la technique indiquée n'est pas la bonne.
Pourquoi ne pas faire la démarche inverse ? A savoir, renseigner la propriété Tag des tables servant à la mise à jour et laisser à zéro la propriété Tag de la table de destination ?
Cette fois-ci, vous pouvez même réaliser cette opération en mode conception.
Imaginons que toutes les tables servant à mettre à jour se nomment Table2..TableN et la table de destination se nomme Table1.
En modifiant la propriété Tag des composants Table2..TableN et en affectant 1, le code suggéré est celui-ci :
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  T: TTable;
begin
{ parcourir tous les composants de la fiche
    (ou du module de donn&#233;es) }
for i :=  0 to  ComponentCount - 1do
begin
{ si le composant courant est une table
    et que sa propri&#233;t&#233; Tag est &#233;gale &#224; 1 (par convention)}
if (Components[i] is TTable) and (Components[i].Tag  = 1) then
begin
{ on prend une r&#233;f&#233;rence sur le composant actuel }
       T :=  Components[i]  as  TTable;
       { On met &#224; jour Table1 avec la table r&#233;f&#233;renc&#233;e par T }
       Table1.BatchMove(T, batAppendUpdate);
    end;
  end;
end;

J'espère que, cette fois-ci, la description du problème était la bonne

<HR color =#008000>
Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.