Page 1 sur 2

[R] Delete item listview et sqlite & réindexation

Posté : jeu. 08 mai 2014 15:04
par aulus
Bonjour,

Le premier champ de la base sqlite, nommé "id" numérote chaque ligne. Ce champ s'incrémentant à chaque création de ligne supplémentaire.
Le contenu de la base s'affiche dans un listview. Trois boutons permettent de modifier la base : Ajouter une ligne, Modifier et Supprimer la ligne sélectionnée. Jusque là tout va bien.

A la suite de la suppression de ligne, je souhaiterais que les lignes retrouvent une numérotation continue, aussi bien dans la base sqlite, que dans le listview :
Je ne parviens pas à faire cela.
J'imagine mettre à jour la base sqlite, dans une boucle pour décrémenter d'1 tous les enregistrements supérieurs au numéro de la ligne supprimée.
Mais là je suis perdu.

Code : Tout sélectionner

;suppression de la ligne sélectionnée :

Local $index = ControlListView("", "", $myList, "GetSelected")

$Base_SQLite = _SQLite_Open($sqlite)
_SQLite_Exec($Base_SQLite, "DELETE FROM liste WHERE id = '" & $index & "';")

;réindexation : là rien ne va plus
$nbre_lignes =  _SQLite_Query($Base_SQLite, "sqlite_num_rows(liste);")
For $i = $index + 1 To $nbre_lignes
$decr = $i - 1
_SQLite_Query($Base_SQLite, "UPDATE liste SET id = '" & $decr & "' WHERE ID = $i;")
Next

 
Je vous remercie de votre aide.

Re: [..] delete item listview et sqlite & réindexation

Posté : jeu. 08 mai 2014 15:44
par jchd
Je te déconseille de bidouiller tes identificateurs pour en faire des numéros de "ligne". Si tu as _vraiment_ besoin de numéroter tes lignes dans ta listview, fais-le mais laisse cette numérotation indépendante de rowid.

Il n'est pas compliqué de faire ça :
_sqlite_gettable2d($hdb, "select 1, col1, col2, col3 from ... where ... order by ...", ...)
ensuite tu remplaces le 1 par un vrai numéro de ligne
for...
next
et tu as ce que tu souhaites, visuellement (tu n'es pas obligé d'afficher le rowid dans ta listview).

Il faut bien comprendre qu'il n'y a aucun ordre dans une table SQL et que, sauf lorsqu'une clause "order by" est spécifiée dans un select, l'ordre peut être quelconque et même varier entre deux invocations successives.

Ah oui, aussi une autre chose qui me chiffonne : je ne sais pas comment tu as défini ta table, mais mettre $index entre quotes en fait une chaîne et non un entier.

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 09:44
par aulus
Merci pour ces précisions. J'essaie de comprendre... ce qui n'est pas gagné ! :)
Mon $index est effectivement un entier de manière à obtenir un affichage correct (10 après le 9, et non après le 1)

Je me mets à la tâche...

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 10:09
par jchd
L'intérêt de dissocier les données de la base et leur numéro d'ordre dans ta présentation est multiple :
o) pas de pollution de la base avec des valeurs n'ayant aucun rapport avec les données qu'elle contient
o) indépendance du numéro d'ordre avec l'ordre de tri et donc l'ordre de présentation dans la listview
o) nul besoin de gérer "à la main" les numéros d'ordre après mise à jour de la base

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 14:28
par aulus
Bonjour,

