Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1785 connectés 

  FORUM HardWare.fr
  Programmation
  SQL/NoSQL

  Violation volontaire de contrainte d'unicité

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Violation volontaire de contrainte d'unicité

n°2259017
saint malo
Posté le 29-05-2015 à 03:39:40  profilanswer
 

Hello,

 

Je veux faire en sorte qu'un script insère des entrées dans ma base de données, sauf si ces entrées existent déjà (auquel cas il ne fait rien).

 

Le premier truc auquel j'ai pensé consistait à vérifier, entrée par entrée, si il n'y a pas déjà une ligne identique qui existe dans la table, si oui, ne rien faire, et si non, ajouter. Sauf qu'à chaque fois que mon script tournera, la plupart de ce que je tenterai d'insérer existera déjà (les nouveautés seront l'exception plutôt que la règle), du coup ça fait beaucoup de requêtes pour rien je trouve.

 

Le moyen le plus efficace (pour limiter le nombre de requêtes SQL) auquel j'arrive à penser consiste à mettre une contrainte d'unicité sur un ou plusieurs de mes champs, insérer tout un array de données (enfin plutôt une matrice comme ça, en fait) d'un coup dans la table, avec un IGNORE au milieu, dans l'espoir que pour les lignes qui n'existent pas déjà, elles soient ajoutés, et pour les autres qui existent déjà, rien ne se passe. (Si je dis pas de bêtises, il me semble que ça fait une seule grosse requête et que c'est mieux que plein de petites requêtes)

 

Questions :
- est-ce que ça marchera comme je le souhaite ? (autrement dit, je veux être sur que le fait qu'une ligne existe déjà n'empêchera pas les lignes qui sont nouvelles d'être ajoutées - ou encore autrement dit, que l'IGNORE ne s'applique que sur les lignes qui violent la contrainte d'unicité et pas sur toute la requête)
- est-ce que cette deuxième manière de faire est effectivement meilleure que la première ? (ou équivalente ou pire, et dans ces cas là, pourquoi ?) Et éventuellement, s'il y a une meilleure manière de faire, je prends.

 

Merci d'avance


Message édité par saint malo le 29-05-2015 à 10:22:42
mood
Publicité
Posté le 29-05-2015 à 03:39:40  profilanswer
 

n°2259027
flo850
moi je
Posté le 29-05-2015 à 09:45:14  profilanswer
 

-oui
-la seconde manière sera la plus efficace : une seule requete, tout est fait dans une seule transaction


---------------

n°2259029
saint malo
Posté le 29-05-2015 à 10:23:21  profilanswer
 

Taupe !
Merci :)

n°2260004
Tibar
Posté le 09-06-2015 à 17:49:21  profilanswer
 

Salut,
 
Sinon tu peux aussi faire ça en passant par une table temporaire, par exemple si tu as une table UTILISATEUR et que tu reçois des fichiers de nouveaux utilisateurs à insérer :  
INSERT INTO UTILISATEUR U
(
SELECT ID, NOM, PRENOM
FROM NOUVEAUX_UTILISATEURS NU
)
WHERE NOT EXISTS
(
SELECT 1
FROM UTILISATEUR U2
WHERE U2.ID = NU.ID  
-- a priori l'ID est unique
)


Message édité par Tibar le 09-06-2015 à 17:51:03
n°2260290
saint malo
Posté le 14-06-2015 à 03:40:55  profilanswer
 

J'ai pas bien compris ce que fait et comment fonctionne cette deuxième solution pour être honnête :)

 

Petite question sur la solution que j'ai envisagée : est-ce qu'il y a un moyen de dire à ma base "tente d'insérer ces 5 entrées, si les 5 sont insérées, très bien, mais si au moins l'une des 5 existe déjà, n'ajoute aucune des 5 entrées et dis moi laquelle était conflictuelle" ?

Message cité 1 fois
Message édité par saint malo le 14-06-2015 à 03:41:02
n°2260291
Tibar
Posté le 14-06-2015 à 03:56:39  profilanswer
 

Salut,

 

