Récupérer le type d'une colonne access

Bonjour,

Je suis entrain de faire une macro qui met à jour une base de données avec des informations entrées dans un fichier excel.

Pour cela j'utilise le code :

Fonction :

Option Explicit
Public Cnx As Object, Rst As Object
'http://tatiak.canalblog.com/
'https://forum.excel-pratique.com/viewtopic.php?p=761466#p761466

Sub Connect_Access(Accdb As String)
    Set Cnx = CreateObject("ADODB.Connection")
    Cnx.Provider = "MSDASQL"
    Cnx.Open "DRIVER={Microsoft Access Driver (*.mdb, *.accdb)}; DBQ=" & Accdb & ";"
    Set Rst = CreateObject("ADODB.Recordset")
End Sub

Function Select_Db(Req As String, Optional Head As Byte = 1) As Variant
Dim T As Variant, Rcd As Variant
Dim lig As Long, col As Long, i As Long, j As Long

    On Error GoTo errhdlr
    ReDim Rcd(1 To 1, 1 To 1)
    Rst.Open Req, Cnx, 3
    lig = Rst.RecordCount
    If lig > 0 Then
        Rst.MoveFirst
        T = Rst.GetRows
        col = Rst.Fields.Count
        ReDim Rcd(1 To lig + Head, 1 To col)

        For j = 0 To col - 1
            Rcd(1, j + 1) = Rst.Fields(j).Name
            For i = 0 To lig - 1
                Rcd(i + 1 + Head, j + 1) = IIf(IsNull(T(j, i)), 0, T(j, i))
            Next i
        Next j
    End If
    Select_Db = Rcd
    Exit Function

errhdlr:
    Rcd(1, 1) = "Erreur n°" & Err.Number & vbCrLf & Err.Description
    Select_Db = Rcd
End Function

Sub Close_Cnx(Optional X As Byte)
    On Error Resume Next
    If X > 0 Then Rst.Close
    Cnx.Close
    Set Cnx = Nothing
    Set Rst = Nothing
End Sub

Function ExisteFichier(S As String) As Boolean
Dim tatiak As Object
    Set tatiak = CreateObject("Scripting.FileSystemObject")
    ExisteFichier = tatiak.FileExists(S)
End Function

Code de MAJ :

