[R] Multi delete listview et sqlite

Aide et conseils concernant AutoIt et ses outils.
Règles du forum
.
Répondre
tekserver
Niveau 3
Niveau 3
Messages : 37
Enregistré le : lun. 24 févr. 2014 11:36
Status : Hors ligne

[R] Multi delete listview et sqlite

#1

Message par tekserver »

Bonjour le forum,

Je viens aujourd'hui demander votre aide sur une fonction que je n'arrive pas à faire.
Actuellement j'utilise une fonction, que j'ai trouvé sur le forum, pour supprimer une ligne dans ma listview puis qui supprime l'entré sql qui correspond. Jusque la tout va bien, ça fonctionne bien.
Je veux cependant pouvoir faire la même chose, si je sélectionne plusieurs lignes dans ma listview.
► Afficher le texte
En vous remerciant par avance,
Cordialement.
Modifié en dernier par tekserver le mar. 01 sept. 2015 15:24, modifié 1 fois.
Avatar du membre
matwachich
Membre émérite
Membre émérite
Messages : 986
Enregistré le : lun. 19 oct. 2009 04:04
Localisation : Algérie
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#2

Message par matwachich »

Pourquoi faire simple quand on peut faire compliqué :p

Code : Tout sélectionner

$aSel = _GUICtrlListView_GetSelectedIndices($hListView, True)

_SQLite_Exec(-1, 'BEGIN')
For $i = 1 To $aSel[0]
    $sText = _GUICtrlListView_GetItemText($hListView, $aSel[0])
    _SQLite_Exec(-1, 'DELETE FROM Aliments WHERE id=' & $sText) ; Si $sText n'est pas un chiffre, il faut alors faire _SQLite_Escape($sText)
Next
_SQLite_Exec(-1, 'END')
Sortons VW du coté obscure! - La curiosité est un vilain défaut! Cliquez ici
TiDi
Niveau 2
Niveau 2
Messages : 27
Enregistré le : sam. 06 juin 2015 23:27
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#3

Message par TiDi »

@matwachich : pourquoi "BEGIN" et "END" ?
tekserver
Niveau 3
Niveau 3
Messages : 37
Enregistré le : lun. 24 févr. 2014 11:36
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#4

Message par tekserver »

Re le forum,

Merci matwachich de m'avoir répondu. Je viens de tester ce que tu me propose mais hélas ça ne fonctionne pas, même si je supprime une seule ligne, ça me delete le ligne suivante de celle qui est sélectionné, et quand j'en sélectionne plusieurs il m'en supprime qu'une. J'ai remarqué que la valeur de $aSel me donne rien.
matwachich a écrit :

Code : Tout sélectionner

$aSel = _GUICtrlListView_GetSelectedIndices($hListView, True)

_SQLite_Exec(-1, 'BEGIN')
For $i = 1 To $aSel[0]
    $sText = _GUICtrlListView_GetItemText($hListView, $aSel[0])
    _SQLite_Exec(-1, 'DELETE FROM Aliments WHERE id=' & $sText) ; Si $sText n'est pas un chiffre, il faut alors faire _SQLite_Escape($sText)
Next
_SQLite_Exec(-1, 'END')
Cordialement.
Modifié en dernier par tekserver le mar. 01 sept. 2015 06:45, modifié 1 fois.
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#5

Message par jchd »

Je dis ça au pif, pas trop creusé :

Code : Tout sélectionner

    $sText = _GUICtrlListView_GetItemText($hListView, $aSel[0])
serait peut-être plutôt ça :

Code : Tout sélectionner

    $sText = _GUICtrlListView_GetItemText($hListView, $aSel[$i])
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
tekserver
Niveau 3
Niveau 3
Messages : 37
Enregistré le : lun. 24 févr. 2014 11:36
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#6

Message par tekserver »

Bonjour le forum,

Merci Jchd, c'était bien ça, durant mon essai d'hier je n'avais pas vu cette petite coquille. Merci à vous deux ça fonctionne parfaitement.
matwachich a écrit :Si $sText n'est pas un chiffre, il faut alors faire _SQLite_Escape($sText)
C'est uniquement l'ID (INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT) que je cible, tout est fonctionnel :)