Tu peux regarder ici : http://stackoverflow.com/questions [...] sql-server

 

Il y les 3 méthode expliquées...

 

Ce que j'ai écrit se lit comme ça :
Insére dans la table UTILSATEUR les valeurs ID, NOM, PRENOM de la table temporaire (les donnees qui viennent d'un fichier, d'une autre table, etc.) où il n'existe pas dans la table UTILISATEUR d'identifiant identique à celui de la table temporaire.

n°2260292
Tibar
Posté le 14-06-2015 à 04:04:36  profilanswer
 

Sinon, pour répondre à ta demande, je ne vois pas de solution en une seule étape, le plus simple que je vois consisterait à compter si tu as des utilisateurs communs entre ta table finale et ta table temporaire, comme ceci par exemple :

 

Select count(*)
From UTILISATEUR U
where exists
(Select 1 from nouveau_utilisateur NU
Where NU.id = U.id)

 

Si tu as 0 c'est que tous tes nouveaux utilisateurs pourront être insérés sans problème, si tu plus de 0, c'est qu'au moins un utilisateur est déjà présent, et peux le trouver en faisant

 

Select *
From UTILISATEUR U
inner join NOUVEAU_UTILISATEUR NU on NU.id = U.id

 

Ça va te donner toutes les lignes de nouveaux utilisateurs que tu voudrais insérer dans utilisateur et qui existent déjà.

n°2260468
saint malo
Posté le 16-06-2015 à 18:38:57  profilanswer
 

Tibar a écrit :

Sinon, pour répondre à ta demande, je ne vois pas de solution en une seule étape, le plus simple que je vois consisterait à compter si tu as des utilisateurs communs entre ta table finale et ta table temporaire, comme ceci par exemple :
 
Select count(*)
From UTILISATEUR U
where exists
(Select 1 from nouveau_utilisateur NU
Where NU.id = U.id)
 
Si tu as 0 c'est que tous tes nouveaux utilisateurs pourront être insérés sans problème, si tu plus de 0, c'est qu'au moins un utilisateur est déjà présent, et peux le trouver en faisant  
 
Select *
From UTILISATEUR U
inner join NOUVEAU_UTILISATEUR NU on NU.id = U.id
 
Ça va te donner toutes les lignes de nouveaux utilisateurs que tu voudrais insérer dans utilisateur et qui existent déjà.


 
Oookay, mais là ça interdira les doublons aux IDs identiques, c'est ça ? Mettons que je veuille interdire deux utilisateurs aux noms identiques, il faut que je remplace " NU.id = U.id " par genre " NU.nom=U.nom " à chaque fois, c'est bien ça ? (vu que leurs ID respectifs dans la table U et la table temporaire NU ont pas de raison d'être les mêmes il me semble...)
Ou j'ai mal compris ?
 
Autre question, en php (avec pdo), je vais répéter plusieurs fois l'opération, je ferais mieux de :
1) recréer la table temporaire au début de chaque tour de boucle et mettre un drop temporary table à la fin à chaque fois ?
ou
2) créer la table temporaire une seule fois avant ma boucle, et à la fin de chacune de mes boucles, vider la table sans la drop ? (Et du coup, il vaudrait mieux la vider avec delete ou truncate ou une autre commande que je ne connais pas ?)
 
(A l'instinct, je dirais l'option 2 mais je demande quand même... :) )
 
Merci pour l'aide en tout cas !

n°2260472
Tibar
Posté le 16-06-2015 à 19:02:34  profilanswer
 