Je suis complètement perdu :( Je ne maîtrise pas suffisamment sqlite. Je vais sans doute succomber à la tentation de remplacer les bases sqlite que j'envisageais de créer par des fichiers textes que je sais manipuler.

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 15:38
par jchd
Je veux bien t'aider mais il faut poster ce qui cloche et expliciter ce que tu souhaites.

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:02
par aulus
Merci pour votre patience et vos encouragements.

Voici les extraits de mon code :

Ma ListView :

Code : Tout sélectionner

    $myList = GuiCtrlCreateListview("n°|mot latin|mot français|texte|oeuvre|auteur latin|manuel|page|auteur manuel|editeur", 10, 50, 1280, 480, -1, _
    BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $WS_EX_CLIENTEDGE))
    GUICtrlSendMsg(-1, 0x101E, 0, 40)      ; n°
    GUICtrlSendMsg(-1, 0x101E, 1, 100)     ; latin
    GUICtrlSendMsg(-1, 0x101E, 2, 100)     ; français
    GUICtrlSendMsg(-1, 0x101E, 3, 300)      ; texte
    GUICtrlSendMsg(-1, 0x101E, 4, 200)      ; oeuvre
    GUICtrlSendMsg(-1, 0x101E, 5, 100)     ;auteur du texte latin
    GUICtrlSendMsg(-1, 0x101E, 6, 200)      ; manuel
    GUICtrlSendMsg(-1, 0x101E, 7, 40)        ; page
    GUICtrlSendMsg(-1, 0x101E, 8, 100)       ; auteur du manuel
    GUICtrlSendMsg(-1, 0x101E, 9, 100)     ; editeur
 
Ma fonction "supprimer un enregistrement de la base sqlite et mise à jour de la listview (suppression de l'affichage de l'enregistrement supprimé et renumérotation)

Code : Tout sélectionner

Func Supprimer()
; suppression de l'enregistrement sqlite
Local $selected = ControlListView($maGui, "", $myList, "GetSelected")
Local $index = Int(ControlListView($maGui, "", $myList, "GetText", $selected, 0))
Local $sLocalSQLiteDll = @ScriptDir & "\sqlite3.dll"
_SQLite_Startup($sLocalSQLiteDll, False, 1)
If @error Then MsgBox($MB_SYSTEMMODAL, "Le fichier SQLite3.dll est introuvable")
$Base_SQLite = _SQLite_Open($sqlite)
_SQLite_Exec($Base_SQLite, "DELETE FROM liste WHERE numero = " & Number($index) & ";")