J'ai deux dernières questions s'il vous plait, c'est quoi la différence entre:

_SQLite_Exec(-1, "Commit;") et _SQLite_Exec(-1, "End;") ?
Puis est-ce bien entre double quote et un ; à la fin ?

Bonne journée à vous,
Cordialement
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#7

Message par jchd »

Tout ordre ("statement") SQL se termine par un point-virgule, qui est en fait optionnel dans la plupart des contextes, sauf pour séparer des ordres. Ainsi si on fait un *_Exec de :
pragma ceci-cela=56;pragma autre=1
le point-virgule est impératif.

Doubles ou simples quotes délimitent des chaînes en AutoIt, au choix mais par paires.

BEGIN ... END délimitent une transaction SQL, c'est à dire une séquence d'ordres qui doivent être tous réalisés comme un seul bloc ou tous ignorés à la suite d'une erreur. Donc tout autre usager de la BdD verra le contenu soit avant le début de la transaction, soit après sa complétude, mais en aucun cas dans un état intermédiaire.

Les transactions sont le seul moyen de garantir l'intégrité et la cohérence des données lorsque plusieurs usagers opèrent simultanément sur une BdD.

Dans ton cas (si j'ai bien compris en lisant en diagonale) tu fais un SELECT puis tu peuples ta listview et ensuite tu permets à l'utilisateur d'en modifier ou effacer un ou plusieurs éléments. Si tu étais dans un contexte multi-utilisateurs tu risquerais de rencontrer le cas suivant :
a) User_A affiche sa listview et va boire un café, comme lui a conseillé Oldelaf
b) User_B affiche sa listview et modifie les éléments 1, 2 et 3
c) User_A a maintenant les trous en face des yeux et efface les éléments 2, 3 et 4 de sa listview
d) User_B ajoute les éléments 17 et 18 à la fin de sa listview
e) User_B modifie soigneusement l'élément 15
f) User_B supprime l'élément 9
g) User_A et User_B sont contents : il n'y a eu aucune erreur et ils ont donc bien fait leur travail
h) Ils impriment alors chacun leur rapport et vont tous deux en salle de réunion où le big boss les attend
i) Personne ne comprend pourquoi rien ne correspond à rien
j) Le boss trouve que A et B sont des charlots incompétents et les vire illico
k) Il les remplace par C et D qui, dès demain, auront le même problème
l) La boîte se casse la gueule et est rachetée par des chinois qui eux savent gérer les choses au carré
m) Ils délocalisent et virent tout le monde.

Dès que tu envisages la possibilité qu'il y ait une action RMW (Read-Modify-Write) sur le contenu d'une BdD, tu dois employer une transaction si plus d'un process ou usager peut employer cette base au même moment. Sinon c'est la ruine de l'économie française, comme le montre l'exemple ci-dessus. Comme tu n'as manifestement pas l'intention de mettre à mal nos chers emplois, tu vas apprendre les bienfaits des transactions.

L'autre intérêt d'une transaction lorsque tu modifies la base de façon plus ou moins massive (insert, update ou delete en boucle) est que le moteur groupe les changements et minimise grandement le nombre d'E/S disque, parfois d'un facteur 10 ou plus.
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
tekserver
Niveau 3
Niveau 3
Messages : 37
Enregistré le : lun. 24 févr. 2014 11:36
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#8

Message par tekserver »

Re,

Merci pour ces éclaircissement, sur le "Begin;" et "End;".
Ta réponse humoristique m'a fait sourire, merci :wink:

Dans quel cas doit-on alors utiliser le "Commit;" ?

Bonne journée,
Cordialement.
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#9

Message par jchd »

END est un alias de COMMIT. Voir la doc.
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
Avatar du membre
matwachich
Membre émérite
Membre émérite
Messages : 986
Enregistré le : lun. 19 oct. 2009 04:04
Localisation : Algérie
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#10

Message par matwachich »

Merci pour la petite correction jchd (je devais manquer de sommeil :p )
Sortons VW du coté obscure! - La curiosité est un vilain défaut! Cliquez ici
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#11

Message par jchd »

COMMIT;
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
tekserver
Niveau 3
Niveau 3
Messages : 37
Enregistré le : lun. 24 févr. 2014 11:36
Status : Hors ligne

