none
Comment obtenir la liste des tables d'une base Access ? RRS feed

  • Question

  • Bonjour à tous

    Je souhaite remplir une Combobox avec la liste des tables d'une base Access.

    J'ai écrit le code suivant : 

    Private Sub TestToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TestToolStripMenuItem.Click
            Mycommand.CommandText = "SHOW TABLES"
            MyConnexion.Open()
            Dim myReader As OleDbDataReader = Mycommand.ExecuteReader()
            ComboBox2.DropDownStyle = ComboBoxStyle.DropDownList
            Do While myReader.Read()
                ComboBox2.Items.Add(myReader.GetString(0))
            Loop
            myReader.Close()
            MyConnexion.Close()
        End Sub

    Je reçois l'erreur suivante au niveau "

    Do While myReader.Read()

    :" Instruction SQL non valide; 'DELETE', 'INSERT', 'PROCEDURE', 'SELECT' ou 'UPDATE' attendus"

    Comment puis-je contourner cette erreur ?


    Pierre Allemand

    mardi 17 avril 2012 14:33

Réponses

  • Bonjour,

    Une confusion avec un autre dialecte SQL ? C'est la première fois que je vois une instruction SHOW TABLES dans le contexte d'une base Access (cela ne serait pas du MySQL ou Oracle ?).

    Je suggère http://msdn.microsoft.com/fr-fr/library/system.data.oledb.oledbconnection.getoledbschematable.aspx qui permet de récupérer la liste en interrogeant le fournisseur OleDb plutôt que directement la base... L'exemple de code présent sur cette page (non testé) est donc :

    Public Function GetSchemaTable(ByVal connectionString As String) _
        As DataTable
    
        Using connection As New OleDbConnection(connectionString)
            connection.Open()
    
            Dim schemaTable As DataTable = _
                connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, _
                New Object() {Nothing, Nothing, Nothing, "TABLE"})
            Return schemaTable
        End Using
    End Function
    Qui retourne donc un datatable avec les colonnes indiquées ici : http://msdn.microsoft.com/fr-fr/library/kcax58fh.aspx. TABLE_NAME sera le nom de la table. Eventuellement tester peut-être TABLE_TYPE (qui indique si c'est une table ou une vue).

    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".



    mardi 17 avril 2012 16:28
    Modérateur
  • 1) Effectivement c'était dans votre code original pour lire la liste des tables mais ce n'est effectivement plus utile, cette lecture étant faite via GetOleDbSchema. Ligne à supprimer.

    2,3) Effectivement le but de cette fonction est de pouvoir récupérer la description des différents objets présents dans une base (pas forcément uniquement les tables). La complexité est sans doute que cela expose un mécanisme général qui va fonctionner sur toutes les sources de données OleDb (et pas seulement avec Access) et peut-être la volonté d'en exposer toutes les possibilités. C'est l'un des intérêts aussi de faire une fonction. Si vous mettez cela dans une fonction, vous "oubliez" ces détails sans intérêts dans votre code principal ou pouvez créer votre propre version plus restrictive si vous le souhaitez.

    4) Ce n'est pas une commande. C'est juste une chaine de caractère comme toute autre chaine que l'on passerait à une fonction. Ici "TABLE" permet de dire que l'on ne veut que les tables (c'est effectivement redondant mais on peut s'en passer apparemment cf code ci-dessous). "TABLE_NAME" utilisé plus loin est le nom de la colonne dans laquelle on va retrouver le nom d'une table en particulier.

    5) Sur quel critère ? Par exemple cela pourrait donner :

        Private Function GetTables(ByVal ConnectionString As String) As DataTable
            Dim Tables As DataTable
            Using cnn As New OleDbConnection(ConnectionString)
                cnn.Open()
                Tables = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
            End Using
            ' Filtrer
            Tables.DefaultView.RowFilter = "TABLE_NAME LIKE 'A*'"
            Return Tables
        End Function
    
        Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
            Dim ConnectionString As String = "Provider=Microsoft.Jet.OleDb.4.0;Data Source=" & Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\Authors.mdb"
            ComboBox1.DataSource = GetTables(ConnectionString)
            ComboBox1.DisplayMember = "TABLE_NAME"
        End Sub
    A noter que c'est bien un filtre. Les lignes concernant les autres tables sont toujours présentes dans l'objet DataTable et pourrait être "réactivées". L'objet DataTable est en gros l'équivalent d'un "recordset" que vous avez sans doute déjà utilisé précédemment.

    La doc technique est ici : http://msdn.microsoft.com/fr-fr/library/aa735598.aspx (ADO.NET). Partir peut-être de http://msdn.microsoft.com/fr-fr/library/63bf39c2.aspx (tableau des technos .NET). Après c'est vraiment de la doc de référence donc il est sans doute difficile de trouver sans avoir au moins une idée de ce que vous recherchez.

    On pourrait même créer qq chose comme OleDbConnection.GetTables en utilisant les "méthodes d'extension" (qui est un simple artifice permettant de transformer une fonction MaFonction(MonObjet) en un appel à MonObjet.MaFonction le but principal étant de faciliter l'accès à cette fonction (car on n'a pas à se souvenir du nom de la fonction, comme avec les "vraies" méthodes, on tape le nom de l'objet et cette méthode nous est proposée par VS bien qu'elle ne fasse pas réellement parti de cet objet). Mais ignorer sans doute cela pour l'instant.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".



    jeudi 19 avril 2012 11:13
    Modérateur