; mise à jour du champ numero ($aRow[0]) dans la base :
$nb_lignes = ??? (je ne trouve pas la fonction retournant le nombre d'enregistrements contenus dans la base sqlite)
Local $hQuery, $aRow 
_SQLite_Query($Base_SQLite, "SELECT * FROM liste ORDER BY numero DESC", $hQuery)
For $i = $index TO $nb_lignes (ne trouvant pas la fonction retournant le nombre d'enregistrements de la base, j'ai testé avec 9)
    Local $indexCorr = $i - 1
    _SQLite_Exec($Base_SQLite, _
    "UPDATE liste SET " & _
                "numero = " & $indexCorr & _
            "WHERE " & _
                "numero = " & $i & ";")
Next

; mise à jour de la listview (suppression de l'affichage de la ligne supprimée et renumérotation)
_GUICtrlListView_DeleteAllItems($myList)
While (_SQLite_FetchData ($hQuery, $aRow) = $SQLITE_OK)
GUICtrlCreateListViewItem($aRow[0] & "|" & $aRow[1] & "|" & $aRow[2] & "|" & $aRow[3] & "|" & $aRow[4] & "|" & $aRow[5] & "|" & $aRow[6] & "|" & $aRow[7] & "|" & $aRow[8] & "|" & $aRow[9], $myList)
Wend
_SQLite_Close ()
_SQLite_Shutdown ()
EndFunc
 
2 problèmes :

1. l'enregistrement supprimé dans la base n'est pas celui que j'ai sélectionné mais l'enregistrement voisin.
2. la numérotation dans la listview n'est pas bonne au réaffichage (des numéros qui se répètent 2 fois)

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:34
par Hugues
Salut,

Moi pour supprimer une entrée dans un ListView, je fais:
► Afficher le texte

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:40
par aulus
Bonsoir,

Ce code supprimerait éventuellement un enregistrement dans la base sqlite, mais pas dans la listview.
Je l'ai testé, mais l'enregistrement n'est pas supprimé dans la base.

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:45
par Hugues
Bin après tu fais un refresh de ta ListView...

Tu fais un

Code : Tout sélectionner

_GUICtrlListView_DeleteAllItems($hListView)
, ensuite tu refais une requête et tu réaffiches dans ta ListView...

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:50
par aulus
Ah oui d'accord.
Toutefois ce code ne me supprime pas l'enregistrement dans la base.

Mon propre code supprime bien un enregistrement dans la base, mais pas le bon !!!

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 19:59
par Hugues

Code : Tout sélectionner

_GUICtrlListView_DeleteAllItems($hListView)
supprime le contenu de la ListView, mais pas dans la base.

Le code

Code : Tout sélectionner

$IndexItemListView = _GUICtrlListView_GetNextItem($hListView)
$aItem = _GUICtrlListView_GetItemText($hListView, $IndexItemListView)

_SQlite_Query (-1, "SELECT * FROM " & $Table & " WHERE ID = " & $aItem & ";", $hQuery)
local $req =_SQLite_Exec (-1, "DELETE FROM " & $Table & " WHERE ID = " & $aItem & ";")
supprime dans la base SQLite.

Et pour retourner le nombre de lignes contenues dans ta base et remplir ta ListView:

Code : Tout sélectionner

$iRval = _SQLite_GetTable2d(-1, "SELECT * FROM " & $Table & ";", $aResult, $iRows, $iColumns) ;$iRows -> Nombre de lignes

If $iRval = $SQLITE_OK Then
        Dim $TempArray[$iRows][Ton nombre de colonnes]

        For $r = 1 To $iRows
            For $c = 0 To 9
                $TempArray[$r-1][$c] = $aResult[$r][$c]
                        Next
                Next
                EndIf

        _GUICtrlListView_AddArray($hListView, $TempArray)

 

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 20:06
par aulus
Oui, j'ai bien noté.

EDIT : SI, votre code supprime bien la ligne sélectionnée dans le listview. Dans le message ci-dessus j'ai parlé trop vite. Je m'excuse.

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 20:14
par Hugues
:mrgreen: et ça supprime le bon enregistrement?

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 20:34
par aulus
Une erreur bloque le programme :

(1545) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
$TempArray[$r-1][$c] = $aResult[$r][$c]
^ ERROR

Re: [..] Delete item listview et sqlite & réindexation

Posté : ven. 09 mai 2014 21:04
par Hugues
Il faut que tu adaptes à la taille de ta base.

Tu dimensionnes ton tableau : Dim $TempArray[$iRows][Ton nombre de colonnes] -> $iRows étant le nombre de lignes contenues dans ta base retournée par la requête:

Code : Tout sélectionner

$iRval = _SQLite_GetTable2d(-1, "SELECT * FROM " & $Table & ";", $aResult, $iRows, $iColumns)
ensuite tu fais une boucle pour récupérer les valeurs:

Code : Tout sélectionner

For $r = 1 To $iRows ; -> Boucle de lecture de la ligne 1 à la dernière
    For $c = 0 To 9; -> Boucle de la colonne 0 à la colonne 9 (à adapter selon tes besoins)
        $TempArray[$r-1][$c] = $aResult[$r][$c]
    Next
Next

 
puis tu remplis ta ListView:

Code : Tout sélectionner

_GUICtrlListView_AddArray($hListView, $TempArray)

Re: [..] Delete item listview et sqlite & réindexation

Posté : sam. 10 mai 2014 00:57
par mikell
aulus tu as vraiment besoin de ce champ 'numero' ?
Tu pourrais très bien faire le delete dans la base avec un WHERE *autre chose* et repeupler ensuite la listview avec un _SQLite_GetTable2d avec (ou non) un ORDER BY

Re: [..] Delete item listview et sqlite & réindexation

Posté : sam. 10 mai 2014 02:48
par GaRydelaMer
Bonjour

j'utilise tous les jours des bases et des listviews, et j'ai résolu ce genre de problème en détournant la fonctionnalité AutoIt des LV.
J'utilise aussi le mode événementiel et des objets que je créé avec AutoItObject, donc je vais pas te donner du code mais le principe, pour le code faut que je retrouve mes vieux projets.

Je m'explique:
J'utilise les listviews créé avec l'UDF Listview, car AutoIt utilise un paramètre invisible pour stocker les identifiants des items de la liste et utilise cet identifiant dans le mode événementiel. C'est pas le cas avec l'UDF, donc je vais utiliser pour moi cette zone pour stocker mon rowid sans gêner le mode événementiel.

Tu as donc ta requête qui te renvoie une liste d'enregistrement: $Datas (j'utilise pas SQLlite mais ADO, le principe est le même), $oConn la connection.
rowid, champ1, champ2 etc....
rowid et le champ de la table un entier autoincrément gérer par la base de donnée.

Cn boucle sur cette liste du début tant que je suis pas à la fin:

Code : Tout sélectionner

While Not $Datas.EOF ;

  $Datas.MoveNext
Wend
L'idée dans cette boucle de peuplé la liste en ajoutant des Items

Code : Tout sélectionner

$iItem = _GUICtrlListView_InsertItem($hLisvtView, $Datas.Fields("champ1").value) ; j'insère un item avec comme texte la valeur du Champ1
_GUICtrlListView_SetItemText($hLisvtView, $iItem, $Datas.Fields("champ2").value, 1) ; on ensuite mettre à jour les valeurs dans les colonnes
_GUICtrlListView_SetItemText($hLisvtView, $iItem, $Datas.Fields("champ2").value, 2)
etc...
_GUICtrlListView_SetItemParam($hLisvtView, $iItem, Int($Datas.Fields("rowid"). value)) ;; C'est ici que je stocke l'identifiant unique de l'enregistrement dans la table.
Ensuite tu peux lire très simplement la ligne dans la liste et la relier à la ligne dans ta base de plus tu peux faire une requête pour afficher la liste et aller modifier la ligne dans la table.
Ici on va retrouver la liste sous forme de tableau des index dans la liste des items sélectionnés

Code : Tout sélectionner

Local $aItems = _GUICtrlListView_GetSelectedIndices($hListView))
Pour supprimer dans la base et la liste

