J'aurais besoin d'aide dans la structure de 2 table :
Au départ je pensais inscrire la question en dure dans l'interface (sans la stocker), mais celle-ci doit pouvoir être modifié depuis une interface de configuration.
Mon besoin est de retrouver les Notes par Type de question, par Secteur et par date (Code Question est associer avec un Type qui est Unique mais peut avoir plusieurs Secteurs)
Pour l'instant :
j'ai une table Question :
avec les colonnes CodeQuestion, Type, Secteur, Question
Une table ResultatQuestion :
avec les colonnes CodeQuestion, Date, Résultat
Le premier problème c'est au niveau de l'optimisation du stockage : j'ai un Grand Varchar pour stocker la question et beaucoup de secteurs (du coup je pense à créer une 3eme table: CodeQuestion, Secteur).
Le deuxième problème c'est Secteur, je sais pas trop quoi en faire (je suis obligé de le mettre également dans ResultatQuestion ?)
secteur peut avoir plusieurs réponses, relation 1,n = clé étrangère dans la table réponse.
Juste un petit point en plus : J'ai également besoin de charger les questions dans l'interface en fonction du secteur
Une Question concerne plusieurs secteur, et un secteur peut avoir plusieurs Questions,
Donc il me faut également ajouter id_secteur dans question et id_question dans secteur ?
EDIT : Pourquoi ce type de Structure ne fonctionnerais pas ?
et je fait un SELECT libelleQuestion WHERE type = 'type' AND Secteur = 'secteur' (avec une jointure sur id_question) pour charger les question.
et quand l'utilisateur répond un INSERT INTO (id question, date, secteur, valeur)
par la suite je peux retrouver toutes les information dont j'ai besoin car id_question n'a qu'un type, secteur et date sont deja enregistrés dans la table réponse.
Pour éviter les problèmes de faute de frappe ou d'orthographe.
Pour assurer l'intégrité référentielle des données.
Pour alléger la taille de l'index lié à la clé étrangère.
Pour alléger la taille de la table.
Et sûrement d'autres que j'oublie...
Je te conseille la lecture du cours MySQL et du doc Conception BDD (cf ma signature).
Je me suis un peu mélangé les idées hier soir, je comprend mieux
Je part sur le fait de rajouter la Table type :
- Pour ce qui est d'éviter les faute de frappe ou d’orthographe tu assigne "sécurité" à un chiffre
- Pour ce qui est de la taille comme tu assigne le mot "sécurité" à "1" par exemple pour chaque question le type sera "1" et pas "sécurité"
- Pour assuré l'intégrité référentielle cela te permet d'avoir un sécurité en plus sur les données inscrit par l'utilisateur, par exemple il ne peut pas ajouter une question si le type n'existe pas...
Merci Benzouye,
Au travers de cette exemple j'ai bien compris comment structurer la BDD,
je vais quand même faire un tour sur le cours MySQL dans ta signature
Je vais également revoir d'autres parties de ma BDD
Encore quelques questions :
- Maintenant si l'administrateur veut rajouter une question : je doit faire un INSERT INTO et l'admin va entrer le type = sécurité au lieux de 1 ? il y a un moyen de gérer cette equivalence en SQL ou je doit passer par de VB ?
- J'ai une BDD globale pour le projet et parfois j'ai des fonctionnalités à réaliser (comme le questionnaire) qui sont part et n'ont aucun lien avec les autres tables de la BDD. Pour ses fonctionnalités à part il y a un avantage à créer plusieurs petites BDD ? peut être a regrouper ce qui est ensemble
- id_reponse sert'il a quelques chose ? comme c'est une finalité et qu'il n'est utilisé nulle part ailleurs ?
l'admin va entrer le type = sécurité au lieux de 1 ? il y a un moyen de gérer cette equivalence en SQL ou je doit passer par de VB ?
Dans ton formulaire de saisie d'une question (en VB ?) tu vas proposer une combobox proposant la liste des types (SELECT * FROM type) avec en valeur l'id et en label le libellé ... Après validation du formulaire tu récupères donc l'id ... pareil pour les secteurs ...
DeveloSt a écrit:
Pour ses fonctionnalités à part il y a un avantage à créer plusieurs petites BDD ?
Normalement on crée une BDD par application pouvoir faire des sauvegardes et des imports de manière bien cloisonnée.
DeveloSt a écrit:
id_reponse sert'il a quelques chose ?
Une réponse est une entité à part entière, définie par une date et une note. En conception de BDD, une entité dispose d'une clé primaire ... c'est la méthode (cf. doc "Conception BDD" de ma signature) ... En collant à la méthode tu permets de l'évolutivité et de la maintenabilité ...
Merci beaucoup Benzouye pour tes réponses et ton aide,
Lors de ma formation j'avais eu un cours SQL/VB d'une trentaine d'heure avec la création d'une BDD météorologique ou j'ai du créer une interface permettant de visualiser les données météorologique de 2018, mais tous ce qui avait été intégrité, structure d'une base de données évolutivité, maintenabilité, (fonction avancées (Transaction, TRIGGERS, Procédure stockée, requête paramétré...) est passé à côté de la formation.
et quand tu est seul développeur sur un projet conséquent (sans personne qui puisse valider la structure, ou donner des conseils sur la manière de développer...).
J’aurai du demander de l'aide avant de commencer le développement sur le forum ou lire des tuto sur SQL.
Du coup je continue ma formation en même temps :)
Après en développement c'est pas toujours évident de revenir en arrière (si tu change ça ou cela, il faut modifier beaucoup de chose derrière) et quand tu as des contraintes de temps...
Je vais faire en sorte de continuer en utilisant les bonnes méthodes afin d'avoir une application complète avec toute les fonctionnalités demandées et revenir après sur l'optimisation de ce que j'ai déjà réalisé.
Avec cette structure si je veux ajouter une question il me faut faire 2 INSERT INTO :
- 1 dans Question
- 1 dans question_secteur
et ici je peux utiliser une transaction (COMMIT ou ROLLBACk) ?
Il y a un truc qui me rend encore un peu perplexe :
- si le type de question n'existe pas, il me faut le détecter (je peux utiliser un TRY CATCH ?), créer un boite de dialogue de confirmation (voulez vous ajouter un nouveaux type de question ?), si oui faire un 3eme INSERT INTO dans la table type_question, et réexécuter les deux première ?
On parle ici plus de conception d'IHM (UI design) que de base de données
Selon moi, Tu dois avoir un formulaire où l'utilisateur saisit le libellé de la question, choisit le type de la question (combobox) et choisit le ou les secteurs associés (combobox à choix multiple).
Si le type n'existe pas dans la liste actuelle, il faut que ton formulaire propose d'en ajouter un "à la volée" avec un bouton qui ouvre un nouveau formulaire de création de type où l'utilisateur saisit le libellé et enregistre, ce qui ferme le formulaire secondaire, insère en base le nouveau type et met à jour la combobox des types du formulaire principal.
Le fonctionnement peut-être similaire pour le secteur.
Exemple :
Après il n'y a ici pas besoin de transaction selon moi. Si l'insertion de la question échoue tu ne pourras pas continuer de toute façon ...
Donc, dans l'ordre :
si l'utilisateur a besoin de créer un nouveau type, tu fais l'INSERT INTO type_question et tu mets à jour le formulaire principal (dans un bloc try/catch pour gérer les erreurs)
si l'utilisateur a besoin de créer un nouveau secteur, tu fais l'INSERT INTO secteur et tu mets à jour le formulaire principal (dans un bloc try/catch pour gérer les erreurs)
quand l'utilisateur valide le formulaire principal tu fais l'INSERT INTO question et tu récupères l'id créé (dans un bloc try/catch pour gérer les erreurs)
ensuite tu fais l'INSERT INTO question_secteur (dans un bloc try/catch pour gérer les erreurs)
Ok, c'est tellement plus poussé que ce que j’avais prévus de faire (mais je pense que pour cette partie je suis obligé)
J'ai déja une interface de ce type :
Je pensais faire un truc du genre pour rester simple :
Pour avoir une cohérence avec le reste de mon interface, mais c'est vrai que si j'utilise des checkBoxList je peux créer une question sur plusieurs secteur en même temps.
(Après ce n'est pas moi qui décide )
C'est quelle logiciel que tu utilise pour faire les schémas ?
Il faut par contre bien penser à mettre en place un mécanisme qui met à jour les ListBox (ou CheckedListBox) de l'onglet question à chaque modification dans les autres onglets ...
DeveloSt a écrit:
j'ai lus que ça prenait moins d'espace de stockage de stocker '1' en varchar que 1 en int ?
C'est vrai ... mais un peu trop réducteur voire trompeur ...
Un INT prend en effet 4 octets en mémoire, et un VARCHAR prend sa longueur (nombre de caractères) + 1 octet en mémoire.
Donc pour stocker la valeur 1 dans une colonne INT tu occuperas en effet 4 octets alors que pour un VARCHAR tu en occuperas seulement 2.
Sauf que ... Si tu dois stocker des nombres entiers, tu vas peut-être avoir à un moment besoin de stocker la valeur 1234 ... et là ton VARCHAR va peser 5 octets ... alors que l'INT toujours 4 ...
Après si tu ne dois stocker qu'un petit nombre d'enregistrements, un TINYINT UNSIGNED te permettra de stocker une valeur allant de 0 à 255 pour seulement 1 octet occupé ...
Cela pour dire qu'il est toujours important de bien choisir le type des colonnes en fonction des valeurs qui vont y être stockées ... mais utiliser un type "caractère" pour stocker une valeur "numérique" reste absurde dans tous les cas ... (de mémoire nous avons déjà eu la même discussion sur les types DATE ...).
(On s'écarte un peu des BDD) mais J'ai un petit problème, j'ai mis l'indexe IdQuestion en Auto-Increment, le problème est que maintenant je ne sait pas comment la récupérer dans le code pour associer ma question a un secteur
Protected Sub TabSecurite_RowCommand(ByVal sender As Object, ByVal e As GridViewCommandEventArgs)
If e.CommandName.Equals("AddNew") Then
Dim AddQuestion As TextBox = DirectCast(TabSecurite.FooterRow.FindControl("AddQuestion"), TextBox)
Dim AddType As DropDownList = CType(TabSecurite.FooterRow.FindControl("AddType"), DropDownList)
Dim AddIlot As ListBox = CType(TabSecurite.FooterRow.FindControl("Addsecteur"), ListBox)
con.Open()
Dim cmd As New MySqlCommand("insert into question_securite(question, id_type) values('" + AddQuestion.Text + "', '" + AddType.SelectedValue + "')", con)
cmd.ExecuteNonQuery()
For Each i As Integer In Addsecteur.GetSelectedIndices()
Dim cmd2 As New MySqlCommand("insert into question_ilot(id_question, id_secteur) values('" + ID QUETION A RECUP +"', '" + Addsecteur.Items(i).Value + "')", con)
cmd2.ExecuteNonQuery()
Next
con.Close()
Securite()
End If
End Sub
EDIT : Avec MYSQL on peut utiliser MAX pour retrouver la plus grande valeur, mais si l'utilisateur suppr la question numéros 2 qu'est ce qu'il se passe ?
EDIT 2 : Finalement il y a deja un methode toute faite : "LAST_INSERT_ID()"
Par contre sa deviens vite n'importe quoi avec les id (c'est pas dans l'ordre ), suffit de supprimer la ligne qui comprend la question avec l'id 2 pour qu'on a un trou dans les id 1 ... 3 ... 4 ....
Je pense que fonctionnellement ça ne change rien, mais quand on a pas l’habitude que ce n'est pas carré ça fait bizarre. (tu as juste envie de tous remettre dans l'ordre mais comme tout est liée tu ne peux pas)
Un id auto incrémenté n'a pas vocation à avoir un sens pour un humain, ce n'est pas un ordre ... il est juste là pour assurer l'intégrité référentielle ...
Si tu es maniaque et que tu veux afficher des numéros dans l'ordre de tes lignes (1, 2, 3, 4, 5, 6, etc.), rien ne t'empêche d'ajouter une colonne dans le tableau affichant les données avec un incrément créé lors de l'affichage dans la boucle ...
Avec MySQL, si vraiment tu veux un numéro dans l'ordre, tu peux faire un truc comme ceci :
SELECT
@numligne := @numligne + 1 AS ligne,
T.id_table,
T.colonne1,
T.colonne2
FROM matable T, ( SELECT @numligne := 0 ) N
ORDER BY T.id_table
con.Open()
Dim cmd As New MySqlCommand("INSERT INTO question_securite(question, id_type) VALUES('" + AddQuestion.Text + "', '" + AddType.SelectedValue + "')", con)
cmd.ExecuteNonQuery()
For Each i As Integer In AddIlot.GetSelectedIndices()
Dim cmd2 As New MySqlCommand("insert into question_secteur(id_question, id_secteur) values(LAST_INSERT_ID(), '" + AddIlot.Items(i).Value + "')", con)
cmd2.ExecuteNonQuery()
Next
con.Close()
Je viens de tomber sur cette erreur lors de mon premier INSERT INTO
System.IndexOutOfRangeException : 'Parameter index is out of range
Je pense que c'est à cause de l'auto_increment ? j'ai laissé cette indexe en INT et pour l'intant l'id monte que jusqu'à 15 :?
EDIT : c'est bizarre car la requete marche bien quand je l'execute directement avec la console
C'est une erreur VB qui n'a rien à voir avec MySQL ...
Je ne connais pas bien VB, mais je pense que l'erreur vient de ta variable i que tu utilises dans la boucle for.
A un moment de la boucle tu dois tomber sur un Items(i) qui n'existe pas ... à poster dans le forum .NET
PS : regarde aussi pour faire des requêtes préparées plutôt que de concaténer tes valeurs avec des guillemets (qui plus est inutiles autour de nombres ...
OK c'est juste que visual studio m'indique que c'est au moment ou j’exécute la requête
EDIT : Ca ne me le fait pas tous le temps, j'essaye de retrouver le cas mais j'ai l'impression que c'est vraiment aléatoire
EDIT : J'ai trouvé c'est quand mes phrase contient des caractère du style " ' "(je pense qu'il faut que je passe par des requête paramétré pour éviter l'erreur.)
- Requête préparée et paramétrée c'est la même chose ?
Par contre j'ai l’impression que c'est beaucoup plus long que de juste concaténer.
(mais à ce que j'ai compris il y a beaucoup d'avantage de le faire)
- Quand exactement utiliser une transaction en SQL ?
par exemple : lors de la connexion d'un utilisateur je fait 4 INSERT INTO, Ici je doit en utiliser 1 ? Parceque je le gère actuellement uniquement avec des TRY CATCH
Try
' ouvrir la connexionD
Dim cmd3 As New MySqlCommand("INSERT INTO ...", con)
cmd3.ExecuteNonQuery()
Catch
End Try
Try
Dim cmd3 As New MySqlCommand("INSERT INTO ...", con)
cmd3.ExecuteNonQuery()
Catch
End Try
Try
Dim cmd3 As New MySqlCommand("INSERT INTO ...", con)
cmd3.ExecuteNonQuery()
Catch
End Try
Try
Dim cmd3 As New MySqlCommand("INSERT INTO ...", con)
cmd3.ExecuteNonQuery()
Catch
End Try
con.Close()
C'est en effet plus long, mais c'est aussi plus lisible et plus précis dans la syntaxe ... tu sais de quel type de données tu parles ...
Le plus gros avantage est contre les injections SQL, et aussi sur le contrôle des types de données.
Concernant les transactions, leur utilité est avérée lorsque plusieurs requêtes sont liées et dépendantes. L'échec d'une doit annuler toutes les autres. Exemple : un mouvement bancaire qui insere une ligne dans la table des mouvements et modifie le solde dans la table client. Si la mise à jour du solde échoue pour une raison ou une autre il faut aussi annuler l'insertion du mouvement... pour garder la cohérence des données.
Dans ton exemple de création d'utilisateurs, déjà ton code ne fait rien en cas d'erreur... il faudrait au moins qu'il s lèvent une erreur pour avertir l'utilisateur. La transaction pourrait être utile ici si l'échec d'un INSERT compromet la cohérence de ta base si les autres INSERT ont réussi. Ce qui semble être le cas... si un des INSERT échoue, il faut ROLLBACK et prévenir l'utilisateur que son action a échoué...
J'ai essayé de gérer des question dupliqué depuis VB, finalement il suffit de mettre la colonne en Unique depuis SQL
Sinon, est-ce qu'il y a un moyen plus efficace que de mettre des INSERT INTO / UPDATE dans un for ? (j'avais entendu que ce n'était pas trop conseillé.)
For Each i As Integer In AddIlot.GetSelectedIndices()
Dim cmd2 As New MySqlCommand("insert into question_secteur(id_question, id_secteur) values(LAST_INSERT_ID(), '" + AddIlot.Items(i).Value + "')", con)
cmd2.ExecuteNonQuery()
Next
EDIT : Pour les réponses j'ai mis les colonne (id_question, id_ilot et date) en unique
je regarde si il y a un erreur puis fait un update.
Protected Sub EnvoyerReponse_Click(sender As Object, e As EventArgs)
For i As Integer = 0 To DataTabQuestion.Items.Count - 1 Step 1
Dim id_ilot As String = CType(DataTabQuestion.Items(i).FindControl("id_ilot"), Label).Text
Dim id_question As String = CType(DataTabQuestion.Items(i).FindControl("id_question"), Label).Text
Dim reponse As RadioButtonList = CType(DataTabQuestion.Items(i).FindControl("RéponseQuestion"), RadioButtonList)
MsgBox(id_question)
MsgBox(id_ilot)
con.Open()
Try
'Commande pour MAJ de la BDD
Dim cmd As New MySqlCommand("INSERT INTO reponse_securite(id_question, id_ilot, date, note) VALUE('" + id_question + "', '" + id_ilot + "', STR_TO_DATE('" + DateJ.Text + "', '%d/%c/%Y'), '" + reponse.SelectedValue + "')", con)
cmd.ExecuteNonQuery()
Catch
Dim cmd As New MySqlCommand("UPDATE reponse_securite SET note = '" + reponse.SelectedValue + "' WHERE id_question = '" + id_question + "' AND id_ilot = '" + id_ilot + "' AND date = STR_TO_DATE('" + DateJ.Text + "', '%d/%c/%Y')", con)
cmd.ExecuteNonQuery()
End Try
con.Close()
Next
End Sub
Est ce qu'il y a moyen de récupérer un code d'erreur dans le try catch -exemple duplicated ) ?
Je vais utiliser les requetes parametrees, je ferais la maj une fois que toute cette partie sera fonctionnelle. Sinon j'enregistre la date dans le bon format
J'avais déjà entendu parler de requêtes preparées, mais j'avais complétement oublié.
Néanmoins j'ai jamais utilisé.
Tu créer la requête ou ? (Pour l'instant je m'aide complétement de workbench pour creer mes tables), j'imagine qu'il faut creer la requête a partir de la.
Puis pour integrer des valeurs des variable depuis VB (cela a aussi un rapport avec des requêtes paramétrées ?)
Je vais m'informer, je pense qu'avec une recherche Internet ca va pas être compliqué (ou sinon je reposte )
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr
Site Internet : https://devst.go.yj.fr