Re: [..] Multi delete listview et sqlite

#12

Message par tekserver »

Merci beaucoup.
TiDi
Niveau 2
Niveau 2
Messages : 27
Enregistré le : sam. 06 juin 2015 23:27
Status : Hors ligne

Re: [R] Multi delete listview et sqlite

#13

Message par TiDi »

Merci jchd pour ces explications ^^ :arrow: Direction les anciens scripts pour quelques modifications ;)
Avatar du membre
jchd
AutoIt MVPs (MVP)
AutoIt MVPs (MVP)
Messages : 2284
Enregistré le : lun. 30 mars 2009 22:57
Localisation : Sud-Ouest de la France (43.622788,-1.260864)
Status : Hors ligne

Re: [R] Multi delete listview et sqlite

#14

Message par jchd »

Bon, alors comme avec la peinture monocouche, il est bon d'en repasser un cht'it coup...

SQLite n'est pas un moteur SQL de type client/serveur, où le serveur est un machin opaque qui coûte bonbon, demande une salle entière climatisée et fait des miracles l'air de rien. De plus, SQLite est "lite" donc taillé pour être embarqué dans un GPS, une télé, une box xDSL, un routeur, tous les smartphones existants, bref dans des centaines de milliards de bidules qui n'ont pas la richesse matérielle et fonctionnelle de serveurs dédiés. SQLite est non seulement portable sur à peu près toutes les plates-formes disposant d'un compilateur C mais les BdD SQLite sont portables sans aucun changement sur toutes ces plates-formes.

Ce n'est pas pour autant qu'il est forcément très limité en termes de capacités SQL, seulement qu'il faut bien être conscient de ses possibilités mais aussi de ce qu'on ne peut pas raisonnablement attendre de lui. Ainsi, j'ai eu l'occasion de travailler sur un serveur web utilisant une base SQLite dépassant 127 To pour servir jusqu'à 50 requêtes par seconde. Un serveur dédié signé Oracle aurait coûté plus du million de USD alors que ce machin tourne sur un serveur lambda (certes gavé de mémoire) coûtant 50 fois moins cher et sans licence annuelle. Juste pour dire que ce n'est pas exactement ni uniquement un jouet pour pré-ados. On peut donc vraiment faire des choses "velues" avec.

Revenons à nos transactions. SQLite n'utilise qu'un seul moyen pour s'assurer que deux transactions vont respecter l'isolation et l'intégrité des données : c'est l'emploi de verrouillage du fichier contenant la BdD (file locking). Etant donné qu'il n'existe aucun protocole de "file locking" fiable sur aucun réseau existant à ce jour, l'emploi de BdD SQLite déportées est à proscrire absolument.

Ce point étant acquis, on peut progresser. SQLite propose 3 types de transactions :
begin [transaction]
begin immediate [transaction]
begin exclusive [transaction]

On va y revenir. Pendant ce temps, une BdD peut être dans l'un quelconque de ces états de verrouillage :
unlocked = aucun lock
shared = une ou plusieurs transactions peuvent lire la BdD
reserved = une seule transaction a posé un verrou "shared" mais elle a signifié qu'elle prévoit d'écrire par la suite ;
dans cet état, d'autre processus peuvent acquérir un verrou "shared" pour lire
pending = la transaction veut écrire et attend que tous les verrous "shared" soient levés
exclusive = la transaction est la seule qui puisse écrire et cela empêche l'octroi de tout autre type de verrou
tant qu'elle n'en n'a pas fini.

Pour info, un requête "toute seule" (non incluse dans une transaction explicite) est en interne délimitée elle-même par une transaction générée par le moteur lui-même. Ainsi, il existe une transaction explicite ou implicite à chaque instant où SQLite est solicité pour une requête SQL.

On comprend assez vite que deux ou plusieurs transactions peuvent s'imbriquer de façon mutuellement bloquante ; ces situations provoquent une erreur SQLITE_BUSY pour toutes les transactions impliquées.

Lorsqu'une transaction ne peut se voir octroyé le type de verrouillage dont elle a besoin SQLite la fait "patienter" un certain temps avec plusieurs tentatives et si l'opération n'aboutit pas au bout d'un "certain temps" (assez court en fait), une erreur SQLITE_BUSY est retournée. La gestion de ce cas de figure par l'application en complique lourdement le code et on peut contourner cette difficulté autrement.