Code : Tout sélectionner

For $i = $aItems[0] to 1 Step -1 ; je boucle à l'envers si tu as plusieurs items sélectionnés pour pas que la LV ré-indexe les Items du tableau $aItems
    $iItem = $aItems[$i] ; je prend l'index de l'item dans le tableau
    $iRowID = _GUICtrlListView_GetItemParam($hLisvtView, $iItem) ; je récupère ici la valeur du paramètre caché de la LV qui contient mon identifiant unique dans la base.
   _GUICtrlListView_DeleteItem($hLisvtView, $iItem) ; Je supprime l'item dans la listview
    $oConn.Execute("DELETE FROM la_Table WHERE rowid = " & $iRowID) ; ici tu vas le supprimé dans la base et le tour est joué
Next

Re: [..] Delete item listview et sqlite & réindexation

Posté : sam. 10 mai 2014 03:55
par jchd
Exactement cela.

Re: [..] Delete item listview et sqlite & réindexation

Posté : sam. 10 mai 2014 08:25
par aulus
Hugues a écrit ::mrgreen: et ça supprime le bon enregistrement?
Non !
Hugues a écrit :Il faut que tu adaptes à la taille de ta base
Tu dimensionnes ton tableau : Dim $TempArray[$iRows][Ton nombre de colonnes]
Maintenant c'est bon : ce "détail" m'avait échappé à la lecture du code.
mikell a écrit :aulus tu as vraiment besoin de ce champ 'numero' ?
Tu pourrais très bien faire le delete dans la base avec un WHERE *autre chose* et repeupler ensuite la listview avec un _SQLite_GetTable2d avec (ou non) un ORDER BY
Le problème est que je ne peux pas faire un WHERE sur un autre champ, car tous les champs peuvent se répéter :

VALUES (1,'cum','quand','Comment on devient serf','Liber de servis','Marmoutiers','Moyen âge','14','Delannoy','OCDL');")
VALUES (2,'cum','comme','Comment on devient serf','Liber de servis','Marmoutiers','Moyen âge','14','Delannoy','OCDL');")
VALUES (3,sicut','comme','Comment on devient serf','Liber de servis','Marmoutiers','Moyen âge','14','Delannoy','OCDL');")
GaRydelaMer a écrit :Bonjour
j'utilise tous les jours des bases et des listviews, et j'ai résolu ce genre de problème en détournant la fonctionnalité AutoIt des LV.
J'utilise aussi le mode événementiel et des objets que je créé avec AutoItObject, donc je vais pas te donner du code mais le principe, pour le code faut que je retrouve mes vieux projets... ...
Merci pour tout ce code qu'il me faut étudier.

=================================================

Grand merci à tous ! Je poursuis mes essais et vous informe de l'évolution...