1) Il n'y a que toi qui puisses garantir qu'il n'y aura pas de doublons d'id, par construction de tes différentes tables.
2) Oui, si tu es sûr de ne pas avoir d'id en double et que tu ne veux pas de noms en double, tu peux remplacer u.id = NU.id par u.nom=NU.nom
3) Je ne comprends pas pourquoi tu parles de boucler, à moins que tu aies plusieurs fichiers avec des nouveaux utilisateurs. Dans ce cas, une bonne méthode est de créer un champ ID_FICHIER dans tes deux tables (temporaire et définitive), de créer une table FICHIER avec quelques champs ID_FICHIER, NOM_FICHIER, CHEMIN_FICHIER, DATE_RECEPTION, DATE_TRAITEMENT, de mettre toutes tes données dans ta table temporaire, ça te permettra de vérifier les doublons avant insertion définitive, de vérifier dans tes données sources que tu n'as pas d'erreur, puis de lancer l'intégration dans ta table définitive en ayant une trace de l'origine de cet utilisateur.
Les champs techniques ne sont pas assez souvent utilisés, et on en a toujours besoin un jour ou l'autre (par exemple date de création, date de mise à jour, processus de création, processus de mise à jour, et dans ton cas présent, fichier source).
Ça complexifie un tout petit peu les traitements (penser à modifier les dates, les processus etc., mais en cas de fausse manip ou de bug, la correction est souvent bien plus simple).


Message édité par Tibar le 16-06-2015 à 20:30:48
n°2260554
lasnoufle
La seule et unique!
Posté le 17-06-2015 à 16:28:31  profilanswer
 

saint malo a écrit :

Petite question sur la solution que j'ai envisagée : est-ce qu'il y a un moyen de dire à ma base "tente d'insérer ces 5 entrées, si les 5 sont insérées, très bien, mais si au moins l'une des 5 existe déjà, n'ajoute aucune des 5 entrées et dis moi laquelle était conflictuelle" ?


Ouaip, suffit de declarer une contrainte d'unicite et de laisser la base s'en occuper.
Bon ca depend p'tetre de ta base cela dit.
 
Pour ta derniere question, je dirais solution 2, mais bon a moins que tu fasse des millions de boucles, ca changera rien niveau perf je pense. La deux est p'tetre plus "propre" c'est tout.
 
Sinon +1 pour ce que dit Tibar


---------------
C'était vraiment très intéressant.
mood
Publicité
Posté le 17-06-2015 à 16:28:31  profilanswer
 

n°2260942
saint malo
Posté le 23-06-2015 à 12:35:40  profilanswer
 

Okidoki, merci pour vos réponses.
 
Petite question supplémentaire :
 

Tibar a écrit :


Select *
From UTILISATEUR U
inner join NOUVEAU_UTILISATEUR NU on NU.id = U.id


 
Avec ce code, est-ce que les lignes sortent dans le même ordre que dans la table NU (s'il y a plus d'une ligne déjà présente bien sur) ?
 
Si non, est-ce que j'ai un moyen de faire en sorte que ce soit le cas ?

n°2260954
Tibar
Posté le 23-06-2015 à 13:53:08  profilanswer
 

Salut,

 

En sql, tu peux utiliser ORDER BY pour trier le résultat de ta requête, ici tu fais
SELECT...
INNER JOIN...
ORDER BY NU.ID

 

Sans cette clause, le résultat peut te sembler trié mais le seul moyen d'être sûr est de mettre le ORDER BY, que tu peux compléter avec ASC pour l'ordre croissant ou DESC pour l'ordre décroissant.

n°2261125
saint malo
Posté le 25-06-2015 à 10:12:14  profilanswer
 

Bon du coup j'ai changé la construction de mon truc pour pouvoir utiliser un order by et faire en sorte que ça fonctionne. Merci à tous pour l'aide !


Message édité par saint malo le 25-06-2015 à 10:12:29
n°2261222
saint malo
Posté le 25-06-2015 à 18:15:42  profilanswer
 

Tibar a écrit :

...le plus simple que je vois consisterait à compter si tu as des utilisateurs communs entre ta table finale et ta table temporaire, comme ceci par exemple :
 
Select count(*)
From UTILISATEUR U
where exists
(Select 1 from nouveau_utilisateur NU
Where NU.id = U.id)
 
Si tu as 0 c'est que tous tes nouveaux utilisateurs pourront être insérés sans problème, si tu plus de 0, c'est qu'au moins un utilisateur est déjà présent, et peux le trouver en faisant  


