• Aucun résultat trouvé

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 :