Il suffit de définir avec _SQLite_SetTimeout pour chaque connexion à une BdB un timeout suffisament long et d'utiliser BEGIN IMMEDIATE pour toute transaction prévoyant de lire puis d'écrire au cours de son déroulement.

Lorsqu'on ne fait qu'écrire massivement (INSERT, UPDATE ou DELETE en boucle), un BEGIN EXCLUSIVE permet de le faire en toute certitude et en toute sécurité.

Si on ne fait que consulter (SELECT) alors un simple BEGIN fait l'affaire.

Quelle valeur de timeout utiliser ? La bonne réponse est "la durée maximum de la séquence la plus longue de toutes les transactions qui pourraient s'exécuter avant que notre transaction ne le fasse". J'avoue que ça semble cryptique comme ça, mais c'est exactement cela. Pourquoi en suis-je arrivé à formuler ceci de cette façon ? Parce que lorsqu'il fait "patienter" une transaction, SQLite la met en sommeil dans une liste et donne sa chance à la transaction suivante dans la liste. Ce schéma n'est pas déterministe et il se peut qu'une transaction "patiente" longtemps car d'autres peuvent lui passer devant à moulte reprises. Perso, je mets facilement un timeout de 20 minutes sans état d'âme, car ainsi je n'ai absolument pas à me poser de questions (ni, même je dois l'avouer, à tester de code erreur dans la grande majorité des cas où je sais qu'on ne va pas me débrancher le disque dur en plein milieu). Si on excepte les cas de figure tordus où ce genre de risque existe, on peut faire confiance au moteur qui fera le boulot qu'on lui a confié, à un moment ou un autre mais peu importe quand.

Pour que ce paradigme fonctionne en pratique, on doit quand même tenir compte de la réalité pour éviter les blocages idiots.

Prenons le cas évoqué plus haut dans ce fil : je lis ma base (SELECT ceci, cela from ...) et le résultat part dans une listview. Un autre process (de la même machine) modifie ce que je vient de lire dès que j'ai le dos tourné et quand je reviens de la caféteria, rien ne m'indique ma listview est obsolète. On pourrait être tenté de faire un "begin immediate" avant le select, mais ça bloquerait toute écriture dans la base pour tous les autres processus. Dans ce cas, la chose à faire est de prévoir une colonne "réservé" contenant un identificateur permettant de savoir que c'est tel process qui est temporairement seul détenteur de droit de modification sur ces éléments. On peut aussi faire un rafraîchissement en mode "immediate" des données concernées dès que l'utilisateur tente de les modifier, lui indiquant que ce qu'il voit n'est plus d'actualité.

Bref, il existe des millions de cas de figure et heureusement toujours une solution disponible, à condition d'y penser avant de se retrouver coincé dans un piège (qu'on a préparé soi-même, comme toujours en programmation).

Oui, je sais mon mur de texte est long et ennuyeux mais voici que point la bonne nouvelle : SQLite est encore plus malin que ça !
Toute modification apportée à l'intérieur d'une transaction explicite n'est physiquement répercuté dans la BdD qu'à l'issue de cette transaction. Comme les données peuvent être massives, SQLite les inscrit dans un fichier "journal" qu'il est interdit de bidouiller si on en trouve un. Outre le mode par défaut de journalisation, qui n'autorise qu'un "writer" OU plusieurs "readers", SQLite offre maintenant le mode WAL, qui permet un "writer" ET plusieurs "readers" concurremment. Il suffit de faire une seule fois un
_SQLite_Exec($hDB, "pragma journal_mode=WAL") et hop, la base restera en mode WAL tant qu'on n'annulera pas ce mode. Il est clair que WAL augmente considérablement la concurrence d'applications sur une base donnée et minimisant les temps de latence d'attente de levée de verrous.

Dans tous les cas, il est primordial de faire en sorte que les transactions soient les plus courtes possibles et cette règle s'applique à tous les moteurs.

Pour les amoureux de détails plus croustillants, la doc est à votre disposition : https://www.sqlite.org/lockingv3.html
La cryptographie d'aujourd'hui c'est le taquin plus l'électricité.
Répondre