Toutes les réponses

  • Bonjour,

    Une confusion avec un autre dialecte SQL ? C'est la première fois que je vois une instruction SHOW TABLES dans le contexte d'une base Access (cela ne serait pas du MySQL ou Oracle ?).

    Je suggère http://msdn.microsoft.com/fr-fr/library/system.data.oledb.oledbconnection.getoledbschematable.aspx qui permet de récupérer la liste en interrogeant le fournisseur OleDb plutôt que directement la base... L'exemple de code présent sur cette page (non testé) est donc :

    Public Function GetSchemaTable(ByVal connectionString As String) _
        As DataTable
    
        Using connection As New OleDbConnection(connectionString)
            connection.Open()
    
            Dim schemaTable As DataTable = _
                connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, _
                New Object() {Nothing, Nothing, Nothing, "TABLE"})
            Return schemaTable
        End Using
    End Function
    Qui retourne donc un datatable avec les colonnes indiquées ici : http://msdn.microsoft.com/fr-fr/library/kcax58fh.aspx. TABLE_NAME sera le nom de la table. Eventuellement tester peut-être TABLE_TYPE (qui indique si c'est une table ou une vue).

    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".



    mardi 17 avril 2012 16:28
    Modérateur
  • Une confusion avec un autre dialecte SQL ? C'est la première fois que je vois une instruction SHOW TABLES dans le contexte d'une base Access (cela ne serait pas du MySQL ou Oracle ?).

    Ça provient d'ici : http://sql.toutestfacile.com/maitriser_les_bases_de_donnees/premieres_requetes_sql_1.php5

    Pour le reste de votre réponse, je suis en train de tester.


    Pierre Allemand

    mardi 17 avril 2012 17:12
  • 1) Je ne vois pas trop l'intérêt de créer une fonction dans mon cas. Mais quand j'utilise simplement les 2 lignes suivantes :

    Private Sub TestToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TestToolStripMenuItem.Click

    Dim schemaTable As DataTable = Myconnexion.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"}) Return schemaTable

    End Sub

    dans lesquelles j'utilise la connection "Myconnexion" déjà ouverte, je reçois une erreur sur "Return" qui ne doit être employé qu'avec une fonction.

    Si j'utilise la fonction, je ne vois pas l'usage de "connectionString". Est-ce que je dois remplacer ça par mon string de connection ?

    2) Comment remplir une ComboBox avec une dataTable ?


    Pierre Allemand

    mardi 17 avril 2012 21:58
  • Ok le lien indique bien "pour MySQL" ce qui explique que l'instruction n'était pas reconnue.

    La fonction pourrait par exemple retourner directement une liste de chaines qui correspondent aux noms des tables. L'intérêt est de pouvoir la réutiliser facilement ailleurs.

    "Return" sert à indiquer la valeur de retour dans une fonction. Dans un sub (pas de valeur de retour) cette instruction est effectivement sans objet.

    Oui regarder la fonction, elle ouvre une connection à la base pour demander la liste des tables (il faut donc une chaine de connexion pour savoir sur quelle base ouvrir la connexion). Sinon utiliser la connexion que vous avez déjà.

    Pour remplir la combo essayez :

    MyCombo.DataSource=schemaTable
    MyCombo.DisplayMember="TABLE_NAME"


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".

    mercredi 18 avril 2012 08:12
    Modérateur
  • J'ai essayé la sub ci-dessous selon vos indications :

     Private Sub TestToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TestToolStripMenuItem.Click
            MyConnexion.Open()
            Dim myReader As OleDbDataReader = Mycommand.ExecuteReader()
            Dim schemaTable As DataTable = MyConnexion.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"})
            ComboBox2.DataSource = schemaTable
            ComboBox2.DisplayMember = "TABLE"
            MyConnexion.Close()
        End Sub

    La ComboBox2 se remplit bien, mais avec 6 lignes identiques : "System.Data.DataRowView"

    Que dois-je faire ?


    Pierre Allemand

    mercredi 18 avril 2012 12:38
  • Mon exemple utilise "TABLE_NAME" et non pas "TABLE".


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".

    mercredi 18 avril 2012 17:13
    Modérateur
  • OK, j'ai remplacé "TABLE" par  "TABLE_NAME" et j'ai la liste des tables. J'avoue que je ne comprends strictement rien à la syntaxe proposée et cela me gène beaucoup :

    1) MyCommand.ExecuteReader() : je comprends qu'on demande (execute) à un mécanisme appelé "reader" de lire quelque chose. Mais on ne lui dit pas quoi. Je ne comprends pas.

    2) MyConnexion.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"}) Je passe sur les "GetOleDbSchemaTable" qui, je pense, signifie "récupère (get) par le système spécialisé dans la connection avec les bases de données (OleDb) la liste (Schema) des tables.

    C'est, à mon humble avis assez redondant (c'est un peu comme si on précisait aussi dans la commande : "Visual Basic", "Microsoft", etc... mais je me trompe sûrement). 

    3) OleDbSchemaGuid Qu'est-ce que c'est lourd ! Ce n'est plus de la redondance, c'est carrément du bégaiement ! (je ne peux pas croire que toutes ces précisions sont indispensable, bien que je ne comprenne pas leur usage).

    4) Bouquet final : {Nothing, Nothing, Nothing, "TABLE"}. Là, je comprendrais peut-être s'il y avait 1 seul exemple. Malheureusement, je n'en ai pas trouvé dans l'aide correspondant à chacun des termes. De plus, le "TABLE" ici n'est pas le même que le "TABLE_NAME" utilisé plus bas. J'avais toujours cru que les termes entre guillemets n'étaient jamais utilisés comme commandes : je m'étais trompé...

    Si je peux me permettre une remarque de vieux routier qui a commencé l'informatique avec les cartes perforées : l'arrivée progressive de possibilités de stockage de plus en plus vastes a fait oublier à Microsoft un principe fondamental : la concision, imposée autrefois par les capacités limitées en mémoire. Le résultat, c'est qu'on a remplacé l'élégance de la concision par un logorrhée invraisemblable, au détriment de la clarté.

    5) Finalement, j'en arrive à ma question : je voudrais filtrer les noms des tables de façon à ne lister que certaines d'entre elles. comment faire ?

    Question subsidiaire : Ou puis-je trouver une aide écrite traitant de ce genre de questions  (Et d'autres) ? (Aucun des ouvrages que j'ai consultés ne descend assez profondément dans la syntaxe pour être vraiment utile. Tous proposent des exemples assez affligeants dont le seul intérêt (mais seulement pour l'auteur) est qu'ils occupent la moitié de l'ouvrage mais sont sans réel intérêt pour le lecteur). Microsoft doit bien pourtant avoir des manuels techniques ?

    Cordialement.


    Pierre Allemand



    mercredi 18 avril 2012 21:21
  • 1) Effectivement c'était dans votre code original pour lire la liste des tables mais ce n'est effectivement plus utile, cette lecture étant faite via GetOleDbSchema. Ligne à supprimer.

    2,3) Effectivement le but de cette fonction est de pouvoir récupérer la description des différents objets présents dans une base (pas forcément uniquement les tables). La complexité est sans doute que cela expose un mécanisme général qui va fonctionner sur toutes les sources de données OleDb (et pas seulement avec Access) et peut-être la volonté d'en exposer toutes les possibilités. C'est l'un des intérêts aussi de faire une fonction. Si vous mettez cela dans une fonction, vous "oubliez" ces détails sans intérêts dans votre code principal ou pouvez créer votre propre version plus restrictive si vous le souhaitez.

    4) Ce n'est pas une commande. C'est juste une chaine de caractère comme toute autre chaine que l'on passerait à une fonction. Ici "TABLE" permet de dire que l'on ne veut que les tables (c'est effectivement redondant mais on peut s'en passer apparemment cf code ci-dessous). "TABLE_NAME" utilisé plus loin est le nom de la colonne dans laquelle on va retrouver le nom d'une table en particulier.

    5) Sur quel critère ? Par exemple cela pourrait donner :

        Private Function GetTables(ByVal ConnectionString As String) As DataTable
            Dim Tables As DataTable
            Using cnn As New OleDbConnection(ConnectionString)
                cnn.Open()
                Tables = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
            End Using
            ' Filtrer
            Tables.DefaultView.RowFilter = "TABLE_NAME LIKE 'A*'"
            Return Tables
        End Function
    
        Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
            Dim ConnectionString As String = "Provider=Microsoft.Jet.OleDb.4.0;Data Source=" & Environment.GetFolderPath(Environment.SpecialFolder.Desktop) & "\Authors.mdb"
            ComboBox1.DataSource = GetTables(ConnectionString)
            ComboBox1.DisplayMember = "TABLE_NAME"
        End Sub
    A noter que c'est bien un filtre. Les lignes concernant les autres tables sont toujours présentes dans l'objet DataTable et pourrait être "réactivées". L'objet DataTable est en gros l'équivalent d'un "recordset" que vous avez sans doute déjà utilisé précédemment.

    La doc technique est ici : http://msdn.microsoft.com/fr-fr/library/aa735598.aspx (ADO.NET). Partir peut-être de http://msdn.microsoft.com/fr-fr/library/63bf39c2.aspx (tableau des technos .NET). Après c'est vraiment de la doc de référence donc il est sans doute difficile de trouver sans avoir au moins une idée de ce que vous recherchez.

    On pourrait même créer qq chose comme OleDbConnection.GetTables en utilisant les "méthodes d'extension" (qui est un simple artifice permettant de transformer une fonction MaFonction(MonObjet) en un appel à MonObjet.MaFonction le but principal étant de faciliter l'accès à cette fonction (car on n'a pas à se souvenir du nom de la fonction, comme avec les "vraies" méthodes, on tape le nom de l'objet et cette méthode nous est proposée par VS bien qu'elle ne fasse pas réellement parti de cet objet). Mais ignorer sans doute cela pour l'instant.


    Please always mark whatever response solved your issue so that the thread is properly marked as "Answered".



    jeudi 19 avril 2012 11:13
    Modérateur
  • Merci beaucoup pour votre réponse claire et complète.

    Et bravo pour votre attitude très professionnelle en face de la mienne qui l'était beaucoup moins, mais qui peut s'expliquer par une journée très chargée... C'est quelquefois difficile de continuer à supporter le stress du travail au-delà d'un certain âge...


    Pierre Allemand

    jeudi 19 avril 2012 15:31