Sub update_database()
    Dim der_ligne As Integer, der_colonne As Integer
    Dim Req As String, str As String
    Dim T As Variant

    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationAutomatic
    Application.StatusBar = "Initialisation"

    'Initialisation de certaines variables
    feuil = "cde en cours"
    der_ligne = ThisWorkbook.Sheets(feuil).Cells(Rows.Count, 2).End(xlUp).Row
    annee = Left(Right(ThisWorkbook.Name, 9), 4)
    der_colonne = ThisWorkbook.Sheets(feuil).Cells(13, Columns.Count).End(xlToLeft).Column
    der_ligne2 = der_ligne + (der_ligne - 13)

    Application.StatusBar = "Copie des données initiales"
    'On copie les lignes originales de la base de donnée
    Req = "SELECT * FROM [cde en cours] WHERE "

    For i = 14 To der_ligne
        Req = Req & "`numero de cde` = " & ThisWorkbook.Sheets(feuil).Cells(i, 4).Value

        If i <> der_ligne Then Req = Req & " OR " Else Req = Req & " ORDER BY [numero de cde]"
    Next

    Connect_Access ThisWorkbook.Path & "\Suivi cde " & annee & ".accdb" 'Fonction
    T = Select_Db(Req, 0) ' avec 1 si on veut l'entête et 0 sinon   Fonction
    ThisWorkbook.Sheets(feuil).Range("B" & der_ligne + 1).Resize(UBound(T, 1), UBound(T, 2)) = T
    Close_Cnx 'Fonction

    If Left(Cells(der_ligne + 1, 2).Value, 6) = "Erreur" Then
        MsgBox "Une erreur est survenue lors de l'exécution de la mise à jours." & vbLf & "Veuillez supprimer une grande partie des lignes non modifiées puis réessayer.", vbCritical + vbOKOnly
        Cells(der_ligne + 1, 2).Delete
        Exit Sub
    End If

    Application.StatusBar = "Préparation des données"
    'On insert 3 colonnes
    ThisWorkbook.Sheets(feuil).Columns("B:D").Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove

    'on ajoute le champ dans la formule et un ";"
    For i = 5 To (der_colonne - 4 + 3)
        concatenation = concatenation & Split(Range(ThisWorkbook.Sheets(feuil).Cells(14, i), ThisWorkbook.Sheets(feuil).Cells(14, i)).Address, "$")(1) & "13;"";"";"
    Next

    'insérer la formule dans la cellule B13
    ThisWorkbook.Sheets(feuil).Range("B13").FormulaLocal = "=CONCATENER(" & concatenation & ")"

    'étendre la formule à toutes les lignes
    ThisWorkbook.Sheets(feuil).Range("B13").AutoFill Destination:=ThisWorkbook.Sheets(feuil).Range("B13:B" & der_ligne2), Type:=xlFillDefault

    'créé la liste pour simplifier la formule suivante
    ThisWorkbook.Names.Add Name:="Concatenation", RefersTo:=ThisWorkbook.Sheets(feuil).Range("B13:B" & der_ligne2)

    'insérer la formule pour trouver les valeurs uniques dans la cellule C13
    ThisWorkbook.Sheets(feuil).Range("C14").FormulaLocal = "=SIERREUR(INDEX(Concatenation; EQUIV(0;INDEX(NB.SI(C$13:$C13; Concatenation)+(NB.SI(Concatenation;Concatenation)<>1);0;0); 0));"""")"

    'étendre la formule à toutes les lignes
    ThisWorkbook.Sheets(feuil).Range("C14").AutoFill Destination:=ThisWorkbook.Sheets(feuil).Range("C14:C" & der_ligne2), Type:=xlFillDefault

    'On copie les VALEURS de la colonnes C afin d'enlever les cellules retournants une valeur vide
    ThisWorkbook.Sheets(feuil).Range("C14:C" & der_ligne2).Copy
    ThisWorkbook.Sheets(feuil).Range("D14").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False

    'On trouve la dernière ligne remplis de la colonne copiée
    Der_ligne_result = ThisWorkbook.Sheets(feuil).Cells(Rows.Count, 4).End(xlUp).Row

    For i = Der_ligne_result To 1 Step -1
        If Cells(i, 4).Value <> "" Then
            Der_ligne_result = i
            Exit For
        End If
    Next i

    'On détermine la dernière ligne utile
     If Der_ligne_result Mod 2 = 0 Then der_ligne_final = 13 + ((Der_ligne_result - 14) / 2) Else der_ligne_final = 13 + ((Der_ligne_result - 13) / 2) + 1

    'On supprime les ligne de valeur unique de références
    ThisWorkbook.Sheets(feuil).Range("D" & der_ligne_final & ":D" & Der_ligne_result).Value = ""

    'On cherche la nouvelle dernière colonne
    der_colonne2 = ThisWorkbook.Sheets(feuil).Cells(13, Columns.Count).End(xlToLeft).Column

    Application.StatusBar = "Mise à jours des données dans la base"
    'On update la table avec les nouvelles valeurs
    For i = 14 To der_ligne_final

        If ThisWorkbook.Sheets(feuil).Cells(i - passe, 4).Value = "" Then
            Exit For
        End If

        If InStr("1", ThisWorkbook.Sheets(feuil).Cells(i, 7).Value, (Split(ThisWorkbook.Sheets(feuil).Cells(i - passe, 4).Value, ";")(2)), 1) Then
            Req = "UPDATE [cde en cours] SET " & vbCrLf
            For j = 5 To (der_colonne2 - 4)
                If ThisWorkbook.Sheets(feuil).Cells(13, j).Value <> "numero de cde" Then
                    'conversion en chaine de caractère
                    str = ThisWorkbook.Sheets(feuil).Cells(i, j).Value

                    'ajout de la valeur à modifier
                    Req = Req & "`" & ThisWorkbook.Sheets(feuil).Cells(13, j).Value & "` = '" & str & "'"

                    'détection de fin de requête, si non ajouter un comparateur
                    If j < (der_colonne2 - 4) Then Req = Req & ", "
                End If '<> "numero de cde"
            Next 'j

            'ajout de la condition d'update
            Req = Req & " WHERE `numero de cde` = " & Split(ThisWorkbook.Sheets(feuil).Cells(i - passe, 4).Value, ";")(2)

            'MsgBox Req

            Connect_Access ThisWorkbook.Path & "\Suivi cde " & annee & ".accdb" 'Fonction
            Cnx.Execute Req
            Close_Cnx 'Fonction
        Else
            passe = passe + 1
        End If 'instr
    Next 'i

    Application.StatusBar = "Nettoyage de la feuille"
    'Nettoyage de la feuille
    ThisWorkbook.Sheets(feuil).Columns("B:D").Delete Shift:=xlToLeft
    ThisWorkbook.Sheets(feuil).Rows(der_ligne + 1 & ":" & der_ligne2).Delete
    ThisWorkbook.Save

    Application.ScreenUpdating = True
    Application.DisplayAlerts = True
    Application.EnableEvents = True
    Application.StatusBar = "Les données ont bien été mises à jours."
    Application.Calculation = xlCalculationAutomatic

End Sub

Dans ce code je détecte les lignes qui ont été changées, en comparant à la base de données ( sa risque de changer quand j'aurai trouvé comment détecter les cellules qui ont été changées, et non la ligne entière), puis je génère une requête de mise à jour de la BDD pour les lignes ayant été modifiées.

Le problème est que je ne détecte actuellement pas les type des colonnes et donc la mise à jours ne se fait pas car certaine données sont du mauvais type (je les envoie tous, actuellement, en chaîne de caractère 'xxxxx' donc les date et les nombre ne passe pas).

Si vous avez des code permettant d'obtenir le type de chaque colonne d'une BDD je suis preneur

Cordialement,

Arthur.

Bonjour Arthur,

J'utilise quelques macros pour typer mes données :

' ©------------------
' 017 Format Date SQL
' -------------------
Public Function SQL_Date(D As Range) As String
Dim Da As String
  Application.Volatile
  Da = "'" & Format(D.Cells.Value, "yyyymmdd") & "'" '"#"
  SQL_Date = Da
End Function
' ©------------------
' 017 Format Date SQL
' -------------------
Public Function SQL_ExcelDate(D As Range) As String
Dim Da As String
  Application.Volatile
  Da = D.Cells.Value
  MsgBox (Format(Right(Da, 2), "dd") & Format(Mid(Da, 2, 2), "mm") & Format(Left(Da, 4), "yyyy"))
  SQL_ExcelDate = Format(Right(Da, 2), "dd") & Format(Mid(Da, 2, 2), "mm") & Format(Left(Da, 4), "yyyy")
End Function
' ©-------------------
' 017 Format Texte SQL
' --------------------
Public Function SQL_Texte(D As Range) As String
Dim Da As String
  Application.Volatile
  Da = "'" & D.Cells.Value & "'"
  SQL_Texte = Da
End Function
Public Sub SQL_GetSchema()
Dim Requete As String
Dim t As String
Dim i As Integer
  i = 1
  Set rs = cnn.OpenSchema(adSchemaTables)
  While Not rs.EOF
    'Selection.Offset(i + 2, 0) = rs!TABLE_NAME
    rs.MoveNext
    i = i + 1
  Wend
End Sub
' ©---------------------------------------
' Recupere le type d'une colonne de la BDD à adapter selon besoin
' ----------------------------------------
Function TypeColonne(tName As String, cName As String) As String
    TypeColonne = UneRequete("SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE Table_Name='" & tName & "' AND column_name='" & cName & "'")
End Function
Function SQL_getPrimaryKeyName(tableName As String)
Dim S As String
   Set rs = cnn.OpenSchema(adSchemaPrimaryKeys, Array(Empty, Empty, tableName, Empty))
   S = rs("COLUMN_NAME")
 SQL_getPrimaryKeyName = S
End Function

J'espère que ces codes te seront utiles, ils fonctionnent pour SQLServer, pour Access, je ne sais pas.

Comme tu le lis, pour récupérer les types de données, tout se passe en SQL en interrogeant les schémas des tables systèmes.

Merci oxydum pour ces fonctions,

Je viens d'essayer

SELECT DATA_TYPE
FROM [cde en cours].COLUMNS

afin d'obtenir les DATA_TYPE de toutes mes colonnes de ma table "cde en cours". Malheureusement j'obtiens une erreur :

Fichier "C:\....\cde en cours.mdb" introuvable.

Aurais-tu une idée ?

J'ai aussi essayé :

SELECT DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE Table_Name='cde en cours'

mais cette fois ci il ne trouve pas la table INFORMATION_SHEMA.COLUMNS

:/

Finalement en bidoullant dans les paramètres d'access, j'ai pu afficher les table cachées et trouver la table qui regroupes les types des colonnes : MSyslMEXColumns

Je vais adapter le code que tu m'as filé et voir si sa passe

SELECT FieldName,DataType
FROM MSysIMEXColumns
ORDER BY Start

Nous affichera le nom des champs et leur type de données de la colonne (en code), le tout dans l'ordre du tableau.

Pour ceux intéresser

Merci pour ton retour

Rechercher des sujets similaires à "recuperer type colonne access"