donnéesPilote de base de données
4. Les lignes résultantes ordonnées selon l’expression du mot clé order by forment le résultat de la requête.
6.3.4 Mise à jour d'une table
Les exemples précédents se contentaient de lister le contenu d'une table. Nous modifions notre programme de gestion de la base d'articles afin qu'il puisse modifier celle-ci. Le programme s'appelle sql. On lui passe en paramètre le nom DSN de la base d'articles à gérer. L'utilisateur tape directement des commandes SQL au clavier que le programme exécute comme le montrent les résultats qui suivent obtenus sur la base MySQL d'articles :
dos>csc /r:microsoft.data.odbc.dll sql.cs dos>sql mysql-articles
Accès aux bases de données 167
Requête SQL (fin pour arrêter) : select * from articles --- code,nom,prix,stock_actuel,stock_minimum --- a300 vélo 2500 10 5 b300 pompe 56 62 45 c300 arc 3500 10 20 d300 flèches - lot de 6 780 12 20 e300 combinaison de plongée 2800 34 7 f300 bouteilles d'oxygène 800 10 5
Requête SQL (fin pour arrêter) : select * from articles where stock_actuel<stock_minimum ---
code,nom,prix,stock_actuel,stock_minimum --- c300 arc 3500 10 20 d300 flèches - lot de 6 780 12 20
Requête SQL (fin pour arrêter) : insert into articles values ("1","1",1,1,1) Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles --- code,nom,prix,stock_actuel,stock_minimum --- a300 vélo 2500 10 5 b300 pompe 56 62 45 c300 arc 3500 10 20 d300 flèches - lot de 6 780 12 20 e300 combinaison de plongée 2800 34 7 f300 bouteilles d'oxygène 800 10 5 1 1 1 1 1
Requête SQL (fin pour arrêter) : update articles set nom="2" where nom="1" Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles --- code,nom,prix,stock_actuel,stock_minimum --- a300 vélo 2500 10 5 b300 pompe 56 62 45 c300 arc 3500 10 20 d300 flèches - lot de 6 780 12 20 e300 combinaison de plongée 2800 34 7 f300 bouteilles d'oxygène 800 10 5 1 2 1 1 1
Requête SQL (fin pour arrêter) : delete from articles where code="1" Il y a eu 1 ligne(s) modifiée(s)
Requête SQL (fin pour arrêter) : select * from articles --- code,nom,prix,stock_actuel,stock_minimum --- a300 vélo 2500 10 5 b300 pompe 56 62 45 c300 arc 3500 10 20 d300 flèches - lot de 6 780 12 20 e300 combinaison de plongée 2800 34 7 f300 bouteilles d'oxygène 800 10 5
Requête SQL (fin pour arrêter) : select * from articles order by nom asc ---
code,nom,prix,stock_actuel,stock_minimum --- c300 arc 3500 10 20 f300 bouteilles d'oxygène 800 10 5
Accès aux bases de données 168
e300 combinaison de plongée 2800 34 7 d300 flèches - lot de 6 780 12 20 b300 pompe 56 62 45 a300 vélo 2500 10 5 Requête SQL (fin pour arrêter) : fin
Le programme est le suivant :
' options Option Explicit On Option Strict On ' espaces de noms Imports System Imports System.Data Imports Microsoft.Data.Odbc Imports System.Data.OleDb Imports System.Text.RegularExpressions Imports System.Collections Imports Microsoft.VisualBasic Module db3
Public Sub Main(ByVal args() As String) ' application console
' exécute des requêtes SQL tapées au clavier sur une
' table ARTICLES d'une base DSN dont le nom est passé en paramètre
Const syntaxe As String = "syntaxe : pg dsnArticles" ' vérification des paramètres
' a -t-on 2 paramètres If args.Length <> 1 Then ' msg d'erreur Console.Error.WriteLine(syntaxe) ' fin Environment.Exit(1) End If 'if ' on récupère le paramètre
Dim dsnArticles As String = args(0) ' chaîne de connexion à la base
Dim connectString As String = "DSN=" + dsnArticles + ";" ' préparation de la connexion à la bd
Dim articlesConn As OdbcConnection = Nothing
Dim sqlCommand As OdbcCommand = Nothing
Try
' on tente d'accéder à la base de données
articlesConn = New OdbcConnection(connectString) articlesConn.Open()
' on crée un objet command
sqlCommand = New OdbcCommand("", articlesConn) 'try
Catch ex As Exception ' msg d'erreur
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")")) ' libération des ressources
Try
articlesConn.Close() Catch
End Try
Environment.Exit(2) End Try 'catch
' on construit un dictionnaire des commandes sql acceptées
Dim commandesSQL() As String = {"select", "insert", "update", "delete"} Dim dicoCommandes As New Hashtable
Dim i As Integer
For i = 0 To commandesSQL.Length - 1 dicoCommandes.Add(commandesSQL(i), True) Next i
' lecture-exécution des commandes SQL tapées au clavier
Dim requête As String = Nothing ' texte de la requête SQL
Dim champs() As String ' les champs de la requête
Dim modèle As New Regex("\s+")
' boucle de saisie -exécution des commandes SQL tapées au clavier
While True
' pas d'erreur au départ
Dim erreur As Boolean = False
' demande de la requête
Accès aux bases de données 169
requête = Console.In.ReadLine().Trim().ToLower() ' fini ?
If requête = "fin" Then
Exit While
End If
' on décompose la requête en champs
champs = modèle.Split(requête) ' requête valide ?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' msg d'erreur
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete") ' requête suivante
erreur = True
End If
If Not erreur Then
' préparation de l'objet Command pour exécuter la requête
sqlCommand.CommandText = requête ' exécution de la requête
Try
If champs(0) = "select" Then
executeSelect(sqlCommand) Else executeUpdate(sqlCommand) End If Catch ex As Exception ' msg d'erreur
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")")) End Try
End If
End While
' libération des ressources
Try articlesConn.Close() Catch End Try Environment.Exit(0) End Sub
' exécution d'une requête de mise à jour
Sub executeUpdate(ByVal sqlCommand As OdbcCommand) ' exécute sqlCommand, requête de mise à jour
Dim nbLignes As Integer = sqlCommand.ExecuteNonQuery() ' affichage
Console.Out.WriteLine(("Il y a eu " & nbLignes & " ligne(s) modifiée(s)"))
End Sub
' exécution d'une requête Select
Sub executeSelect(ByVal sqlCommand As OdbcCommand) ' exécute sqlCommand, requête select
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader() ' Exploitation de la table récupérée
' affichage des colonnes
Dim ligne As String = "" Dim i As Integer
For i = 0 To (myReader.FieldCount - 1) - 1 ligne += myReader.GetName(i) + "," Next i
ligne += myReader.GetName(i)
Console.Out.WriteLine((ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf + ligne + ControlChars.Lf + "".PadLeft(ligne.Length, "-"c) + ControlChars.Lf))
' affichage des données
While myReader.Read()
' exploitation ligne courante
ligne = "" For i = 0 To myReader.FieldCount - 1 ligne += myReader(i).ToString + " " Next i ' affichage Console.WriteLine(ligne) End While
' libération des ressources
myReader.Close()
End Sub End Module
Nous ne commentons ici que ce qui est nouveau par rapport au programme précédent : • Nous construisons un dictionnaire des commandes sql acceptées :
Accès aux bases de données 170
Dim commandesSQL() As String = {"select", "insert", "update", "delete"} Dim dicoCommandes As New Hashtable
Dim i As Integer
For i = 0 To commandesSQL.Length - 1 dicoCommandes.Add(commandesSQL(i), True) Next i
ce qui nous permet ensuite de vérifier simplement si le 1er mot (champs[0]) de la requête tapée est l'une des quatre commandes acceptées :
' requête valide ?
If champs.Length = 0 Or Not dicoCommandes.ContainsKey(champs(0)) Then
' msg d'erreur
Console.Error.WriteLine("Requête invalide. Utilisez select, insert, update, delete") ' requête suivante
erreur = True
End If 'if
• Auparavant la requête avait été décomposée en champs à l'aide de la méthode Split de la classe RegEx :
Dim modèle As New Regex("\s+") ....
' on décompose la requête en champs champs = modèle.Split(requête)
Les mots composant la requête peuvent être séparés d'un nombre quelconque d'espaces.
• L'exécution d'une requête select n'utilise pas la même méthode que celle d'une requête d'une mise à jour (insert, update, delete). Aussi doit-on faire un test et exécuter une fonction différente pour chacu de ces deux cas :
' préparation de l'objet Command pour exécuter la requête sqlCommand.CommandText = requête
' exécution de la requête Try
If champs(0) = "select" Then
executeSelect(sqlCommand) Else executeUpdate(sqlCommand) End If 'try Catch ex As Exception ' msg d'erreur
Console.Error.WriteLine(("Erreur d'exploitation de la base de données (" + ex.Message + ")")) End Try
L'exécution d'une requête SQL peut générer une exception qui est ici gérée.
• La fonction executeSelect reprend tout ce qui a été vu dans les exemples précédents.
• La fonction executeUpdate utilise la méthode ExecuteNonQuery de la class OdbcCommand qui rend le nombre de lignes affectées par la commande.
6.3.5 IMPOTS
Nous reprenons l'objet impôt construit dans un chapitre précédent :
' options
Option Strict On Option Explicit On
' espaces de noms
Imports System
Public Class impôt
' les données nécessaires au calcul de l'impôt ' proviennent d'une source extérieure
Private limites(), coeffR(), coeffN() As Decimal
' constructeur
Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal) ' on vérifie que les 3 tablaeux ont la même taille
Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length If Not OK Then
Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
End If
Accès aux bases de données 171 Me.limites = LIMITES Me.coeffR = COEFFR Me.coeffN = COEFFN End Sub ' calcul de l'impôt
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Integer)
As Long
' calcul du nombre de parts
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2 Else
nbParts = CDec(nbEnfants) / 2 + 1 End If
If nbEnfants >= 3 Then
nbParts += 0.5D End If
' calcul revenu imposable & Quotient familial
Dim revenu As Decimal = 0.72D * salaire Dim QF As Decimal = revenu / nbParts ' calcul de l'impôt limites((limites.Length - 1)) = QF + 1 Dim i As Integer = 0 While QF > limites(i) i += 1 End While ' retour résultat
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function End Class
Nous lui ajoutons un nouveau constructeur permettant d'initialiser les tableaux limites, coeffR, coeffN à partir d'une base de données ODBC : Imports System.Data Imports Microsoft.Data.Odbc Imports System.Collections ... ' constructeur 2
Public Sub New(ByVal DSNimpots As String, ByVal Timpots As String, ByVal colLimites As String, ByVal
colCoeffR As String, ByVal colCoeffN As String)
' initialise les trois tableaux limites, coeffR, coeffN à partir
' du contenu de la table Timpots de la base ODBC DSNimpots
' colLimites, colCoeffR, colCoeffN sont les trois colonnes de cette table
' peut lancer une exception
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
' la requête SELECT
Dim selectCommand As String = "select " + colLimites + "," + colCoeffR + "," + colCoeffN + " from " + Timpots
' tableaux pour récupérer les données
Dim tLimites As New ArrayList Dim tCoeffR As New ArrayList Dim tCoeffN As New ArrayList
' on tente d'accéder à la base de données
impotsConn = New OdbcConnection(connectString) impotsConn.Open()
' on crée un objet command
sqlCommand = New OdbcCommand(selectCommand, impotsConn) ' on exécute la requête
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader() ' Exploitation de la table récupérée
While myReader.Read()
' les données de la ligne courante sont mis dans les tableaux
tLimites.Add(myReader(colLimites)) tCoeffR.Add(myReader(colCoeffR)) tCoeffN.Add(myReader(colCoeffN)) End While
' libération des ressources
myReader.Close() impotsConn.Close()
' les tableaux dynamiques sont mis dans des tableaux statiques
Me.limites = New Decimal(tLimites.Count) {} Me.coeffR = New Decimal(tLimites.Count) {}
Accès aux bases de données 172
Me.coeffN = New Decimal(tLimites.Count) {} Dim i As Integer
For i = 0 To tLimites.Count - 1
limites(i) = Decimal.Parse(tLimites(i).ToString()) coeffR(i) = Decimal.Parse(tCoeffR(i).ToString()) coeffN(i) = Decimal.Parse(tCoeffN(i).ToString()) Next i
End Sub
Le programme de test est le suivant : il reçoit en arguments les paramètres à transmettre au constructeur de la classe impôt. Après avoir construit un objet impôt, il fait des calculs d'impôt à payer :
Option Explicit On Option Strict On ' espaces de noms Imports System Imports Microsoft.VisualBasic ' pg de test Module testimpots
Sub Main(ByVal arguments() As String) ' programme interactif de calcul d'impôt
' l'utilisateur tape trois données au clavier : marié nbEnfants salaire
' le programme affiche alors l'impôt à payer
Const syntaxe1 As String = "pg DSNimpots tabImpots colLimites colCoeffR colCoeffN"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' vérification des paramètres du programme
If arguments.Length <> 5 Then ' msg d'erreur Console.Error.WriteLine(syntaxe1) ' fin Environment.Exit(1) End If 'if
' on récupère les arguments
Dim DSNimpots As String = arguments(0) Dim tabImpots As String = arguments(1) Dim colLimites As String = arguments(2) Dim colCoeffR As String = arguments(3) Dim colCoeffN As String = arguments(4) ' création d'un objet impôt
Dim objImpôt As impôt = Nothing
Try
objImpôt = New impôt(DSNimpots, tabImpots, colLimites, colCoeffR, colCoeffN) Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message)) Environment.Exit(2)
End Try
' boucle infinie
While True
' au départ pas d'erreurs
Dim erreur As Boolean = False
' on demande les paramètres du calcul de l'impôt
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim() ' qq chose à faire ?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' vérification du nombre d'arguments dans la ligne saisie
Dim args As String() = paramètres.Split(Nothing) Dim nbParamètres As Integer = args.Length If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2) erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
Accès aux bases de données 173
' marié
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n")) erreur = True
End If
' nbEnfants
nbEnfants = 0 Try
nbEnfants = Integer.Parse(args(1)) If nbEnfants < 0 Then
Throw New Exception End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument nbEnfants incorrect : tapez un entier positif ou nul") erreur = True End Try ' salaire salaire = 0 Try
salaire = Integer.Parse(args(2)) If salaire < 0 Then
Throw New Exception End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument salaire incorrect : tapez un entier positif ou nul")
erreur = True
End Try
End If
If Not erreur Then
' les paramètres sont corrects - on calcule l'impôt
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire).ToString + " F"))
End If
End While End Sub End Module
La base utilisée est une base MySQL de nom DSN mysql-impots :
C:\mysql\bin>mysql --database=impots --user=admimpots --password=mdpimpots Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5 to server version: 3.23.49-max-debug Type 'help' for help.
mysql> show tables; +---+ | Tables_in_impots | +---+ | timpots | +---+
mysql> select * from timpots; +---+---+---+ | limites | coeffR | coeffN | +---+---+---+ | 12620 | 0 | 0 | | 13190 | 0.05 | 631 | | 15640 | 0.1 | 1290.5 | | 24740 | 0.15 | 2072.5 | | 31810 | 0.2 | 3309.5 | | 39970 | 0.25 | 4900 | | 48360 | 0.3 | 6898 | | 55790 | 0.35 | 9316.5 | | 92970 | 0.4 | 12106 | | 127860 | 0.45 | 16754 | | 151250 | 0.5 | 23147.5 | | 172040 | 0.55 | 30710 | | 195000 | 0.6 | 39312 | | 0 | 0.65 | 49062 | +---+---+---+
L'exécution du programme de test donne les résultats suivants :
dos>D:\data\devel\vbnet\poly\chap6\impots>vbc /r:system.data.dll /r:microsoft.data.odbc.dll /r:system.dll /t:library impots.vb
Accès aux bases de données 174
dos>vbc /r:impots.dll testimpots.vb
dos>test mysql-impots timpots limites coeffr coeffn
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000 impôt=22506 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 2 200000 impôt=33388 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 3 200000 impôt=16400 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 3 300000 impôt=50082 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 3 200000 impôt=22506 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :