12. februar 2004 - 23:44Der er
34 kommentarer og 1 løsning
en ADOQuery og listboks
En ADOQuery lægger en hel række fra en access database ind i en listboks.. nu ville det være rart om jeg kunne klikke på en af dem i listboksen som er kommet til syne og så rettede resten at databasen sig ind og fandt alle dem der hørte med til det ID nummer..og dermed alle de andre som er i databasen så de vises i hvert deres respektive felt eller DBedit boks, som det jo er.. Kan det lade sig gøre ?
Hvis du får resultatet af din query ud i en grid - vil resultatet af de enkelte records jo blive vist når du clicker i gridden.Så hvorfor ikke lægge queryen ind i en listbox.En dynamisk tabelvisning ville self. være flot samtidig, men ville vel kræve at alle tabellerne havde den samme struktur ?.
Jeg kan ikke bruge det i skriver til noget , så jeg har prøvet af omformulerer det :
En DBListbox (eller en alm listbox) sætter en ADOTable felt mavn ind fra en database. Feltet hedder navn, men når jeg klikker på et af de navne der vists i DBListbox kommer resten af det som høre til databasen ikke med, eller de vises ikke. DBListbox kan altså ikke bruges til at "gå frem eller tilbage" med i databasen.. Det ville ellers være smart hvis den kunne det... Kan det lade sig gøre ? Databasen har en id autonummer som den rettes sig efter måske er det detfor, for jeg er næsten sikker på at hvis jeg satte DBListbox til at visse alle id nummer som er primary key som ville den køre hen til den jeg klikkede på. Hvordan for jeg den til det sammen selv om det ikke er id nummer ?
Måske det hjælper.... Jeg har også læst i mine for 2000 kr Delphi bøger, ingenting ? Jeg har været på www og søge (Google er din ven ? ) ingenting ? ØV jeg savner Visual Basic, der ku jeg....
Ja, det er nok derfor koden12 skriver det på den 'mærkelige' måde - en variabel -aha.Og dem kan man altsaa bruge i queryen.Ja jeg bliver altid lidt klogere af koden12.s spm. selvom jeg ofte ikke forstår dem primært.Smart.Det må jeg prøve. Så på dolphin.com at man kan bruge 'underscores' til at finde specifikke karakterer: (se nedenfor), men det er self. ikke nær så smart som at bruge en variabel. To match a single character as opposed to any number of characters, use the underscore (“_”) character. An example might be the following query which selects records where the second letter of Category is ‘r’:
Koden12: du vil have vist alle dine records, som jeg forstår det.Du kommer i første linie meget hurtigt henover Databasen-Tabellerne- og frem til feltnavnet.Det er jo her det kommer til at 'knage' afh. af om du vælger en fast tabel (ADOTABLE som source) eller du bruger en ADOconnection.I sidstnævnte tilfælde, hvor du har dynamikken til at vælge mellem mange tabeller i din database har du ikke umiddelbart så mange muligheder for at få vist detailler (memo bl.a., der ikke vises i en grid), mens du kan få de detailler du vil hvis du sætter dig fast på tabellen.Årsagen må være den simple ting at strukturen i de forskellige tabeller jo nødvendigvis ikke er ens?.En smart måde at omgå det på kan vel være - som du selv åbenbart osse har 'leget' lidt med: at lade 'visningen' afhænge af nogle SQL-kommandoer, hvor 'Sourcen' kan sættes 'dynamisk', hvis man laver en 'close'+'open' manøvre hver gang man ændrer sin query.
<<koden12 Jeg vil gerne hjælpe dig, men gider ikke lave overflødigt arbejde! Du har en post, der fx. er opbygget således: ID: Primary key Navn: String Adresse: String By: String Tlf: String ... Du får vis alle dine navne i en DBListboks vha en ADOQuery. Når du klikker på et navn i listboksen, vil du gerne ha' retsen af posten vist i diverse DBEdit eller blot alm, labes.
nca : Jes, det er helt præsis..(det er nu telefon og ikke navn) men det er lige meget..undskyld hvis jeg lød lidt knotten, her valetins dag... Det burde jo være let ? og underligt at de ikke køre sammen..
Ja egentligt er det vel ligelyldigt om det er ADOQuery eller ADOTable det bliver brugt , det er bare mærkeligt at ikke DBListbox følger de andre ved klik på en af navnene.. kun ved hjælp af Navigasionspanelet kan det gå frem og tilbage.. Om det er listbox eller DBlistbox er vil også det samme, de flytter sig ikke når det klikkes på en af dem man gerne vil se.. det burde vel være normalt.. og indlagt fast i den at det skal den... Eller er jeg helt gal på den ?
Jeg synesi hvert fald at det er en avanceret ting, bla. a. fordi gevinsten ved at have det i en listbox er lidt svær at få øje på når gridden er opfundet, men det nærmeste min hukommelse rækker til er nogle 'gamle' programmer her fra E, hvor man brugte ADOCommand og hvor man brugte listboxvisningen til at delete records med et specifikt tlf-nr.Er ikke dygtig nok til at videreudvikle metoden, da der er mange ting jeg ikke forstår endnu (mousedown osv), men smider lige en kopi at den ene procedure, så vi kan kigge lidt på den .-) procedure TForm1.Button9Click(Sender: TObject); begin
if ListBox3.ItemIndex >= 0 then begin ADOCommand1.CommandText := 'DELETE FROM tabel WHERE phone = :phone'; ADOCommand1.Parameters.ParamByName('phone').Value := ListBox3.Items.Strings[ListBox3.ItemIndex]; ADOCommand1.Execute; end else Showmessage('Du har ikke valgt et item!');
At gevinsten er lille er nu ikke rigtigt, da jeg tænker at den Form1 kan have listboksen og ved klik på en af dem åbner fx form2 på det rigtige sted således at fx " et firma kan have flere ansatte" og klik komme de ansatte. Jeg kan godt huske vores Grid : ) den er også go og jeg bruger den som en slags søgning ? men helt perfekt syntes jeg ikke den er..Især ikke da den ikke indeholder memo felter.. Meno(notat) felter fik jeg aldrig til at virke..
procedure TForm1.Button1Click(Sender: TObject); var x:Integer; begin for x:=0 to adotable1.Recordset.RecordCount -1 do begin listbox1.Items.Add(ADOTable1.FieldByName('Navn').AsString); ADOTable1.Next; end; ADOTable1.First; end;
procedure TForm1.ListBox1Click(Sender: TObject); var AltOK: Boolean; begin AltOK:=ADOTable1.Locate('Navn',ListBox1.Items[Listbox1.ItemIndex],[loCaseInsensitive]); end;
Forklaring: Jeg har en ADOconnection, som peger på databasen. Jeg har en ADOTable, som peger på navnetabellen i databasen Jeg har en Datasource som har ADOTable som dataset Jeg har en almindelig listboks. Derudover har jeg det ønskede antal DBLabels, som alle har DataSource1 som datasource og som så har forskellige Datafields fra tabellen, fx. Navn Ved et klik på Button1 får jeg fyldt alle navnene fra databasen ind i listboksen. Sætter en OnClick event på listboksen. Når jeg klikker på et navn i listboksen, bliver der lavet en søgning i databasen. Dette flytter den aktuelle pegepind i databasen og derved bliver også indholdet i de forskellige DBLabels opdateret.
Hvis du orker... send en forklaring ! jeg vil meget gerne forstå det.. Jeg kan godt se at den selvfølgelig skal tælle det op først ! ADOTable1.local ??? hvad står local for ? (jeg ved det godt jeg burde have sat 100 point på den )
Kan jeg på nogle måder sende dem hemmeligt til dig ?
Tord Samdal: Delphiapplikasjoner mot SQL-databaser
Forutsetninger for å ha utbytte av dette skrivet er 1) God kjennskap til Delphi som utviklingsverktøy 2) Kjennskap til databaser og SQL
SQL-språket er tredelt, et DDL (Data Definition Language), et DML (Data Manipulation Language) og et DCL (Data Control Language). I dette skrivet skal vi holde oss til DDL og DML-delen. Vi skal også utføre spørringene mot lokale dBase- og Paradoxtabeller, men i og med at vi holder oss til SQL, blir prinsippene de samme som om vi f.eks. skulle ha forholdt oss til en Oracle-database.
Hjelpefilen for LocalSQL (Borlands versjon av SQL for Delphi) ligger litt bortgjemt i c:\Programfiler\CommonFiles/Borland Shared/Bde/Localsql.hlp. Denne bør du ha lett for hånden.
1. Aktuelle komponenter Databasekomponentene å finne på to "flipper" i Delphi, "Data Access" og "Data Controls." På Data Access ligger komponentene som sørger for at applikasjonen kan knyttes opp til databasefiler, og på Data Controls ligger komponentene som sørger for at dataene kan vises fram på formen.¨
For å knytte oss opp mot tabeller, trenger vi to komponenter, en Query-komponent og en DataSource-komponent. Det kan kanskje virke tungvindt at man må bruke to komponenter for å aksessere databaser, men slik er det.. Query-komponenten har en SQL-property, som gjør det mulig for oss å utføre spørringer. Så la oss først sette ut to slike på en form, og se litt nærmere på Query-komponenten.
Og her er Query-komponentens propertyliste:
2. SQL-spørringer i Delphi 2.1 Komme i gang Som eksempel skal vi bruke to tabeller, merke.dbf og modell.dbf som inneholder data for hhv. bilmerker og bilmodeller (Disse kan du finne på i:\laerer\tords\N6100 Databaser). Strukturen er slik:
Sett ut tre komponenter på en form, en Query, en DataSource, og en DBGrid. Så setter du noen få propertyer: I Query1: DatabaseName: m:\ (eller den katalogen hvor du har de to databasefilene liggende)
I DataSource1: DataSet: Query1
I DBGriden: DataSource: DataSource1
For å teste at dette virker kan du skrive inn denne setningen i Query1 sin SQL: Select * from merke (Legg merke til at du ikke skal ha med filetternavn!)
Hvis du deretter setter Query1 sin Active-property til True, skal dbGriden bli fylt av bilmerkeinformasjon.
Hvis det ikke skjedde: 1. Satte du Queryens DatabaseName til den katalogen hvor tabellen ligger? 2. Skrev du Selectsetningen riktig? 3. Brukte du riktig tabellnavn i Selectsetningen? 4. Har du satt Query1 som DataSet-property i DataSource1? 5. Har du satt DataSet1 som dbGridens DataSet-property? 6. Finnes virkelig tabellen i den angitte katalogen?
2.2 Spørringer ved hjelp av SQL-propertyen Den enkleste måten å lage spørringer på, er å bruke Queryens SQL-property. Denne propertyen er av TStrings-klassen. Det betyr at vi programmeringsmessig kan lage spørringer ved å bruke Add-metoden, slik: Query1.SQL.Add('SELECT * FROM MERKE');
Men før vi kommer så langt er det tre ting som må programmeres: 1. Queryen må lukkes med Close. 2. SQL-propertyen må nullstilles. 3. Queryen må nullstilles med metoden UnPrepare.
Punkt 1 og 3 er punkter som strengt tatt ikke er nødvendig, men ved å kommandere Close først sikrer vi oss at Queryen virkelig er lukket og dermed klar til å endres på. UnPrepare gjør at eventuelle unødvendige ressurser som Queryen binder blir frigjort.
Når så SQL-setningen er lagt inn, bruker vi først Prepare, som nå betyr "Optimaliser denne spørringen". For å utføre selve spørringen er det to metoder, Open og ExecSQL. Tommelfingerregelen er å bruke Open-metoden på Selectsetninger, og ExecSQL på alt annet. Vi lager nå en egen metode KjoerSporring som kan kalles når vi vil ha utført spørringen. Metoden blir slik: procedure TForm1.KjoerSporring; begin Query1.Close; Query1.SQL.Clear; Query1.UnPrepare; Query1.SQL.Add('SELECT * FROM MERKE'); Query1.Prepare; Query1.Open; end;
Vi kan også bruke det resereverte ordet with på denne måten, som gir akkurat det samme resultatet: procedure TForm1.KjoerSporring; begin with Query1 do begin Close; SQL.Clear; UnPrepare; SQL.Add('SELECT * FROM MERKE'); Prepare; Open; end; end;
Prøvekjør dette ved f.eks. å legge inn denne koden i et knappeklikk:
procedure TForm1.Button1Click(Sender: TObject); begin KjoerSporring; end;
La oss så lage dette med litt større mulighet for variasjon. Husk at når vi bruker Add-metoden blir en ny linje lagt til TStringsobjektet. For SQL-setningen sin del spiller det ikke noen rolle hvor mange linjer vi sprer spørringen utover. Følgende måte å gjøre det på er like riktig som den forrige: SQL.Add('SELECT'); SQL.Add('*'); SQL.Add('FROM'); SQL.Add('MERKE');
Dette kan vi utnytte. Vi lager først tre string-variabler som vi kaller kolonner, tabeller og betingelse. Så tilordner vi verdier, enten direkte slik som det følgende, eller f.eks. som følge av brukerens klikking i listebokser e.l.: kolonner := 'merke.Merkenavn, Modellnavn'; tabeller := 'merke, modell'; betingelse := 'merke.Merkenavn = modell.Merkenavn'; og så setter vi disse inn i Select-setningen på denne måten:
På denne måten kan vi bygge opp de forskjelligste spørringer, bare ved hjelp av en Querykomponent.
2.3 Parameterspørringer Et alternativ til å bruke SQL-propertyen, er å bruke Params-propertyen. Gitt at man gjør det riktig gir den like stor fleksibilitet. Params-propertyen er et array av verdier. Disse settes inn i "runtime" og brukes så i Selectsetningen. Gitt følgende Selectsetning: SELECT * FROM merke WHERE merkenavn = 'AUDI'
I SQL finnes det noe som heter parameterspørringer, som gjør at hvis vi skriver det slik: SELECT * FROM merke WHERE merkenavn = :NAVN
altså med kolon foran et variabelnavn, vil få opp en dialogboks hvor verdien til variabelen (i dette tilfellet :NAVN) skal fylles inn. I Delphi kan dette gjøres på lignende måte. Her er en bit kode: 1 procedure TForm1.KjoerSporring; 2 begin 3 Query1.Close; 4 Query1.SQL.Clear; 5 Query1.UnPrepare; 6 Query1.SQL.Add('SELECT * FROM merke WHERE Merkenavn = :Navn'); 7 Query1.Params[0].AsString := 'AUDI'; 8 Query1.Prepare; 9 Query1.Open; 10 end;
Dette kan nok ved første blitt virke litt uklart. Poenget er at Delphi vil oppdage at Selectsetningen (linje 6) har en parameter (:Navn). Neste setning (linje 7) definerer parameteren. Her sies det at første element i Params-arrayet skal være en String med verdi 'AUDI'. Når spørringen bli kjørt (linje 8 og 9) blir 'AUDI' satt inn for :Navn, slik at setningen i realiteten blir Query1.SQL.Add('SELECT * FROM merke WHERE Merkenavn = "AUDI"');
Parameteren :Navn kunne ha hett hva som helst. Systemet finner ut at det er første (og i dette tilfellet eneste) parameter, og setter derfor inn Params[0], altså første element i arrayet.
Nå er det ikke alt i en Selectsetning som lar seg parametrisere. Denne setningen vil f.eks. ikke gå: SELECT * FROM :EnTabell
Løsningen i slike tilfeller er å bruke Format-funksjonen. Jeg går ikke dypt inn i hvordan den fungerer nå, hvis du vil vite mer, så sjekk hjelpesystemet. Men i korte trekk: Formatfunksjonen tar to parametre: En stringvariabel og en liste av verdier. Listen av verdier skal stå i firkantparanteser (den er et array). Dersom verdien er en string skal det stedet verdien skal settes inn markeres med %s og dersom verdien f.eks. er et heltall markerer du det med %d. Uansett, hele uttrykket blir konvertert til en lang string og tilordnet stringvariabelen (den første parameteren). I denne setningen Format('SELECT * FROM %s', ['merke']);
vil resultatet bli 'SELECT * FROM merke' fordi første %s vil bli erstattet med første element i verdiarrayet i firkantparantesen.
Denne setningen Format('SELECT %s FROM %s', ['*', 'merke']); vil gi nøyaktig det samme resultatet
Husk at Format er en funksjon, dvs. den returnerer en stringverdi. Den følgende koden er et eksempel på korrekt bruk av Format:
procedure TForm1.Button2Click(Sender: TObject); var S: String; begin S := Format('SELECT * FROM %s', ['merke']); Query1.Close; Query1.SQL.Clear; Query1.UnPrepare; Query1.SQL.Add(S); Query1.Prepare; Query1.Open; end;
Til slutt et litt mer avansert eksempel. Gitt at du i har lagt inn aktuelle tabeller i en listeboks, kunne du f.eks. bruke følgende kode:
procedure TForm1.Button2Click(Sender: TObject); var baseStreng, tabell, s: String; begin baseStreng := 'SELECT * FROM %s'; tabell := ListBox1.Items.Strings[ListBox1.ItemIndex]; s := Format(baseStreng, [tabell]); Query1.Close; Query1.SQL.Clear; Query1.UnPrepare; Query1.SQL.Add(s); Query1.Prepare; Query1.Open; end;
3. Bruk av DDL for å lage tabeller For å lage en tabell til i den samme katalogen (DatabaseName) som de andre knyttet til Query1, bruker vi CREATE TABLE som SQL-properety til Query-komponenten. Følgende kode lager "employee.db"-tabellen som blir brukt som eksempel i LocalSQL-hjelpefilen:
Legg merke til at vi bruker ExecSQL i dette tilfellet og ikke Open, siden denne setningen ikke returnerer et resultatsett slik som Select. Denne tabellen får filendelsen db som du ser. Det betyr at det blir det laget en tabell i Paradoxformat. De andre to tabellene vi brukte, merke og modell, hadde filendelsen dbf, noe som indikerer at de der dBase-tabeller. Hvis du nå legger inn employee i listeboksen hvor det står merke og modell fra før, kjører programmet og klikker på Button2 etter å ha valgt en tabell i listeboksen, vil du se at alle tabellene åpnes som de skal. Formatet spiller med andre ord ingen rolle når vi bruker SQL-setninger på dem.
3. Bruke standardkomponenter for å vise data 3.1 La hver rad i en tabell bli et alternativ i en ComboBox
Et behov som av og til vil melde seg, er å vise innholdet i en tabell i vanlige ikke-datafølsomme komponenter. Det kan f.eks. være at du bruker fremmednøkkel mot en tabell med få poster for å oppnå referanseintegritet.
La oss tenke oss at du skal registrere elektroniske komponenter, og at et av feltene er "kategori". En kategori kan være "Motstander", "Dioder", "Transistorer" osv., med andre ord et begrenset antall. For å sikre deg at brukeren velger rett, har du laget en tabell "Kategori" med fremmednøkkel mot denne i komponent-tabellen (referanseintegritetet!). Du lar derfor ikke brukeren skrive inn kategorien i en editboks, men ønsker å la ham kunne velge kategori blant alternativer på nedtrekkslisten i en combo-boks.
Din første tanke er å bruke en DBComboBox til dette, inntil du finner ut at den bare viser verdien til det aktuelle feltet i text-propertyen, og at ikke noe fra tabellen kommer på nedtrekkslisten.
I dette tilfellet vil det være bedre å bruke en vanlig ComboBox, og lage en løkke gjennom tabellen for å hente ut feltverdier som så "addes" til Items-propertyen i ComboBoxen. SQL har en FETCH-kommando som kan brukes til å gå radvis gjennom en tabell (FETCH FIRST, FETCH NEXT osv.), men denne kommandoen er ikke støttet av Local SQL. Vi er derfor tvunget til å gjøre det på en annen måte, men det er ikke så vanskelig.
En TQuery er en arving av TDataSet, og TDataSet har alle de nødvendige metodene for å navigere i en tabell (First, Next osv.). Det som trengs er å først bruke First-metoden for å plassere oss i toppen av tabellen, deretter sette opp en løkke som går like mange ganger som det er rader i tabellen med Next-kommandoen inne i løkka (ikke glem det!!!), mens vi for hver løkkegjennomgang legger til feltverdien blant ComboBoxens items.
Koden står på neste side.
// "Hekt av" alle datafølsomme kontroller for å unngå flimring og ekstra tid Query1.DisableControls;
// Gå til første post Query1.First;
// Blank ut comboboksen ComboBox1.Clear;
// Sett teksten i valgt felt i første post som text-property ComboBox1.Text := Query1.FieldByName('KATEGORI').AsString;
// Løkke inntil vi kommer til siste post while not (Query1.EOF) do begin
// Legg til feltet på nedtrekkslisten ComboBox1.Items.Add(Query1.FieldByName('KATEGORI').AsString); // HUSK DENNE: Gå til neste post (ellers får du evig løkke) Query1.Next;
end;
// Gå tilbake til toppen (eller hvor du vil...) Query1.First;
// Hekt på kontrollene igjen Query1.EnableControls;
>>kode12 Der står ikke ADOTable1.local men ADOTable1.locate Locate er en søgning i dataset'et, hvor man angiver 3 parametre: 1) Hvilket felt der skal søges i (Der kan godt angives flere) 2) Hvad man søger efter (Her har jeg søgt efter den linie, der er markeret i listboksen) 3) Søgeparameter: a)loCaseInsensitive (Ligegyldigt med store og små bogstaver) eller b)loPartialKey (Man kan nøjes med at angive en del af søgestrengen)
Jeg vil bare give mit besyv med og udtrykke min begejstring for det smarte locate-trick i listboxe: flot og grundig forklaring nca. Man kunne måske have uddybet at adotabellen: "Jeg har en ADOTable, som peger på navnetabellen i databasen" - gør dette via AdoConnectionen - for de helt jomfruelige database-entusiaster. Og jeg skal gerne tage i mig at gridden er sufficient - dette er meget 'sjovere' koden12 ;-)
Hej igen... : ) Efter at have siddet og lavet 3.00.000 ting samt lagt det på et datamodul !!! ville den IKKE tage denne sætning ???? : = [loCaseInsensitive])
Hvorfor ikke ? Nu var jeg lige nået så langt ... øv..
Præsis , Pyhh, tak... DVS, jeg skal holde øje med om Delphi nu også skriver alle dem oppe i toppen , eller hur ? Som du skriver den manglede ADODB , da den kom på ... ingen problemer..
VH koden12
PS : Jeg havde 7000 brugere under mig : ) dengang jeg arbejdede..
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.