Le forum (ô combien francophone) des utilisateurs de Powerbuilder.
Bonjour,
J'ai parcouru le forum et pas mal google , mais dans la plupart des cas , bien évidemment , on ne parle que de l'export vers excel.
Je dois , pour une application , me servir d'une feuille excel comme "base de données"
Je peux parcourir le fichier aisément mais je voudrais savoir si il existe un moyen "light" pour faire une recherche dans excel.
(J'utilise l'objet OLE)
Imaginons que j'ai comme colonnes : NOM AGE
Est-ce que pour trouver l'age de "JEAN" je dois parcourir tous les enregistrements 1 à 1 ?
Merci d'avance
Dernière modification par micmx (24-02-2009 06:53:08)
Hors ligne
Bonjour,
Pour lire des données depuis Excel, le plus simple est de définir une source de donnée ODBC de type Microsoft Excel Driver (dans PB, ouvrir le painter Db Profile puis utiliser ODBC Administrator dans le groupe Utilities de la section ODB ou passer par le panneau de config windows -> outils d'amin -> source de données ODBC). Ensuite dans PB, il suffit de déclarer une source de donnée de type ODBC utilisant celle déclarée dans ODBC.
SQLCB.DBMS = "ODBC" SQLCB.DBParm = "ConnectString='DSN=ma source xls',DelimitIdentifier='No'"
où "ma source xls" correspond à la source de donnée définie dans ODBC
Dans PB les feuilles Excel sont considérées comme des tables systèmes (est inexploitable en tant que tel), par contre il suffit de définir un nom pour un groupe de cellule pour que ce nom soit considéré comme une table utilisateur (la première ligne contient les noms de colonnes, les lignes suivantes les données). Ta feuille Excel est alors utilisée comme une vraie BDD. Tu peux faire autant de table utilisateur que tu le souhaites et tu peux faire des DW sur ces tables pour lire les données... L'avantage est que cela est très souple. tu peux avoir plusieurs tableaux (donc table) sur une même feuille Excel ou à l'inverse définir un nom pour l'ensemble d'une feuille est ainsi en faire une énorme table (attention, en fonction des versions d'Excel le nombre de colonne d'une feuille peut être supérieur au nombre supporté par SQL via ODBC. Dans ce cas un "select * from ma_zone" plantera avec un beau message ODBC "Trop de champs définis"...)
Hors ligne
il suffit d'importer le fichier exel via la datawindow.
1: tu enregistres le fichier exel en format TXT avec tabulation comme separateur
2: tu importes ce fichier via une datawindow, avec le meme nombre de colonnes.
3: tu en fais ce que tu veux.
Hors ligne
Bonjour,
De plus, dans la mesure ou tu effectues une connexion OLE sur le fichier EXCEL tu disposes de toutes les possibilités du language de macro d'excel.
Je n'ai jamais fait de recherches avec excel mais je suis sur que la fonction existe, il suffit de regarder la doc excel.
Hors ligne
Le problème avec une source de données ODBC , c'est qu'une ressource odbc doit être renseignée pour chaque fichier il me semble..
Dans le cas de mon application , tous les jours , un nouveau fichier excel est créé (même structure) , je dois aller le rechercher pour y trouver certaines informations.
Je n'arrive pas à l'importer via une datawindows
Pour l'instant la seule manière où j'arrive à exploiter les données est :
lo_ole = CREATE oleobject lo_ole.ConnectToNewObject("excel.application") lo_ole.workbooks.open(ls_nom_du_fichier) for li_i= 1 to 5 lc_val = char(65+li_i) lo_ole.Range(lc_VAL+"18").Select ls_texte = string(lo_ole.ActiveCell.Value) messagebox(lc_VAL+"18",ls_texte) next
Hors ligne
Bonjour, il faut utiliser la méthode Find : http://msdn.microsoft.com/en-us/library/bb178845.aspx
With Worksheets(1).Range("a1:a500") Set c = .Find(2, lookin:=xlValues) If Not c Is Nothing Then firstAddress = c.Address Do c.Value = 5 Set c = .FindNext(c) Loop While Not c Is Nothing And c.Address <> firstAddress End If End With
NB : pour trouver facilement une commande VBA, il suffit de le faire à la main dans Excel tout en enregistrant une macro. Ensuite il n'y a plus qu'à regarder le code de la macro.
Hors ligne
Désolé mais je ne suis pas familié avec excel...
en pb j'aurai
lo_ole.Range("e1:e800").Find("D574-60652-001-20")
Cette synthax ne provoque pas d'erreur , je suppose que c'est bon.
La methode find retourne un objet de type "Range" , hors , je ne vois pas comment le récupérer vu que ça n'existe pas en PB
Dans l'exemple que tu as cité , "c" est un objet de quel type ?
Merci beaucoup de ton aide
Dernière modification par micmx (18-02-2009 09:17:49)
Hors ligne
tu le récupères comme OLE également, un truc dans le genre devrait fonctionner
OLEobject lole_range Any la_value lole_range = lo_ole.Range("e1:e800").Find("D574-60652-001-20") // test IfNull( lole_range ) à faire la_value = lole_range.Cells(1, 1).Value
Hors ligne
Ah merci , on progresse.. seulement c'est étrange , ça fonctionne à moitier :s
Je m'explique :
à la ligne E575 , j'ai 1480292288.
Avec
lole_range = lo_ole.Range("E18:E712").Find("1480292288")
J'obtiens NULL
A la ligne A16 = 3
A la ligne A18 = 3,74
A la ligne A23 = 4,13
lole_range = lo_ole.Range("A18:A30").Find("3")
J'obtiens 4,13
lole_range = lo_ole.Range("A17:A30").Find("3")
J'obtiens NULL
lole_range = lo_ole.Range("A16:A30").Find("3")
J'obtiens 3
A ne plus rien y comprendre !
vive pb , vive excel ! (ou vive moi ? )
Hors ligne
et avec un Cells.Find ?
Hors ligne
si en E401 j'ai "3547536", avec :
lole_range = lo_ole.Cells(401,5).Find("3547536")
Il me le retrouve bien , mais je ne peux pas lui donner d'intervalle de cette manière
Hors ligne
en principe un Cells.find sans rien préciser recherche dans toute la page
Hors ligne
C'est ce que je pensais aussi mais il ne trouve aucun résultat de cette manière oO
Hors ligne
micmx a écrit:
Le problème avec une source de données ODBC , c'est qu'une ressource odbc doit être renseignée pour chaque fichier il me semble..
Dans le cas de mon application , tous les jours , un nouveau fichier excel est créé (même structure) , je dois aller le rechercher pour y trouver certaines informations.
Je pense pas que cela soit un problème.
Tu définis une source ODBC sur un nom de fichier générique du type c:\bdd excel\mon fichier.xls
Ensuite avant chaque traitement tu copies ton fichier xls à traiter en c:\bdd excel\mon fichier.xls
Pour automatiser le traitement complètement, tu utilises ta connexion OLE pour lancer une macro excel pour nommer la plage de colonne contenant des données afin d'en faire une table et tu peux ensuite faire un select * from ta_table et utiliser le SyntaxFromSQL et le Create pour créer dynamiquement une DW et manipuler les données...
La macro :
Sub NommerMaTable() ' Les colonnes A à C contiennent mes données Columns("A:C").Select ActiveWorkbook.Names.Add Name:="ma_table", RefersToR1C1:="=Feuil1!C1:C3" End Sub
Hors ligne
Ok je vais regarder à tout ça..
.. et voir si je peux traduire ça en vb :D
Merci à vous
Hors ligne
Bonjour,
Si ça peut t'aider, je te me ci-dessous le code que j'ai écrit pour charger un fichier excel dans une structure par lien OLE. Il suffit ensuite d'affecter la structure à une datawindow.
Il contient quelques fonctions propres à mon application dont tu comprendras aisément la fonction. Le code nécessite donc quelques adpatations pour être utilisé.
J'ai préféré ne pas utiliser le driver ODBC pour ne pas avoir à l'intégrer dans l'installation de l'application.
global type f_file_load_excel from function_object end type forward prototypes global function integer f_file_load_excel (string as_filename, ref s_filedata astr_filedata[]) end prototypes global function integer f_file_load_excel (string as_filename, ref s_filedata astr_filedata[]);// Fonction : Lecture d'un fichier excel, décomposition et chargement dans une structure s_filedata // Auteur : PH. BUCAILLE // Date : 03/11/2005 // Arguments : // as_filename : nom du fichier excel à charger // Valeur retournée par référence : // astr_filedata : contenu du fichier excel décomposé dans une structure s_filedata // Valeur retournée : // 1 : opération réussi // -1 : Impossible de se connecter à l'objet (excel n'est probablement pas installé sur le poste) // -2 : Classeur excel introuvable // -3 : Feuille de calcul introuvable // -4 : Feuille de calcul vide // -5 : Opération annulée par l'utilisateur long ll_nbrlignes, ll_nbrcols, ll_nbrsheet, ll_nbrclasseur long ll_i, ll_j string ls_waitmsg, ls_path, ls_file, ls_range, ls_columnname any ls_columncontent[] s_filedata lstr_filedata[] w_progress lw_prg OLEObject lole_excel lole_excel = CREATE OLEObject DoSablier(True) UsrWaitPlease(TRUE) IF lole_excel.ConnectToObject( as_filename ) = 0 THEN ls_waitmsg = "Un instant S.V.P, lecture du fichier excel en cours : " getfilefrompath(as_filename, ls_path, ls_file) // Récupération informations sur le fichier excel // Nombre de classeur ll_nbrclasseur = lole_excel.application.workbooks.Count IF ll_nbrclasseur = 0 THEN lole_excel.DisconnectObject() DESTROY lole_excel UsrWaitPlease(FALSE) RETURN -2 // Aucun classeur trouvé dans le document excel END IF // Nombre de feuille de calcul ll_nbrsheet = lole_excel.application.workbooks(ls_file).worksheets.Count IF ll_nbrsheet = 0 THEN lole_excel.DisconnectObject() DESTROY lole_excel UsrWaitPlease(FALSE) RETURN -3 // Aucune feuille de calcul trouvée dans le document excel END IF // Remarque : Il faut préciser nominativement le nom du classeur (workbooks) et non mettre l'indice 1 // En cas d'instance d'excel ouverte avec un fichier, on importe ce fichier et non celui désiré // Nombre de colonnes ll_nbrcols = lole_excel.application.workbooks(ls_file).worksheets(1).UsedRange.Columns.Count // Nombre de lignes ll_nbrlignes = lole_excel.application.workbooks(ls_file).worksheets(1).UsedRange.Rows.Count IF ll_nbrcols = 0 OR ll_nbrlignes = 0 THEN lole_excel.DisconnectObject() DESTROY lole_excel UsrWaitPlease(FALSE) RETURN -4 // Feuille de calcul vide END IF UsrWaitPlease(FALSE) lw_prg = ThermoInit ( ls_waitmsg , ll_nbrcols, True ) FOR ll_j = 1 TO ll_nbrcols // Nom de la colonne sous la forme : $A:$A ls_columnname = lole_excel.application.workbooks(ls_file).worksheets(1).Columns(ll_j).Address // Extraction de la dénomination alphabétique uniquement de la colonnne : $A:$A => A ls_columnname = Mid( ls_columnname, 2, Pos(ls_columnname, ':') - 2) // Construction de la plage de données à extraire de la colonne => ex : "A1:A3300" ls_range = ls_columnname + '1:' + ls_columnname + string(ll_nbrlignes) // Extraction du contenu de la colonne ls_columncontent = lole_excel.application.workbooks(ls_file).worksheets(1).Range(ls_range).Value FOR ll_i = 1 TO ll_nbrlignes lstr_filedata[ll_i].ss_data[ll_j] = trim(string(ls_columncontent[ll_i])) // Cas particulier de l'import du plan comptable API // Colonne contenant la valeur VRAI ou FAUX => EXCEL l'interprète comme la valeur logique // En lecture, on obtient les valeurs true ou false et non la chaîne VRAI ou FAUX CHOOSE CASE lower(lstr_filedata[ll_i].ss_data[ll_j]) CASE "false" lstr_filedata[ll_i].ss_data[ll_j] = "0" CASE "true" lstr_filedata[ll_i].ss_data[ll_j] = "1" END CHOOSE IF IsNull(lstr_filedata[ll_i].ss_data[ll_j]) THEN lstr_filedata[ll_i].ss_data[ll_j] = "" // Remarque PH. BUCAILLE 01/03/2006 PB 9.0.3.8565 : On n'utilise pas le code çi-dessous avec une variable intermédiaire // On obtient par cette méthode une utilisation aberrante de la mémoire : 600 Mo pour 3000 lignes // Problème d'allocation mémoire surement à la taille maximum de la variable ls_cellule !! // ls_cellule = trim(string(lole_excel.application.workbooks(ls_file).worksheets(1).cells(ll_i, ll_j).value)) // IF ISNULL(ls_cellule) THEN ls_cellule = "" // lstr_filedata[ll_i].ss_data[ll_j] = ls_cellule NEXT IF NOT lw_prg.Stopped THEN ThermoIteration ( ll_j, lw_prg) ELSE DoSablier( FALSE ) lole_excel.DisconnectObject() DESTROY lole_excel CLOSE( lw_prg ) RETURN -5 END IF NEXT DoSablier( FALSE ) CLOSE( lw_prg ) lole_excel.DisconnectObject() ELSE UsrWaitPlease(FALSE) Dosablier(FALSE) RETURN -1 END IF DESTROY lole_excel astr_filedata = lstr_filedata RETURN 1 end function
La structure :
global type s_filedata from structure string ss_data[] end type
Hors ligne
Génial , avec de petites modifications , j'arrive à récupérer l'ensemble du fichier excel grâce à ton code bien clair.
Je vais maintenant voir si je peux l'importer dans une datawindow ou datastore.
Merci
PS: Je me demandais si cette méthode ne nécessitait pas trop de ressource?
Dernière modification par micmx (19-02-2009 10:44:18)
Hors ligne
Bonjour,
Il est très facile d'affecter le contenu d'une structure à une datawindow, il suffit d'utiliser la syntaxe :
// Copy the data from the array of structures to the destination DataWindow
dw_dest.Object.Data = lstr_Data
Dans les exemples fournis avec Powerbuilder tu regardes :
User Objects -> DataWindows to Structure Utility
DataWindows -> Runtime Modification -> Copy Datawindow
A l'origine, la fonction était très lente dans ces premières versions car la feuille excel était lue celulle par cellule, avec cette version il ne faut que quelques secondes pour lire des fichiers excels de taille importante dans la mesure ou les données sont rapatriées colonne par colonne.
Hors ligne
null object reference
Je cherche des exemples mais ce que tu me donnes ne correspond pas avec l'aide..
j'ai pb10
N'est-ce pas tout aussi bien , plutôt que de stocker tout dans la structure , stocker plutôt les informations directement dans une datastore ?
Dernière modification par micmx (19-02-2009 14:17:36)
Hors ligne
Bonjour,
En fait, je ne faisais pas référence à l'aide mais à l'application livré avec Powerbuilder démontrant toutes les fonctionnalités de PB.
Dans le dossier C:\Program Files\Sybase\PowerBuilder 10.0\Code Examples\Example App, tu ajoutes la "target" PB Examples.pbt à un "workspace" et tu exécutes.
Les références correspondent à des entrées dans le treeview :
User Objects -> DataWindows to Structure Utility
DataWindows -> Runtime Modification -> Copy Datawindow
Je pense qu'il n'y a rien de plus efficace que de remplir une datastore en une seule ligne en lui affectant une structure. D'ailleurs, tu le constatera, l'exemple Copy Datawindow compare les différences de performances selon la méthode utilisée pour remplir une datawindow.
Hors ligne
Bonjour,
Génial les exemples
J'ai regardé le code, comme tu m'as dis..c'est tout simplement
dw_dest.Object.Data = lstr_Data
Cependant lorsque je fais
lds_exceldata2 = create nv_ds_excel lds_exceldata2.dataobject="dw_testexcel" li_retour=f_load_excel(ls_file,lstr_datas) lds_exceldata2.Object.Data=lstr_datas
PB se ferme ! (cette fonction rempli la structure lstr_datas)
si je met
li_retour=f_load_excel(ls_file,lstr_datas)
//lds_exceldata2.Object.Data=lstr_datas
ça fonctionne bien (ma structure est bien remplie tableau de structure qui contiennent elles même un tableau de string)
Hors ligne
Bonjour,
Tu as bien construit une datastore en adéquation avec la structure des données à recevoir ?
Hors ligne
Je viens de remarquer que là était mon erreur !!
Je suis vraiment nouveau dans PB , je rame un peu
J'ai comme dans ton exemple , fait une structure contenant un tableau de string..
donc je remplissais ma structure de cette manière :
lstr_filedata[ll_i].ss_data[ll_j] = trim(string(ls_columncontent[ll_i]))
Si dans ma structure lstr_filedata de type s_filedata , à la place d'un tableau de string , je renseigne une autre structure , ça ne pose pas de problème ?
Je ne vois que cette manière pour pouvoir remplir ma structure par une boucle..
donc ça donnerait :
struct => s_filedata un élément de type s_fields
struct => s_fields contient string nom,string age (par exemple)
Afin de pouvoir garder la synthax
lstr_filedata[ll_i].ss_fields[ll_j] = trim(string(ls_columncontent[ll_i]))
PS: A la base lorsque ma structure contenant un tableau de string était remplie , j'avais 172 struct de 29 string , ma DW comportait 29 cols
PS²: Je ne peux pas parcourir les champs d'une structure par une boucle , donc le code que j'ai mis plus haut n'est pas réalisable
Dernière modification par micmx (20-02-2009 09:46:31)
Hors ligne
Bonjour,
Non, si s_field est une structure.
Pour l'indice de la colonne de l'age :
lstr_filedata[ll_i].str_fields.si_age = integer(trim(string(ls_columncontent[ll_i])))
Pour l'indice de la colonne du nom :
lstr_filedata[ll_i].str_fields.ss_nom = trim(string(ls_columncontent[ll_i]))
Hors ligne
Voilà..
J'ai du procédé comme ceci donc pour que ça fonctionne..
choose case ll_j case 2 astr_efiledata[ll_nb].v_fb4=integer(trim(string(ls_columncontent[ll_i]))) case 3 astr_efiledata[ll_nb].v_flex1=integer(trim(string(ls_columncontent[ll_i]))) case 4 astr_efiledata[ll_nb].v_newfl=integer(trim(string(ls_columncontent[ll_i]))) case 5 astr_efiledata[ll_nb].v_of=long(trim(string(ls_columncontent[ll_i]))) case 6 astr_efiledata[ll_nb].v_pn=trim(string(ls_columncontent[ll_i])) case 7 astr_efiledata[ll_nb].v_qty=integer(trim(string(ls_columncontent[ll_i]))) end choose
Si une autre méthode plus professionnelle telle que
astr_efiledata[ll_nb].ss_field[ll_j]=trim(string(ls_columncontent[ll_i]))
est possible , tenez moi informé svp
Merci beaucoup buck et les autres pour votre aide
Hors ligne