Pagina 3 di 3 primaprima 1 2 3
Visualizzazione dei risultati da 21 a 27 su 27
  1. #21
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    1,469
    Originariamente inviato da alka
    Sì o no?
    E se no, perché mai?
    Non lo so, non devo essere assertivo.
    Anche su questa affermazione, sarebbe interessante conoscere i motivi, visto che commetto questo "errore concettuale" quotidianamente, per cui mi piacerebbe sapere perché mai sarebbe sbagliato.
    Sono i casi della vita.
    Non è obbligatorio rispondere alle discussioni: se lo si vuole fare, si rimanga in tema, si dia il suggerimento che si ritiene opportuno, opzionalmente motivandolo, così che si possa mettere in pratica o confutare, se necessario.
    Mi sembrava di aver capito che i suggerimenti devono essere possibilisti.
    Perchè ci sono i fatti, e le opinioni (per me esistono solo queste ultime)
    Se si decide di scrivere solamente delle circonlocuzioni o fare delle "supercazzole", allora meglio astenersi.
    mah... che ti devo dire? avrai ragione
    a me pareva di aver non solo dato la "soluzione", ma anche le motivazioni... o forse no

    EDIT: ri-metto il codicino per la tkbmemtable, mai vorrei essere accusato di circonluzioni o supercazzole

    Supponiamo di avere una tabella documenti con ser autoincrementante [visto che vogliamo usarli... sia mai che si possa farne a meno!]

    Supponiamo di avere una TABSQUERY chiamata absqryoneshot
    Una memTbl (tkbmemtable)
    Una funzione ABSXSQL(che non fa altro che una query controllando che il db sia aperto, non ci sia sql injection etc)

    codice:
           ABSXSQL(absqryoneshot,'select codicecliente,... from documenti where ser=0');
           memTbl.LoadFromDataSet(absqryoneshot,[mtcpoStructure,mtcpoProperties]);
    questo è/potrebbe essere il modo per popolare un "nuovo" record, ossia una query con un campo chiave sicuramente inesistente che determina il ritorno, però, della struttura della tabella, il che rende superfluo dover mettere i campi "a mano" dentro memtbl o, peggio ancora, crearli a runtime.

    Nel caso in cui, ovviamente, si MODIFICHI un documento esistente, ci vorrà la condizione di selezione "giusta"

    EDIT2: per il "dump" (mi sembrava di averlo già messo, ma la vecchiaia avanza...] di una tabella si può usare qualcosa tipo così

    absdb: tabsdatabase
    chunk:integer; //il numero di "passi" di avanzamento da mostrare
    memtbl:tkbmemtable
    i,j:integer
    abstabella: tabstable
    s:string

    tralascio evidentemente il try...except
    Chiaramente nel caso in cui sei in fase di EDIT (e non di append) devi posizionarti sul record, e fare "edit" anzichè "append".
    Vabbè penso sia abbastanza semplice da capire.

    codice:
    absdb.starttransaction;
        chunk:=(tblMem.recordcount div 10)+1; // il +1 è sempre buono e giusto
    
           i:=0;
           with tblmem do
           begin
           First;
           while not eof do
           begin
               inc(i);
               if (i mod chunk)=0 then
               begin
                   //...mostra un avanzamento o qualcosa del genere, oppure no
               end;
    
               abstabella.Append;
               for j:=0 to FieldDefs.count-1 do
           	begin
               	s:=uppercase(fielddefs[j].Name);
    // abbiamo detto che ci piacciono i campi SER autoincrementanti, o forse no...
                 
      if s<>'SER' then
                   	abstabella.fieldbyname(s).asstring:=fieldbyname(s).asstring;
               end;
    
               abstabella.post;
    // se sei paranoico qui ci metti il flush del db: se la transazione abortisce "sparisce tutto"
               Next;
           end;
    
       end;
       absdb.commit(false);

  2. #22
    Utente di HTML.it
    Registrato dal
    Oct 2008
    Messaggi
    11
    Franzauker mi hai convinto !!!
    Penso che Alka non sbagli affatto, però, per le ragioni descritte da Franzauker, anche se c'è da perdere un po di tempo in più per la stesura del codice, a lungo un progetto così gestito dovrebbe dare meno problemi dal punto di vista tecnico in caso di errori al db e quindi di integrità referenziale dei dati.
    Forse adesso chiedo troppo...
    Non avresti un progettino già pronto master detail di esempio tipo fattura o qualcosa di simile da analizzare per poterci studiare sopra ???
    O comunque un link qualcosa dove posso documentarmi meglio.
    Fino ad ora ho lavorato principalmente senza l'utilizzo delle query se non in maniera molto superficiale.
    Grazie ancora per i consigli.

  3. #23
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    1,469
    Originariamente inviato da it9qbd
    Non avresti un progettino già pronto master detail di esempio tipo fattura o qualcosa di simile da analizzare per poterci studiare sopra ???
    certo che ce l'ho... faccio proprio quello per vivere, da 24 anni

    fermo che è fondamentale tenere la "mente aperta" (niente visioni-tunnell) direi che è assai più interessante fare il contrario, ossia scrivi cosa vuoi fare, e come secondo te andrebbe fatto.

    così magari ne discutiamo, col contributo anche di idee diverse dalle mie

    Fino ad ora ho lavorato principalmente senza l'utilizzo delle query se non in maniera molto superficiale.
    Cominciamo dall'ABC (in generale, con riferimento particolare ad ABS)
    Ci sono le query "read-only" e le query "modificabili".
    Le seconde sono praticamente indistinguibili da una tabella, le prime no.

    Esempio di seconde:
    select * from documenti where anno=2010

    (a parte che scritta così è una bestialità, la * viene usata proprio per essere sicuri della modificabilità, in generale non è bene usarla)

    legge "magicamente" da qualche parte la tabella, filtra le righe, e ti ritorna nel componente (diciamo per andar brutali nel TDATASET associato al componente blablabla) le singole righe.

    La cosa "buona" è che puoi scegliere tu quali righe caricarti sul client (questo è fondamentale se usi mysql e simili). Più è restrittiva -> meglio è (meno memoria utilizzi).

    L'effetto "negativo-positivo" principale (ce ne sono tanti, ma questi sono quelli che IO ritengo i più "rognosi") è che NON si refreshano da soli (e questo è bene, se l'applicazione è "seria" nel senso "con molti dati")

    Esempio: l'utente X esegue una query selezionando tutti i record che supponiamo siano 1000 e li mostra su una griglia (cosa questa da evitare, ma pazienza).
    L'utente Y aggiunge su un altro client un record (che quindi sono 1001)

    L'utente X non lo saprà mai, finquando
    - non riesegue la query
    - o "magicamente" qualcuno gli dice che qualcosa è cambiato (=esempio di comunicazione interprocesso, è un altro elemento per i programmi client-server "seri")

    Non hai sostanzialmente automatismi.
    Tieni presente, però, che gli automatismi nel mondo "reale"... non ci sono, sono un retaggio del "mondo" DB-III, Paradox etc (a filettone condiviso)

    Se i dati risiedono in un server mysql (engine innodb), nessuno sa, ad esempio, quante righe abbia una tabella.
    Nessuno, neanche il server mysql (!!). Lo puoi sapere solo contando le singole righe, con l'opportuno comando.

    ---
    Questo per farti "intuire" che più ti "distacchi" dagli automatismi (che non sono altro che strati su strati) meno "automatismi" avrai ma, di conseguenza, maggiori prestazioni potrai ottenere.
    ---
    Non bisogna però essere "fanatici", nel senso di reinventare la ruota.
    Se consideri che il concetto di query SQL esiste praticamente in tutti i componenti DB (ABS e ZEOS direi i principali per i tuoi scopi) quello è un buon punto (a mio parere) da cui partire.
    ---
    Primissima cosa: la funzione di query SQL più o meno generica

  4. #24
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    24,466
    Originariamente inviato da franzauker
    1) ABS è lenta, ti ho già spiegato come fare per vederla all'opera lentissima.
    Per renderla meno lenta (ossia vediamo di rispondere alla domanda) devi ridurre al minimo, ma proprio al minimo, tutti i refresh dei campi calcolati.
    Non conosco ABS, ma conosco il componente standard TDataSet di Delphi che, generalmente, elabora i "campi calcolati" quando è necessario, non una volta di più, non una volta di meno.

    Tutt'al più, un suggerimento valido può essere quello di eliminare qualsiasi operazione "pesante" che possa essere eseguita all'interno dell'evento che Delphi generalmente richiama quando necessita di conoscere il valore di un campo calcolato (es. OnCalcFields); ad esempio, senz'altro non è consigliabile introdurre l'esecuzione di una query in un simile evento, poiché viene richiamato per ogni campo calcolato e per ogni record presente in tabella, ma in tutti gli altri casi (es. concatenamenti, calcolo di totali, ecc.) non vi sono problemi di alcun tipo ravvisabili in generale nell'uso dei campi calcolati.


    Originariamente inviato da franzauker
    Ridurre al minimo il refresh dei campi calcolati (il che significa "a manina", nel senso "quando li vuoi fare tu, e non quando li vuole fare qualcun altro")
    I momenti e le modalità con cui i campi calcolati vengono elaborati non sono "casuali": anche se il codice è scritto da terzi, cioè da chi produce Delphi di fatto, ciò non vuol dire che la gestione dei campi calcolati sia priva di logica.


    Originariamente inviato da franzauker
    Anzi, meglio, puoi anche clonarti una tabella (o porzione) in una tabella RAM, operare su quella, e poi scriverla in un colpo solo con una transazione ABS.
    Questo dipende da quanti record ci sono nella tabella. In ogni caso, questo è esattamente ciò che fa il componente TClientDataSet: memorizzare i record provenienti da un DataSet qualunque in memoria, gestire i cambiamenti e, infine, inviare al server (o all'origine dati) le modifiche apportate in un "colpo solo".


    Originariamente inviato da franzauker
    2) la "chiave" di qualcosa dovrebbe (dal punto di vista teorico) essere legato all'oggetto stesso descritto, e non ad un campo autoincrementante aggiunto posticciamente.
    Cosa vuol dire "posticciamente": un campo che si autoincrementa non ha nulla di differente rispetto a qualsiasi altro campo della tabella, se non che possiede un compito ben specifico che è quello di identificare univocamente un record in modo sicuro anche in ambito multiutenza.

    Una chiave legata all'oggetto descritto, come può essere il Codice Fiscale per un cliente di anagrafica, appartiene a un tipo più complesso rispetto a un campo intero autoincrementale, e ciò rallenta le ricerche, anche se basate sugli indici; inoltre, se ogni tabella correlata a quella del cliente usa come campo di correlazione una stringa da 16 caratteri o più campi al posto di un unico campo autoincrementale, ad esempio, lo spazio occupato dai dati aumenta oltremodo a dismisura.

    Originariamente inviato da franzauker
    Nel caso di specie il "problemino" riguarda a "cosa succede se il campo autoincrementante non rimane più quello che ti "aspetti".
    Nel caso di specie, questo "problemino" non si verifica mai, perché il campo autoincrementale avanza sempre di una unità e, una volta attribuito a un record, per definizione non può essere modificato.


    Originariamente inviato da franzauker
    Ricorda che non hai il controllo sul suo valore.
    E chi lo dice? Puoi ottenere il valore di quel campo come qualsiasi altro, così come puoi determinarlo con la totalità dei database SQL esistenti (MySQL, SQL Server, FireBird, ecc.), ed è proprio lo strumento che garantisce il corretto riferimento a uno specifico record di una tabella (dato che utilizzare altri campi per la chiave primaria può comportare che questa cambi se si modificano i campi sui quali essa si basa, rendendo impossibile gestire le "collisioni" se queste modifiche ai campi chiave vengono fatti da più utenti, cosa che non accade se si usa un campo autoincrementale).


    Originariamente inviato da franzauker
    Ciò può accadere (anzi in generale accade) con mysql ed i restore: il tuo programma, in sintesi, sarà difficilmente (se non impossibilmente) portabile verso altri motori db.
    Ciò che rende portabile un database verso altri motori è lo standard SQL che si utilizza.


    Originariamente inviato da franzauker
    Ne deriva che, assai banalmente, conviene usare una chiave "vera" la quale può essere generata o da un gruppo di campi [...]
    in questo modo, però, la chiave inizia a diventare lunghetta, facendoti perdere in prestazioni.
    Appunto. E proprio per questo, non è una soluzione preferibile, rispetto a tante altre che invece risolvono questo e altri problemi.


    Originariamente inviato da franzauker
    A questo punto dipende da quanto vuoi "prendere in mano" la situazione, rispetto a "lasciarla così com'è".
    Ma cosa vuol dire?


    Originariamente inviato da franzauker
    Nel primo caso, proprio ultrabrutalmente, ti basta usare un campo STRINGA. Nel secondo dipende se usi ABS o un database "serio".
    Tutti i database sono "seri", anche perché non ne ho mai visto qualcuno "ridere".


    Originariamente inviato da franzauker
    Nel secondo caso puoi usare il CRC32 della chiave calcolata, mettendo un doppio controllo al momento del join
    Fare il CRC delle chiavi di ogni record, sia quelli della tabella primaria che quelli della tabella secondaria da correlare, deve essere performante di brutto.

    Poi, visto che sopra si parlava di "portabilità", sarebbe interessante vedere se è più portabile un campo autoincrementale (che esiste su ogni DB) rispetto a una funzione che calcola un CRC. La risposta, ovviamente, è implicita nell'ironia.


    Originariamente inviato da franzauker
    Questo per la gestione delle collisioni che, seppur rare, possono esserci.
    Una chiave primaria univoca non deve mai dare luogo a collisioni, altrimenti la parola "univoca" cosa ci sta a fare?

    In un sistema di gestione documentale, ad esempio, quando la "collisione" fortuita porta un cliente a visualizzare la fattura di un altro, sono problemi grossi...



    Originariamente inviato da franzauker
    fermo che è fondamentale tenere la "mente aperta" (niente visioni-tunnell) direi che è assai più interessante fare il contrario, ossia scrivi cosa vuoi fare, e come secondo te andrebbe fatto.
    "Mente aperta" significa essere permeabili ai cambiamenti e alle metodologie che nascono per uno sviluppo corretto del software, non essere inclini a qualsiasi tipo di soluzione da "macellai dello sviluppo software", secondo me.


    Originariamente inviato da franzauker
    Questo per farti "intuire" che più ti "distacchi" dagli automatismi (che non sono altro che strati su strati) meno "automatismi" avrai ma, di conseguenza, maggiori prestazioni potrai ottenere.
    Non bisogna però essere "fanatici", nel senso di reinventare la ruota.
    Come tu possa affermare qualcosa e, subito dopo, il suo contrario, per me rimane sempre affascinante.

    Ciao!
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Home | Blog | Delphi Podcast | Twitch | Altro...

  5. #25
    Utente di HTML.it
    Registrato dal
    Jan 2011
    Messaggi
    1,469

    u

    Originariamente inviato da alka
    Non conosco ABS,
    Meraviglioso poi quando uno comincia con "non conosco qualcosa, ma ne voglio discutere lo stesso".
    Normalmente per me il discorso si concluderebbe qui.
    ma conosco il componente standard TDataSet di Delphi che, generalmente, elabora i "campi calcolati" quando è necessario, non una volta di più, non una volta di meno.
    E' una bella asserzione. Peccato rimanga solo questo, ossia un'asserzione. Meno addirittura di un'opinione. Assai interessante poi il "generalmente".

    Tutt'al più, un suggerimento valido può essere quello di eliminare qualsiasi operazione "pesante" che possa essere eseguita all'interno dell'evento che Delphi generalmente richiama quando necessita di conoscere il valore di un campo calcolato (es. OnCalcFields); ad esempio, senz'altro non è consigliabile introdurre l'esecuzione di una query in un simile evento, poiché viene richiamato per ogni campo calcolato e per ogni record presente in tabella, ma in tutti gli altri casi (es. concatenamenti, calcolo di totali, ecc.) non vi sono problemi di alcun tipo ravvisabili in generale nell'uso dei campi calcolati.
    per ogni campo calcolato e per ogni record... mmmhhhh... e cosa significa "niente query"?
    Per me un buon suggerimento è quello di scegliere la strada migliore.
    Hai un motore mysql? evita come LA PESTE le operazioni per ogni record del tipo "first... while not eof... next" (cioè... proprio queste ad esempio).
    Le operazioni di calcolo e somma eseguita da mysql, in generale, sono ordini di grandezza più veloci di quelle delphi.
    Esattamente il contrario bisogna dire nel caso, ad esempio, di ABS. Cosa fare in questo caso? I totali (esempio) vanno calcolati-aggiornati volta per volta, non "riparti da zero e risomma tutte le righe".

    Ti chiedi perchè in certi casi dico una cosa, ed in altri il contrario? semplice, perchè so qual'è la strada migliore per ogni singolo caso, avendole ormai provate non dico "tutte", ma "quasi" tutte.

    Non applico pedissequamente la medesima strategia in tutti i casi, sperando "magicamente" che sia la scelta migliore.

    Esamino, penso, sperimento, sbaglio, verifico "cronometro" alla mano, decido.

    Questa è la differenza, per me, tra un professionista ed un dilettante.


    I momenti e le modalità con cui i campi calcolati vengono elaborati non sono "casuali": anche se il codice è scritto da terzi, cioè da chi produce Delphi di fatto, ciò non vuol dire che la gestione dei campi calcolati sia priva di logica.
    E chi lo sostiene?
    Il punto è "chi conosce MEGLIO la logica DEL MIO programma", IO, o cappuccetto rosso?
    Chi potrà minimizzare i ricalcoli inutili, IO o cappuccetto rosso?
    O magari decidere che mi va benissimo come fa il componente e "lasciarlo fare"?

    Questo dipende da quanti record ci sono nella tabella. In ogni caso, questo è esattamente ciò che fa il componente TClientDataSet: memorizzare i record provenienti da un DataSet qualunque in memoria, gestire i cambiamenti e, infine, inviare al server (o all'origine dati) le modifiche apportate in un "colpo solo".
    perchè, cosa ti aspettavi, che fosse "magico"?
    Avesse "poteri particolari", potesse fare "riti voodoo" negati agli umili programmi?

    Cosa vuol dire "posticciamente": un campo che si autoincrementa non ha nulla di differente rispetto a qualsiasi altro campo della tabella, se non che possiede un compito ben specifico che è quello di identificare univocamente un record in modo sicuro anche in ambito multiutenza.
    Ha qualcosa di fondamentalmente differente, ed è ovvio.
    Non decidi tu quale sia il suo valore.
    In realtà non lo decide neppure il motore SQL, o meglio non finquando non viene materializzato.
    "come" funziona è lungo da spiegare, e francamente non sono molto interessato a farlo.
    la "multiutenza" mi pare c'entri poco, anzi diciamo che non c'entra affatto, ma si vede che sei abituato a "delegare" a qualcun altro (in generale) invece di riflettere su come "ben" operare.
    Nulla di male, è solo un modus operandi.

    Una chiave legata all'oggetto descritto, come può essere il Codice Fiscale per un cliente di anagrafica, appartiene a un tipo più complesso rispetto a un campo intero autoincrementale
    Non è "più complesso", è "più lungo".
    Ma, in realtà, mai e poi mai mi verrebbe in mente di usare il codice fiscale, tanto per dirne una.
    Il motivo è ovvio.
    e ciò rallenta le ricerche, anche se basate sugli indici;
    Questo è banale.
    inoltre, se ogni tabella correlata a quella del cliente usa come campo di correlazione una stringa da 16 caratteri o più campi al posto di un unico campo autoincrementale, ad esempio, lo spazio occupato dai dati aumenta oltremodo a dismisura.
    Non direi proprio.
    "a dismisura"? dipende dalla dimensione della riga.
    Qui si sta parlando della testata (e delle righe) di documenti, che normalmente (e facilmente) hanno tra 50 e 100 campi ciascuna
    Spesso addirittura con cambi BLOB (text) per eventuali descrizioni super-lunghe.

    POI c'è la suddivisione in pagine dei DB, il che fa sì (vado sbrigativo, anche qui non ho una gran voglia di spiegare per filo e per segno, chi ha dubbi s'informi) tale per cui non ci vuol niente che un record con una certa chiave occupi il medesimo spazio di un record (record complesso, come sopra precisato) con una chiave più lunga.

    Provare, per credere.

    Nel caso di specie, questo "problemino" non si verifica mai, perché il campo autoincrementale avanza sempre di una unità e, una volta attribuito a un record, per definizione non può essere modificato.
    Sono tutte asserzioni false nel merito

    1) può aumentare anche PIU' di una unità. Leggiti il manuale mysql, ad esempio, se hai qualche dubbio.

    ma, soprattutto

    2) PUO' essere modificato (parlo sempre di mysql come esempio "classico") IN FASE DI RESTORE
    Nei dump mysql c'è il campo AUTO_INCREMENT che rappresenta il PROSSIMO valore che verrà dato al campo.
    Se non lo imposti con cura "a mano" rischi che, fatto il restore, ti trovi con TUTTE LE CHIAVI PRIMARIE CAMBIATE.
    Quando capita sono davvero (per usare le tue parole) "grossi problemi", te lo posso assicurare

    3) PUO' causare disastri nel momento di attività diciamo così "di manutenzione" (che capitano sempre nella vita reale).
    Estrai tutti i documenti di un anno, ricalcolali, ri-scrivili etc.

    Esiste in tutti i DB questo rischio? No, non in tutti.
    Vale la pena di correrlo?
    Per me no, manco pagato, ho già dovuto stimare i danni cagionati da chi non ne ha tenuto conto.

    "la chiave è mia, e me la gestisco io" (vabbè c'è un lieve doppiosenso... per sdrammatizzare!)

    E chi lo dice? Puoi ottenere il valore di quel campo come qualsiasi altro, così come puoi determinarlo con la totalità dei database SQL esistenti (MySQL, SQL Server, FireBird, ecc.), ed è proprio lo strumento che garantisce il corretto riferimento a uno specifico record di una tabella
    QUESTO è il suo "vero" utilizza naturale, fare il LOCATE di una riga (ad esempio dopo aver "salvato" la posizione corrente, come fosse un cursore)

    ma non questo

    (dato che utilizzare altri campi per la chiave primaria può comportare che questa cambi se si modificano i campi sui quali essa si basa, rendendo impossibile gestire le "collisioni" se queste modifiche ai campi chiave vengono fatti da più utenti, cosa che non accade se si usa un campo autoincrementale).
    ??? non è certo il campo autoincrementante a "supplire" ad una mancanza di logica nella gestione della multiutenza.
    E' ovvio (o almeno per me lo è) che i campi della chiave primaria non possano variare, per lo stesso motivo che sono... porzioni di chiave primaria a loro volta.

    E' proprio l'ABC della progettazione

    Ciò che rende portabile un database verso altri motori è lo standard SQL che si utilizza.
    Ma non direi proprio.
    Anzi ti dirò di più: il campo più difficile da "portare" è proprio (anzi il secondo, dopo il booleano) proprio il campo autoincrementante.

    Prova a portarlo verso postgres, o db2, tanto per divertirti un pochino con le "sequenze".
    Saranno un 10 anni che evolvono postgres proprio per avere dei "pseudoautoincrementanti" vagamente "semplici".
    "semplici" per modo di dire.

    Tutti i database sono "seri", anche perché non ne ho mai visto qualcuno "ridere".
    Io invece rido spesso quando li vedo, penso che avrei scritto di meglio quando avevo 15 anni.


    Fare il CRC delle chiavi di ogni record, sia quelli della tabella primaria che quelli della tabella secondaria da correlare, deve essere performante di brutto.
    Sì, ci vorranno diciamo una 50ina di cicli-macchina
    Poi, visto che sopra si parlava di "portabilità", sarebbe interessante vedere se è più portabile un campo autoincrementale (che esiste su ogni DB) rispetto a una funzione che calcola un CRC. La risposta, ovviamente, è implicita nell'ironia.
    Io direi nella non-conoscenza.

    Ma che ti credi, che nella QUERY ci metta la funzione che calcola il CRC (o l'SHA1?)

    Quella è "roba" che sta nell'applicazione, il DB vede solo un intero, una stringa o quello che vuoi

    "ogni db" poi mi ha fatto quasi ribaltare dalla sedia.

    metti "ogni db che (forse) conosco", mi sembra più corretto.

    Una chiave primaria univoca non deve mai dare luogo a collisioni, altrimenti la parola "univoca" cosa ci sta a fare?
    Vedi, ecco la differenza tra il "dilettante" e il "professionista"

    Il "dilettante" [quello che "pensa di sapere, ma in realtà ha una mera conoscenza per sentito dire"] ti direbbe: hai molte righe, non c'è problema.
    Usa SHA1 invece di CRC, e metti addirittura un "sale" random nella formulazione della chiave.
    Fine dei problemi di collisione, eccoti la tua chiave primaria.

    Il "professionista" ti dice invece: hai molte righe, ma non vuoi pagare il "costo" di una chiave primaria lunga?
    Bene, usa CRC, ma fai attenzione a mettere un duplice controllo.
    Ottieni sia i benefici di una chiave intera (quindi corta e veloce), sia eviti i rischi di "collisioni" (o meglio se capitano non ti succede nulla).

    Poi, già che c'è, ti direbbe anche "stai attento, giovane padawan. Se usi mysql-innodb e non definisci una chiave primaria, te la mette lui nascosta, proprio autoincrementante" e così via.

    In un sistema di gestione documentale, ad esempio, quando la "collisione" fortuita porta un cliente a visualizzare la fattura di un altro, sono problemi grossi...
    Non ti so dire, pensa che scrivo e vendo sistemi di gestione documentali a centinaia di utenti con (complessivamente) centinaia di milioni di documenti gestiti... e mai nessun problema, nè grande nè piccolo , mentre per lavoro devo dire quali sono stati gli errori delle principali software house italiane nel fare i loro programmi... la vita è proprio... ironica!
    "Mente aperta" significa essere permeabili ai cambiamenti e alle metodologie che nascono per uno sviluppo corretto del software, non essere inclini a qualsiasi tipo di soluzione da "macellai dello sviluppo software", secondo me.
    Secondo me invece significa essere inclini a lavorare con soluzioni sartoriali, professionali, sicure e di alte prestazioni e non standard del tipo "autocomposizione avanti-avanti-avanti e speriamo bene", quello per me è "macelleria-dello-sviluppo-di-spazzatura", manco lo chiamo software

    Come tu possa affermare qualcosa e, subito dopo, il suo contrario, per me rimane sempre affascinante.

    Ciao!
    per me invece rimane affascinate discutere con chi (da quanto scrive) non ha la minima idea di cosa sta trattando, però almeno mi rallegra la giornata!

  6. #26
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    24,466

    Prima parte

    Originariamente inviato da franzauker
    Meraviglioso poi quando uno comincia con "non conosco qualcosa, ma ne voglio discutere lo stesso". Normalmente per me il discorso si concluderebbe qui.
    Ciò di cui ho parlato riguarda argomenti generali di Delphi, quindi sono argomenti che conosco.


    Originariamente inviato da franzauker
    E' una bella asserzione. Peccato rimanga solo questo, ossia un'asserzione. Meno addirittura di un'opinione. Assai interessante poi il "generalmente".
    E' un'asserzione fondata su verità, poiché il meccanismo dei campi calcolati non esiste da ieri, ma da anni, e ha sempre funzionato in un modo preciso, da qui il termine "generalmente" che, ovviamente, aveva un'accezione ironica, e non stava per un "forse".

    I campi calcolati funzionano "a caso", oppure male, oppure hanno qualche bug?
    Fai qualche esempio.


    Originariamente inviato da franzauker
    per ogni campo calcolato e per ogni record... mmmhhhh... e cosa significa "niente query"?
    Vuol dire che se devi gestire un campo calcolato che, per ogni record, unisce il nome al cognome per esigenze di visualizzazione, ad esempio, non comporta alcun decadimento significativo di performance. Se invece il campo viene calcolato facendo una query, ad esempio, è chiaro che le cose sono diverse.

    Conclusione del discorso: i campi calcolati hanno un funzionamento preciso, non vanno a caso, e conoscendo il loro funzionamento vanno utilizzati correttamente, e non nel modo errato, e se usati correttamente svolgono egregiamente il loro compito senza problemi, a meno che non sia lo sviluppatore a introdurli perché non sa come lavorano, oppure escludendoli a priori perché inclusi in questa sfera di "oggetti diabolici" più astrattamente targati da te come "automatismo.


    Originariamente inviato da franzauker
    Hai un motore mysql? evita come LA PESTE le operazioni per ogni record del tipo "first... while not eof... next" (cioè... proprio queste ad esempio).
    Non c'è nulla di strano nell'usare i metodi che hai indicato, vengono usati ovunque, da chiunque, tranne da te, che li sconsigli, ma non dici perché.

    Porta argomentazioni tecniche, per favore.

    Aprire un cursore su una query SQL di MySQL e scorrerla record per record non è un'operazione da evitare come la peste, ma è un'operazione del tutto normale.

    Non si capisce da dove arrivano questi tuoi suggerimenti: se sono basati su considerazioni diffuse, cita le fonti, altrimenti se sono basati su esperienze personali, cita i motivi dal punto di vista tecnico.


    Originariamente inviato da franzauker
    Le operazioni di calcolo e somma eseguita da mysql, in generale, sono ordini di grandezza più veloci di quelle delphi.
    E allora? Certo che sono (tendenzialmente) più veloci in quanto eseguite dal server; infatti, pur esistendo "campi calcolati" in Delphi, non è obbligatorio utilizzarli *sempre*, ma ciò non vuol dire che vadano "evitati come la peste": ogni strumento ha il suo scopo, così come un rastrello raccoglie le foglie e una ramazza pulisce per terra... il giusto sta utilizzare ogni strumento per il suo scopo, e non sconsigliare l'uno o l'altro in senso assoluto senza calarlo nel contesto in cui viene impiegato.

    Per esemplificare (come faccio sempre, ma lo faccio solo io a quanto pare): è senz'altro conveniente usare campi calcolati lato database SQL, se possibile, però occorre tenere a mente che il database utilizza appunto SQL, e tale linguaggio non consente operazioni complesse che invece si potrebbero facilmente ottenere usando il linguaggio Delphi all'interno di un evento, avendo a disposizione tutto il set di istruzioni fornite dallo stesso linguaggio.

    L'uso poi va scelto in base ai casi, senza escludere né una soluzione né l'altra a priori, ma valutando quello che serve.


    Originariamente inviato da franzauker
    Esattamente il contrario bisogna dire nel caso, ad esempio, di ABS. Cosa fare in questo caso? I totali (esempio) vanno calcolati-aggiornati volta per volta, non "riparti da zero e risomma tutte le righe".
    Bisogna intendersi su ciò di cui si sta parlando: i totali non sono campi calcolati, ma si chiamano campi aggregati (Aggregates, in Delphi): i campi calcolati sono quelli presenti in ciascun record che contengono un valore specifico per quel record calcolato in qualche modo (ad esempio, determinato dalla concatenazione di altri campi dello stesso record). I campi aggregati, invece, sono quelli basati su una funzione (di aggregazione, appunto) che determina un valore ottenuto dall'applicazione di quella funzione a tutti i record della query.


    Originariamente inviato da franzauker
    Ti chiedi perchè in certi casi dico una cosa, ed in altri il contrario? semplice, perchè so qual'è la strada migliore per ogni singolo caso, avendole ormai provate non dico "tutte", ma "quasi" tutte.
    Modesto...

    Originariamente inviato da franzauker
    E chi lo sostiene?
    Il codice sorgente di Delphi!


    Originariamente inviato da franzauker
    Il punto è "chi conosce MEGLIO la logica DEL MIO programma", IO, o cappuccetto rosso?
    Ma perché usi Delphi e non sviluppi con le API, allora?


    Originariamente inviato da franzauker
    Chi potrà minimizzare i ricalcoli inutili, IO o cappuccetto rosso?
    Un ingegnere informatico esperto di algoritmi, probabilmente (ipotizzo) con più conoscenze e dedizione sul problema di te (solo per il fatto che nello sviluppo software non puoi ovviamente occuparti di tutto) e senz'altro più di Cappuccetto Rosso, senza dubbio.

    Certo, esistono software con bug, e senz'altro i tuoi programmi non ne sono esenti, ma tu non puoi dire - senza argomentare e dimostrarlo tecnicamente con riferimento a difetti conosciuti o parti di codice sorgente - che qualcosa non funzioni solo perché non l'hai fatto tu, o al contrario che funzioni meglio solo perché l'hai fatto tu.

    O meglio, puoi dirlo, ma non ci sono prove, riscontri, fatti su cui poterlo determinare.


    Originariamente inviato da franzauker
    Non decidi tu quale sia il suo valore.
    Bene, e allora? E' normale che sia così: quel tipo di campo è progettato esattamente per fare questo. Il fatto di non poter decidere qual è il suo valore e che si incrementi automaticamente in ambito multiutente è il fulcro della sua garanzia, e sarebbe un guaio se fosse diverso.

    Non hai citato un difetto, ma una definizione tecnica di ciò che rappresenta quel campo.


    Originariamente inviato da franzauker
    In realtà non lo decide neppure il motore SQL, o meglio non finquando non viene materializzato.
    Lo deciderà nostro Signore.
    Poi non si capisce cosa si intende con "materializzato", non essendo un fantasma.

    Il valore viene determinato nel momento in cui al motore SQL di riferimento viene richiesto, e in tal sede viene generato.


    Originariamente inviato da franzauker
    "come" funziona è lungo da spiegare, e francamente non sono molto interessato a farlo.
    la "multiutenza" mi pare c'entri poco, anzi diciamo che non c'entra affatto, ma si vede che sei abituato a "delegare" a qualcun altro (in generale) invece di riflettere su come "ben" operare.
    Nulla di male, è solo un modus operandi.
    Questo è puro "bla bla bla". Non sei interessato a discutere, escludi un ambito in cui i contatori o i generatori autoincrementali sono generalmente impiegati (la multiutenza) senza motivarlo, poi fai assunzioni sul mio conto senza conoscermi, dicendo poi che non rifletto, senza uno straccio di giustificazione.

    Originariamente inviato da franzauker
    Non è "più complesso", è "più lungo".
    E' più "complesso", perché può includere più campi, o se si tratta di un campo solo deve essere ottenuto da un concatenamento, ed è più "lungo" (come giustamente riconosci) perché difficilmente sarà più corto di un campo intero.

    Originariamente inviato da franzauker
    Ma, in realtà, mai e poi mai mi verrebbe in mente di usare il codice fiscale, tanto per dirne una.
    Un campo con un codice fiscale, intanto, non è detto che sia meno performante di una coppia di campi, anno e numero di documento (quelli che hai suggerito), se il campo numero documento non è strettamente numerico (e in molti casi si usano anche lettere).

    In ogni caso, resta il fatto che una chiave non numerica è sempre più dispendiosa in termini di spazio e lenta in termini di performance.


    Originariamente inviato da franzauker
    Il motivo è ovvio.
    Questo è banale.
    E' tutto ovvio e banale, ma sono tutte le soluzioni che proponi tu.


    Originariamente inviato da franzauker
    "a dismisura"? dipende dalla dimensione della riga.
    Qui si sta parlando della testata (e delle righe) di documenti, che normalmente (e facilmente) hanno tra 50 e 100 campi ciascuna
    Dipende dalla dimensione della riga se consideriamo solo la tabella che contiene quella chiave, ma in genere la chiave viene "correlata", cioè vi saranno facilmente altre tabelle che fanno riferimento alla tabella in questione e per associare un record dell'una all'altra è necessario correlare le chiavi, pertanto anche la tabella secondaria, il cui numero di record non può essere conosciuto a priori, avrà tanti valori che saranno più spreconi di spazio tanto quanto è lunga la chiave, e via via discorrendo per tutte le tabelle che andrai a correlare.

    In breve, l'uso di una chiave numerica in primis è senz'altro preferibile per i motivi detti sopra (poi nulla vieta di costruirsi un indice univoco per impedire duplicazioni di campi specifici o velocizzare le ricerche basate su un insieme preciso di campi).

    Questo porta alla seconda considerazione: qual è quel tipo di campo basato su un intero, quindi valido come chiave, di cui però non si vorrebbe gestire manualmente l'incremento e che dovrebbe essere garantito come progressivo anche quando al database accedono più utenti e nel contesto di più transazioni possibili? Toh, guarda un po', è il contatore.


    Originariamente inviato da franzauker
    POI c'è la suddivisione in pagine dei DB, il che fa sì (vado sbrigativo, anche qui non ho una gran voglia di spiegare per filo e per segno, chi ha dubbi s'informi) tale per cui non ci vuol niente che un record con una certa chiave occupi il medesimo spazio di un record (record complesso, come sopra precisato) con una chiave più lunga.
    La suddivisione in pagine dei DB riguarda l'indice associato alla chiave, quindi puoi crearlo comunque anche basando la chiave primaria su un altro campo.

    In secondo luogo, non è affatto vero che l'indicizzazione di un campo intero (con le pagine o con quello che vuoi) occupi lo stesso spazio dell'indicizzazione di un campo stringa, o di più campi: tu crei un indice in cui specifichi la posizione di una determinata serie di valori e, in questo indice, secondo te, elencare un insieme di valori interi e un insieme di stringhe è la stessa cosa? cioè, occupano lo stesso spazio?


    Originariamente inviato da franzauker
    Provare, per credere.
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Home | Blog | Delphi Podcast | Twitch | Altro...

  7. #27
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    24,466

    Seconda parte

    Originariamente inviato da franzauker
    Sono tutte asserzioni false nel merito
    1) può aumentare anche PIU' di una unità. Leggiti il manuale mysql, ad esempio, se hai qualche dubbio.
    La mia frase serviva a sottolineare che il valore del contatore viene sempre incrementato nelle sue condizioni di utilizzo normali, poi è chiaro che ogni database SQL applica la sua "personalizzazione" delle opzioni, come ad esempio stabilire un valore iniziale, un valore di incremento, o addirittura un valore casuale.

    La parte importante che andava capita è appunto la gestione automatizzata del contatore senza possibilità/necessità di intervento, che poi è la base del funzionamento del contatore.

    Tu hai "guardato il dito che indica la luna".

    Visto che ci siamo, anticipo che sono a conoscenza del fatto che il contatore si possa "spegnere" o disattivare, se occorre... non dovrebbe essere necessario precisarlo, ma visto che qui facciamo il pelo e contropelo alle affermazioni superflue.

    Spero di non aver dimenticato qualche casistica...


    Originariamente inviato da franzauker
    2) PUO' essere modificato (parlo sempre di mysql come esempio "classico") IN FASE DI RESTORE
    Nei dump mysql c'è il campo AUTO_INCREMENT che rappresenta il PROSSIMO valore che verrà dato al campo.
    Se non lo imposti con cura "a mano" rischi che, fatto il restore, ti trovi con TUTTE LE CHIAVI PRIMARIE CAMBIATE.
    Quando capita sono davvero (per usare le tue parole) "grossi problemi", te lo posso assicurare
    Ho già risposto sopra. In ogni caso, se sbagli lo script SQL, rischi di scrivere il valore di un campo all'interno di un altro... ma che ragionamenti sono?

    La conoscenza del database, del suo funzionamento e del linguaggio per programmarlo si suppone che siano noti a chi ci opera sopra e svolge delle operazioni, altrimenti non c'è nulla che possa impedire il danneggiamento dei dati, a prescindere dall'uso o meno di un campo incrementale.


    Originariamente inviato da franzauker
    3) PUO' causare disastri nel momento di attività diciamo così "di manutenzione" (che capitano sempre nella vita reale).
    Estrai tutti i documenti di un anno, ricalcolali, ri-scrivili etc.
    Se usato correttamente, non ci sono disastri: quando chi usa il database sa come va usato, se io chiedo al database di comportarsi in un certo modo, il database lo fa, senza sbagliarsi, ergo il disastro avviene solo se chi fa le operazioni non sa quello che sta facendo, che è tutta un'altra questione e tutto un altro problema.


    Originariamente inviato da franzauker
    Esiste in tutti i DB questo rischio? No, non in tutti.
    Sì, per tutti i DB esiste il rischio che chi lo manipola non sappia cosa sta facendo.
    In tutti gli altri casi, il DBA chiede, il motore SQL esegue.


    Originariamente inviato da franzauker
    Per me no, manco pagato, ho già dovuto stimare i danni cagionati da chi non ne ha tenuto conto.
    Esatto: il problema è di chi non tiene conto di come funziona il DB, non del DB e degli strumenti che mette a disposizione. In questo caso, la soluzione non è evitare un contatore, ma formare chi lavora.


    Originariamente inviato da franzauker
    QUESTO è il suo "vero" utilizza naturale, fare il LOCATE di una riga (ad esempio dopo aver "salvato" la posizione corrente, come fosse un cursore)
    Le chiavi primarie, gli indici e i contatori non hanno un Contratto Nazionale del Lavoro: il "vero" utilizzo che se ne fa è quello che giustifica, a livello di performance e occupazione, il loro impiego.


    Originariamente inviato da franzauker
    ??? non è certo il campo autoincrementante a "supplire" ad una mancanza di logica nella gestione della multiutenza.
    Non ho detto questo. Se contesti quello che non dico, sfido che non ci saltiamo fuori.

    Il campo autoincrementale è la base dell'uso del database corretto in multiutenza, poi è chiaro che non è sufficiente e che il software ne deve tenere conto.

    Non ho mai affermato che, per rendere un software multiutente, sia sufficiente usare un contatore. E ci mancherebbe.


    Originariamente inviato da franzauker
    E' ovvio (o almeno per me lo è) che i campi della chiave primaria non possano variare, per lo stesso motivo che sono... porzioni di chiave primaria a loro volta.
    Non sta scritto da nessuna parte che la chiave primaria NON possa mai variare: la chiave primaria, per definizione, deve essere univoca, e basta, ma che non possa variare non l'ha mai detto nessuno.

    La considerazione è: se si usa un contatore intero, si ha la garanzia di univocità, si ha la minor occupazione di spazio, si ha la minor probabilità (direi anzi "zero") che questa cambi generando aggiornamenti in cascata, si ha anche la semplicità intrinseca del tipo di valore stesso per l'identificazione del record, si ha la garanzia dell'univocità anche in ambito di multiutenza, e anche indipendentemente dalle transazioni.

    Ciò che per te è ovvio, cioè che la chiave che definisci non possa cambiare, in realtà potrebbe non essere più ovvio un domani (in base ai campi scelti) e non potrebbe esserlo per altre persone che lavorano (visto che prima hai parlato di persone che "non hanno tenuto conto" di alcuni aspetti del DB): se si usa un contatore debitamente configurato (lo specifico perché poi me lo rinfacci), a meno che non si voglia intenzionalmente variarne il flusso di lavoro, il dato non si modifica e non si altera, e in ogni caso la sua univocità e automazione sono evidenti dal tipo di campo scelto, e non da condizioni logiche che - seppur documentate - potrebbero essere tralasciate.


    Originariamente inviato da franzauker
    Ma non direi proprio.
    Anzi ti dirò di più: il campo più difficile da "portare" è proprio (anzi il secondo, dopo il booleano) proprio il campo autoincrementante.
    Se indirizzi motori di database differenti, è senz'altro necessario creare script specifici poiché vi sono già molteplici differenze nelle istruzioni base SQL rispetto allo standard.

    Il punto che io stavo rimarcando è un altro: un campo contatore (se scelto come intero) è accessibile a qualsiasi driver/applicazione.

    Ciò che va ritoccato, al massimo, è la sintassi di dichiarazione del campo.

    Poi, è chiaro che se si preferisce sprecare spazio e performance per applicare una modifica in meno per ogni tabella a uno script SQL che comunque va "personalizzato" per il motore a cui viene applicato, tutto è lecito.


    Originariamente inviato da franzauker
    Sì, ci vorranno diciamo una 50ina di cicli-macchina
    Se anche ce ne volesse solo uno e mezzo, è un ciclo di macchina che nel mio caso viene risparmiato, nel tuo caso viene fatto, e per ogni record da correlare da ambo le tabelle.

    E per cosa poi? Per ottenere un valore numerico univoco, che già avresti se utilizzassi un contatore, risparmiando fatica nella localizzazione del record, nella correlazione dello stesso e in tutti gli altri contesti in cui questo valore è già disponibile e pronto all'uso.

    Se hai avuto problemi con gente che non sapeva gestire opportunamente i contatori, figuriamoci se ci saltano fuori col CRC.


    Originariamente inviato da franzauker
    Io direi nella non-conoscenza.
    Attendiamo ancora dimostrazione...



    Originariamente inviato da franzauker
    Ma che ti credi, che nella QUERY ci metta la funzione che calcola il CRC (o l'SHA1?)
    Quella è "roba" che sta nell'applicazione, il DB vede solo un intero, una stringa o quello che vuoi
    "ogni db" poi mi ha fatto quasi ribaltare dalla sedia.
    metti "ogni db che (forse) conosco", mi sembra più corretto.
    Se tu fossi bravo ad argomentare quanto ad offendere, saremmo a posto.



    Originariamente inviato da franzauker
    Vedi, ecco la differenza tra il "dilettante" e il "professionista"
    Il "dilettante" [quello che "pensa di sapere, ma in realtà ha una mera conoscenza per sentito dire"] ti direbbe: hai molte righe, non c'è problema.
    Usa SHA1 invece di CRC, e metti addirittura un "sale" random nella formulazione della chiave.
    Fine dei problemi di collisione, eccoti la tua chiave primaria.
    Il "professionista" ti dice invece: hai molte righe, ma non vuoi pagare il "costo" di una chiave primaria lunga?
    Bene, usa CRC, ma fai attenzione a mettere un duplice controllo.
    Ottieni sia i benefici di una chiave intera (quindi corta e veloce), sia eviti i rischi di "collisioni" (o meglio se capitano non ti succede nulla).
    Io invece - che mi ritengo semplicemente una persona normale - dico usa un contatore, così puoi fottertene dell'inefficienza di algoritmi di hashing e crypting che nel contesto non danno alcun vantaggio se non qualcosa in più da gestire o da controllare opportunamente per fare sì che tutto il sistema produca gli stessi vantaggi ed effetti rispetto all'avere, semplicemente, un contatore.


    Originariamente inviato da franzauker
    Poi, già che c'è, ti direbbe anche "stai attento, giovane padawan. Se usi mysql-innodb e non definisci una chiave primaria, te la mette lui nascosta, proprio autoincrementante" e così via.
    Caspita, chissà perché mai lo fanno... non sanno che se usano CRC, SHA1 e fanno due capriole all'indietro possono tranquillamente evitare di farlo?

    Bah, in effetti, che cavolo vuoi che ne sappiano quelli che sviluppano MySQL di MySQL...

    Il codice non l'ha scritto "franzauker", che dice di aver visto tutto, quindi ci deve essere senz'altro qualcosa che non va o un ragionamento sbagliato da qualche parte...



    Originariamente inviato da franzauker
    Non ti so dire, pensa che scrivo e vendo sistemi di gestione documentali a centinaia di utenti con (complessivamente) centinaia di milioni di documenti gestiti... e mai nessun problema, nè grande nè piccolo , mentre per lavoro devo dire quali sono stati gli errori delle principali software house italiane nel fare i loro programmi... la vita è proprio... ironica!
    Sul fatto che la vita sia ironica sono proprio d'accordo, però non per il motivo che hai detto tu.

    Comunque, tutte queste cose io non le posso verificare, né mi posso esprimere, per cui puoi dire di essere pure Bill Gates per quanto mi riguarda che cambia ben poco: lo dico per indicare che la personale autoincensazione non è uno degli elementi su cui attribuisco le mie valutazioni, le ragioni o i torti.



    Originariamente inviato da franzauker
    Secondo me invece significa essere inclini a lavorare con soluzioni sartoriali, professionali, sicure e di alte prestazioni e non standard del tipo "autocomposizione avanti-avanti-avanti e speriamo bene", quello per me è "macelleria-dello-sviluppo-di-spazzatura", manco lo chiamo software
    Quella è una cosa ben diversa, ed è senz'altro tutto ciò a cui NON mi riferivo.

    Soluzioni e metodologie si studiano ovunque nel mondo.

    Tutto questo non c'entra nulla con "autocomposizioni" o altre cose del genere, che sono questioni totalmente scollegate.

    Il discorso comunque è astratto, mentre si stava ragionando su questioni ben specifiche.



    Originariamente inviato da franzauker
    per me invece rimane affascinate discutere con chi (da quanto scrive) non ha la minima idea di cosa sta trattando, però almeno mi rallegra la giornata!
    Hanno ragione a dire che c'è tante gente che si diverte con poco.
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Home | Blog | Delphi Podcast | Twitch | Altro...

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.