20. januar 2000 - 17:36Der er
28 kommentarer og 1 løsning
Delphi og sql
Jeg har en del problemer med at få nogle sql sætninger til at virke derfor har jeg brug for lidt hurtig hjælp da det snart skal være færdigt det jeg arbejder på. Her er et par eks. with datamodul do begin Queryordrenr.SQL.Clear; Queryordrenr.sql.add('select ordrenr'); Queryordrenr.sql.add ('From ordretabel'); Queryordrenr.sql.add ('where idnr = :ordreid'); Queryordrenr.ParamByName('ORDREID').asinteger:= strtoint(dbeid.Text); Queryordrenr.Prepare; Queryordrenr.open; Denne sql sætning har jeg fået udleveret af min IT lærer jeg kan bare ikke få det til at virke da delphi siger at ordreid ikke er en rigtig integer, hvilket jeg ikke kan forstå da min IT lærer har haft det til at virke. Et andet eks. er dette. with datamodul do
begin querykundetype.SQL.Clear; querykundetype.SQL.Add ('select kundetype'); querykundetype.SQL.Add ('from kundetypetabel'); querykundetype.SQL.Add ('where kundetypetabel.kundetypenummer = '+dbetypeid.Text); // and ('forretningstabel.idnr = '+dbeid.DataField ); querykundetype.open; if querykundetype.recordcount > 0 then dbetype.text := querykundetype.fields[0].value; querykundetype.Prepare; end; Her kommer Delphi igen med en fejl meddelelse som jeg ikke forstår så meget af. Er der nogen der kan svare på dette og hvis det kræver flerer point finder vi også ud af det, bare jeg kan få et svar hurtigst muligt.
I dette særtema ser vi på, hvordan cloud og AI bliver fundamentet for virksomhedernes digitale forretning, og hvordan de nye muligheder for automatisering og forretningsværdi kan udnyttes uden at miste overblik, sikkerhed og menneskelig kontrol.
I det første eks mangler du querykundetype.params.createparam(ftinteger,'ordreid',ptoutput); der skal skrives før linien med parambyname.
Alternativt kan denne linie være skabt som et param under querykundetype sql-tablet i property editoren for params.
i det andet eks skal du fjerne punktum mellem kundetypetabel og kundetypenummer (hvid ellers feltet hedder kundetypenummer i tabellen hvis det bare hedder nummer skal du også fjerne kundetype foran).
Når du skriver: querykundetype.SQL.Add ('where kundetypetabel.kundetypenummer = '+dbetypeid.Text);
giver det fejl. Som jeg svarede til dit andt spm. skal du give dine "from" tables identer men kun når du bruger flere af dem i samme select. Når du kun bruger et er det ikke nødvendigt.
Select leverer en tabel med et antal rækker svarende til antallet af records det bliver fundet, og med et antal kolonner svarende til de felter der selekteres.
Når man så selekterer felter der findes i 2 eller flere af de tabeller man selekterer fra er man nødt til at give det enkelte felt en ident foran feltnavnet ellers vil sql ikke kunne oprette det "table" der kommer ud af kaldet, og du ville ikke være istand til at skelne mellem hvilket felt er hvad. Hvis du nu havde 2 kolonner der hed idnr, hvilken af dem var så udfyldt fra kundetype og hvilken fra forretningstabel ?? Kan du se det ?
Derfor angiver man en ident foran som f.eks. a eller X eller hvad man nu kan finde på. Jeg er ikke helt sikker på om der er nogen restriktioner vedr. antallet af tegn identen kan bestå af. Jeg bruger altid selv hhv. a, b, c o.s.v. svarende til den rækkefølge tabellerne er skrevet i, mellem From og Where.
Jeg håber at det har givet dig lidt indblik i hvad det egentligt er der foregår.
Sjensen hvorfor siger Delphi at b er en uidentificeret variabel når jeg forsøger med dette: querykundetype.SQL.Add ('select kundetype, idnr'); querykundetype.SQL.Add ('from a.kundetypetabel, b.forretningstabel'); querykundetype.SQL.Add ('where (a.kundetypenummer = '+dbetypeid.Text) and b.idnr = '+dbeid.DataField );
Fordi jeg ikke lige havde mit hoved med mig da jeg svarede. Jeg prøver lige igen, og denne gang rigtigt. Udgangspunkt er at kundetypenummer er et felt i kundetype tabel og idnr et felt i forretningstabel. Både kundetypetabel og forretningstabel har desuden begge felter:
'select kundetypetabel.kundetypenummer, forretningstabel.idnr from kundetypetabel, forretningstabel where (kundetypetabel.kundetypenummer = '+dbetypeid.text+') and (forretningstabel.idnr = '+dbeid.datafield+')'
Sjensen prøver lige at kigge på det nyeste du har skrevet men kan du forklare at dette virker. with datamodul do begin Queryby.SQL.Clear; queryby.sql.add('select Bynavn From Postnrtabel where postnr = '+dbepostnr.text); queryby.open; if queryby.recordcount > 0 then dbeby.text := queryby.fields[0].value; end; men det gør dette ikke with datamodul do begin Queryordrenr.SQL.Clear; Queryordrenr.sql.add('select Ordrenr From Ordretabel where IDnr = '+dbeid.Text); Queryordrenr.open; if Queryordrenr.recordcount > 0 then dbeordrenr.Text := Queryordrenr.fields[0].value; end; Jeg synes det er lidt mystisk da fremgangsmåden er identisk.
Jeg har nu forsøgt at modificere det eks. du har givet ovenfor så den passer helt perfekt på min database her er den. with datamodul do begin Querykundetype.SQL.Clear; Querykundetype.sql.add('select kundetypetabel.kundetype, forretningstabel.kundetypenr'); Querykundetype.sql.add('from kundetypetabel, forretningstabel'); Querykundetype.sql.add('where forretningstabel.kundetypenr = '+dbtid.datafield+')'); Querykundetype.open; if Querykundetype.recordcount > 0 then dbetype.Text := Querykundetype.fields[0].value; end; men dette virker bare ikke. Jeg prøver lige at beskrive med ord hvad det er der skal ske, jeg skal bruge den kundetype fra kundetypetabel hvor det kundetypenr som er i kundetypetabel er lig med det kundetypenr der er i forretningstabel og resultatet skal vises i dbetype.text. Delphi påstår at der er en fejl token = ) fejl i linie 3.
Nu har jeg rent faktisk fået dette til at virke tildels. with datamodul do begin Querykundetype.SQL.Clear; Querykundetype.sql.add('select kundetypetabel.kundetype, kundetypetabel.kundetypenummer'); Querykundetype.sql.add('from kundetypetabel, forretningstabel'); Querykundetype.sql.add('where kundetypetabel.kundetypenummer = '+Dbtid.DataField+''); Querykundetype.open; if Querykundetype.recordcount > 0 then dbetype.Text := Querykundetype.fields[0].value; end; Der er bare det problem at den viser kun det første resultat, altså den første record hvor kundetypen passer til den dertil svarende forretning, er dette fordi at dbtid.datafield rent faktisk ikke skifter værdi, men forbliver den samme.
Svar på kommentar fra 15:12: Nej, det har du ret i. De er ens og burde derfor virke. Men du overser lige en detalje: Bynavn er et tekstfelt og ordrenr er et tal. Når du bruger ..fields[0].value overføres værdien i den form den er. For bynavn er den tekst og derfor virker det men ordrenr er tal og dem kan du ikke flytte over i en text. Der skal du enten bruge inttostr(..fields[0].value) (hvis ordrenr er en integer, ellers floattostr) eller bruge den indbyggede konvertering: ..fields[0].asstring;
Svar på kommentar fra 16:30: Hvis kundetype er et tal gælder samme regel vedr. ..[0].value som jeg skrev for ordrenr. Brug i stedet dbetype.text := querykundetype.fields[0].asstring;
Men det er ikke det sql brokker sig over. Med mindre det er dig der bare har skrevet galt i din kommentar har du et ekstra + tegn efter dbtid.datafield: kundetypenr = '+dbtid.datafield+')'
Det skal du fjerne.
En andet spørgsmål er: Hvilken felttype er dbtid.datafield ? tekst, integer, dato ?
Prøv i øvrigt at tilføje en listboks på skærmen og brug listbox1.items.add istedet for query.sql.add og sæt query.sql til listbox1.items, lige inden sql.open, så kan du se din sql-sætning inden den eksekveres:
Dermed kan du se hvad det egentligt er der bliver kaldt med, og om alle dele af sql sætningen er korrekt bygget op.
En anden generel information du har brug for, og som er gældende for din 3. kommentar, vedr. testen efter sql.open, og overførslen af værdien til din textboks, ..fields[0].xxx :
Fields[0] svarer til det første felt du har angivet efter ordet Select, Fields[1] svarer til det andet. o.s.v.
..[0].value vil overføre værdien som den er og det kan kun bruges når modtagerfeltet er af samme type som afsender, altså textboks := tekstfelt; Når felttyperne er forskellige skal de konverteres: tekstboks := inttostr(integer) eller floattostr(number), eller bruge den indbyggede konvertering: tekstboks := ..[0].asstring;
"Der er bare det problem at den viser kun det første resultat, altså den første record hvor kundetypen passer til den dertil svarende forretning, er dette fordi at dbtid.datafield rent faktisk ikke skifter værdi, men forbliver den samme"
Nej:
Dbtid.datafield skifter aldrig værdi. I dit kald omsættes den værdi dbtid.datafield har på det tidspunkt til en værdi og sql finder alle de records i kundetypetabel hvor kundetypetabel.kundetypenummer er lig med dbtid.datafield.
Men.. Du overfører jo kun en record til din tekstboks (dbetype.text). Hvis der er flere records, d.v.s. at querykundetype.recordcount ikke kun er større end 0 men også større end 1, 2 eller hvor mange der nu er fundet, så kan du ikke bruge et enkelt tekstfelt til at vise dem alle.
Igen skal du f.eks. bruge en listboks:
If Querykundetype.recordcount > 0 then begin querykundetype.first; // start med den første while not querykundetype.eof do // fortsæt så længe dr er records begin listbox1.items.add(querykundetype.fields[0].asstring); querykundetype.next; end; end;
Så vil du få alle sammen. Og så må du finde en metode til at vælge den af dem der er, som du vil bruge.
Iøvrigt er der en anden fejl, eller rettere overflødighed, i din sql sætning her:
I dette har du "from ..., forretningstabel" men du bruger selecter ingen felter fra forretningstabel, og tester heller ikke på nogen, så "forretningstabel" behøver du ikke i denne sql sætning.
Jeg håber at dette hjælper lidt på den generelle forståelse af hvordan sql virker.
Hvorfor siger delphi at der er en invalid brug af nøgle ord token = linie 3. når jeg kører dette. with datamodul do begin Queryordrenr.SQL.Clear; Queryordrenr.sql.add('select ordretabel.ordrenr'); Queryordrenr.sql.add('from ordretabel'); Queryordrenr.sql.add('where ordretabel.idnr = '+Dbeid.Text); Queryordrenr.open; if Queryordrenr.recordcount > 0 then dbeordrenr.Text := Queryordrenr.fields[0].asstring; end;
I øvrigt: Synes du ikke snart jeg har gjort mig fortjent til de points du har sat på højkant. Jeg vil meget gerne hjælpe, men du må betale lidt points for det.
ordretabel.idnr er et tal altså en integer, men delphi siger at det er en string og hvis jeg forsøger dette 'where ordretabel.idnr = ' + strtoint(dbeid.text); siger delphi at der er fejl string = Integer hvilket jeg ikke forstår for i paradox er idnr altså en integer. men hvis jeg lader være med at skrive det siger delphi at der er invalid uses of keyword, men jeg kan ikke se at jeg bruger nogle reserverede ord forkert. Og jeg har også select idnr nu.
procedure TForm1.Button1Click(Sender: TObject); begin q1.sql.clear; q1.sql.addstrings(listbox1.items); q1.open; end;
procedure TForm1.Button2Click(Sender: TObject); begin listbox1.items.clear; listbox1.items.add('Select '+edit1.text); listbox1.items.add('From '+edit2.text); if (edit3.text <> '') and (edit4.text <> '') and (edit5.text <> '') then begin listbox1.items.add('Where '+edit3.text+' '+edit4.text+' '+edit5.text); end; end;
end. // og hertil klipper du koden ind i editoren istedt for den kode du har i Unit 1.
Derefter trykker du på F12 for at se din form, og højre klikker på formen og vælger View as Text. Den tekst der nu fremkommer fjerner du helt og klipper følgende ind i stedet:
object Form1: TForm1 Left = 200 Top = 104 Width = 696 Height = 352 Caption = 'Form1' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] PixelsPerInch = 96 TextHeight = 13 object Label1: TLabel Left = 16 Top = 36 Width = 33 Height = 13 Caption = 'Select ' end object Label2: TLabel Left = 344 Top = 36 Width = 23 Height = 13 Caption = 'From' end object Label3: TLabel Left = 16 Top = 76 Width = 32 Height = 13 Caption = 'Where' end object Label4: TLabel Left = 280 Top = 76 Width = 7 Height = 13 Caption = 'is' end object Label5: TLabel Left = 16 Top = 176 Width = 140 Height = 13 Caption = 'Sådan ser SQL sætningen ud' end object Label6: TLabel Left = 336 Top = 176 Width = 90 Height = 13 Caption = 'Og her er resultatet' end object DBGrid1: TDBGrid Left = 336 Top = 192 Width = 337 Height = 120 DataSource = DataSource1 TabOrder = 8 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name = 'MS Sans Serif' TitleFont.Style = [] end object ListBox1: TListBox Left = 16 Top = 192 Width = 297 Height = 121 ItemHeight = 13 TabOrder = 6 end object Button1: TButton Left = 336 Top = 142 Width = 75 Height = 25 Caption = 'Kald SQL' TabOrder = 7 OnClick = Button1Click end object Edit1: TEdit Left = 64 Top = 32 Width = 209 Height = 21 TabOrder = 0 end object Edit2: TEdit Left = 376 Top = 32 Width = 209 Height = 21 TabOrder = 1 end object Edit3: TEdit Left = 64 Top = 72 Width = 209 Height = 21 TabOrder = 2 end object Edit5: TEdit Left = 376 Top = 72 Width = 209 Height = 21 TabOrder = 4 end object Edit4: TEdit Left = 304 Top = 72 Width = 49 Height = 21 TabOrder = 3 end object Button2: TButton Left = 16 Top = 144 Width = 145 Height = 25 Caption = 'Sæt SQL sætning' TabOrder = 5 OnClick = Button2Click end object q1: TQuery DatabaseName = 'dbdemos' Left = 608 Top = 24 end object DataSource1: TDataSource DataSet = q1 Left = 640 Top = 24 end end
//Denne linie skal du ikke have med. Herefter højreklikker du igen og vælger View as Form og du kan nu se den form jeg har lavet.
For TQuery retter du aliasnavn fra dbdemos til dit eget alias. Herefter gemmer du programmet og compilerer det. Herefter kan du starte det.
I de 5 editbokse skiver du dit sqlstatement, men ikke ordene Select og From, dem sætter programmet selv ind:
I edit1.text skriver du: ordretabel.ordrenr i edit2.text skriver du: ordretabel i edit3.text skriver du: ordretabel.idnr i edit4.text skriver du: = i edit5.text skriver du: 'Den værdi du ellers sætter ind i dbeid.text'
Derefter trykker du på knappen Sæt Sql Sætning hvorefter du kan se din SQL sætning, og derefter på knappen Kald SQL for at teste det. Hvis du har gjort det rigtigt vises de records der blev fundet nu i dbgridden, incl. de navne kolonnerne har.
Jeg håber du får løst dit problem.
NB! Hvis du bruger D4 eller D5, og du ikke kan få compileret programmet så sig til, så sender jeg et nyt i D4 eller D5 i løbet af Mandagen.
Jeg har lige lavet det i D4 også, men der er en lille fejl når man kopierer fra Eksperten. Følgende test
if (edit3.text <> ') and (edit4.text <> ') and (edit5.text <> ') then
er selvfølgeligt forkert fordi testen på om de 3 edits er tomme skal være med 2 anførselstegn. I eks. er der kun vist et. Det er et problem Eksperten har når man skriver 2 enkeltanførselstegn efter hinanden. Så viser den kun en.
Jeg er nu ved at forsøge at lave en simpel søgemaskine,
begin if edtforretning.Text <> '' then begin with datamodul do begin querysogning.sql.Clear; querysogning.sql.Add ('select forretningsnavn'); querysogning.sql.Add ('from forretningstabel'); querysogning.sql.Add ('where forretningsnavn = '+edtforretning.Text); querysogning.Open; if querysogning.recordcount = 0 then Showmessage( edtforretning.Text + ',findes ikke i databasen') else if querysogning.recordcount = 1 then datamodul.ForretningsSource.DataSet.???? end;
men jeg har lidt problemer for det første volder det problemer når brugeren skriver noget forkert i edtforretning, da Delphi da klager over at der ikke findes nogen record med navnet, hvor efter jeg mener at programmet skal aflevere en fejlmeddelse. derudover volder det lidt problemer med hvordan jeg skal fortælle delphi at den nu skal gå til den record hvor forretningsnavn = edtforretning.
"Lige et side spørgsmål kan et dbgrid på nogen måde være transparent" Det er et godt spørgsmål. Du ahr nogle få muligheder for at ændre på udseende af den. Dels ved at ændre på options i dbgrid properties og dels ved at højreklikke på gridden og vælge "Coloumns Editor". I editoren højreklikker du og vælger ADD og for hver linie (kolonne) du opretter kan du vælge farve, tekst, format m.m. i properties. Om du på denne måde kan gøre den gennemsigtigt ved jeg ikke rigtigt, men prøv dig frem.
Vedr. søgemaskine: Den fejl du får når der ikke er nogen recs., kommer den ikke kun når du kører programmet fra Delphi eller også når du kører programmet fra EXE filen ?
Den måde du tester på er ok, så det burde være rigtigt. Men du kan selvfølgeligt pakke det ind i en try..Except..end a la:
try sql.open; if querysogning.recordcount > 0 then .. except on exception do Showmessage('Forretning ikke fundet'); end;
Der muligt du efter except skal bruge en test på exception istedet for den globale "On Exception". Jeg kender ikke alle men jeg forestiller noget a la "EdatabaseError". Det kan du læse mere om i hjælp under E-exceptions
Vedr. det at positionere delphi på den record du har fundet:
Jeg bruger normalt en table der peger på samme db som Query, og når jeg så har fundet den record jeg vil have fat i med SQL, kalder jeg
if Table1.findkey([querysogning.fields[0].value]) then ; // tom if for at pos. record men det kræver at du har nøglen til det pågældende register med i din sql select, som første felt:
"select idnr, forretningsnavn"
så den try..expect jeg skrev ovenstående ville blive:
try sql.open; if querysogning.recordcount > 0 then begin if Table1.findkey([querysogning.fields[0].value]) then ; // tom if for at pos. record end; except on exception do Showmessage('Forretning ikke fundet'); end;
Sjensen jeg har forsøgt nogle forskellige muligheder med try - except her er koden: procedure TSogning.StartsogningClick(Sender: TObject); begin try if edtforretning.Text <> '' then with datamodul do begin querysogning.sql.Add ('select ident, "forretningsnavn"'); querysogning.sql.Add ('from forretningstabel'); querysogning.sql.Add ('where forretningsnavn = '+edtforretning.Text); querysogning.Open; end;
if datamodul.querysogning.recordcount > 0 then begin if datamodul.forretningstabel.findkey([datamodul.querysogning.fields[0].value]) then ; // tom if for at pos. record end; except on exception do Showmessage( edtforretning.Text + ',findes ikke i databasen') end;
men kan ikke helt få det til at virke, de to hoved problemer er at delphi ikke vil akseptere når edtforretning indeholder en værdi der ikke er at finde i databasen, da skulle den jo gå til except men dette sker ikke da den laver en fejl inden. Der efter er der problemet med at få den til at akseptere værdier med mellemrum hvilket er ret vigtigt, har du nogle ideer til hvordan dette kan løses.
Det med mellemrum løser du ved at sætte anførselstegn omkring edtforretning.text:
"'where forretningsnavn = '+edtforretning.Text"
Vedr. Fejl:
Ja og Nej. Jeg kan godt se nu at med den opstilling du laver er det ligemeget med try/except. brug istedet for:
if edtforretning.Text <> ' then with datamodul do begin querysogning.sql.Add ('select ident, "forretningsnavn"'); querysogning.sql.Add ('from forretningstabel'); querysogning.sql.Add ('where forretningsnavn = '+edtforretning.Text); querysogning.Open; end;
if datamodul.querysogning.recordcount > 0 then begin if datamodul.forretningstabel.findkey([datamodul.querysogning.fields[0].value]) then ; end else begin Showmessage( edtforretning.Text + ',findes ikke i databasen') end;
Jeg lavede en fejl i 2 linie. Det skal rigtigt se således ud:
'where forretningsnavn = '+"edtforretning.Text"
sjensen
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.