Désolé, je reviens à la charge, j'ai un souci. Pour la requête où je compte le nombre de mots communs entre la table temporaire et la table permanente, j'utilise ce code (nb : j'utilise PDO) :
 

Code :
  1. $qryCondition = $bdd->prepare('SELECT COUNT(*)
  2.  FROM tableA a
  3.  WHERE EXISTS
  4.   (SELECT 1 from TMP_tableB b
  5.   WHERE a.mot = b.mot)'
  6. );
  7. $qryCondition->execute();
  8. $condition = $qryCondition->fetchAll();


J'espérais que $condition me donnerait le nombre de mots communs entre les deux tables, et donc pouvoir l'utiliser comme ça par exemple :
 

Code :
  1. if ($condition == 0) {
  2. //certaines actions
  3. } elseif ($condition > 0) {
  4. //d'autres actions
  5. }


Mais ça ne fonctionne visiblement pas. var_dump($condition); me donne ça à chaque fois, quelque soit le nombre de mots communs entre les deux tables, que ce soit plusieurs, un seul, ou même s'il n'y en a aucun :
 

Code :
  1. array(1) {
  2.  [0]=> array(2) {
  3.   ["COUNT(*)"]=> string(1) "0"
  4.   [0]=> string(1) "0"
  5.  }
  6. }


 
Une idée de ce qui ne va pas ? C'est le code php ?
 
Merci d'avance :)

n°2261243
Tibar
Posté le 26-06-2015 à 00:12:52  profilanswer
 

Sans passer par le code PHP, ta requête te donne quoi ?
 
Pour analyser, tu peux essayer de remplacer le  
SELECT COUNT(*)
par
SELECT *
 
et si tu veux aller plus en détail, tu fais  
 
SELECT *
FROM tableA A
INNER JOIN tableB B ON B.mot = A.mot
 
ça devrait te sortir toutes les lignes où tu as un mot identique entre la table tableA et la table tableB
 
Si ça ne sort rien, c'est qu'il n'y a aucun mot de A qui a une valeur identique dans B, si visuellement tu es sûr que c'est le cas, tu peux déjà mettre un ou deux exemples du contenu de tes tables, et ensuite essayer de jouer avec UPPER(A.mot) = UPPER(B.mot) (pour s'affranchir de la casse), les TRIM(A.mot) = TRIM(B.mot) pour les espaces avant et après tes valeurs (par exemple '  test' est différent de 'test'), ensuite on pourra regarder avec COLLATE pour s'affranchir des accents, des caractères spéciaux etc.
 
Si par contre ta requête renvoie un résultat, c'est que c'est le code PHP, et là je laisse la main ;-)

n°2261257
saint malo
Posté le 26-06-2015 à 09:39:41  profilanswer
 

Par construction, normalement, les majuscules et accents sont tous supprimés. Pour les espaces je ne sais pas, mais à priori en regardant ce qui est dans la base permanente, le problème ne vient pas de là non plus.
 
Quoi qu'il en soit, j'ai testé avec 0, 1 et plusieurs mots clés identiques entre les deux bases (et aussi avec 0, 1 et plusieurs mots clés qui sont dans l'une sans être dans l'autre) sur phpMyAdmin, et à chaque fois la requête donnait la bonne réponse, donc à priori le souci ne vient pas de la requête. Du coup je vais reposter la question dans la partie PHP de HFR. Merci ;)

n°2261271
saint malo
Posté le 26-06-2015 à 11:33:46  profilanswer
 

Par sécurité, je vais quand même mettre des trim partout où ça a du sens, on verra...


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  SQL/NoSQL

  Violation volontaire de contrainte d'unicité

 

Sujets relatifs
utiliser une jauge de contrainte avec arduinoViolation d'accès lors de la lecture de l'emplacement 0x0000001c
Macro VBA sur catia / Contrainte entre 2 plansTravail pour volontaire
Access Violation en écriture[Résolu] Probleme d'unicité dans un HashSet
Contrainte sur le prompt d’un InputBoxunicité d'une courbe de HIlbert en 3D a partir du motif initial
violation d'accès ou pas[MYSQL] retirer la contrainte unique d'un champ
Plus de sujets relatifs à : Violation volontaire de contrainte d'unicité


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR