Avatar billede madshenrik Nybegynder
13. januar 2000 - 17:46 Der er 21 kommentarer og
2 løsninger

SQL problem

Jeg sidder og laver en kundedatabase men har ikke rigtig noget erfaring med sql sammen med delphi. Hvis man har en editbox der skal være ligmed resultatet fra en sql sætning hvordan gøres dette så. sql sætningen lyder noget i retning af
select by
from forretningstabel
where postnrtabel.postnr = forretningstabel.postnr
Er der nogen der kan hjælpe med dette.
Avatar billede erikjacobsen Ekspert
13. januar 2000 - 20:15 #1
Bare en kommentar: ordet  BY  er et reserveret ord i SQL, og vil
give syntaksfjel sådan som du skriver det. Vælg et andet navn for
attributten: STAD, BYNAVN, CITY eller sådan noget...
Avatar billede sjensen Nybegynder
14. januar 2000 - 00:30 #2
Når dit sqlkald er eksekveret korrekt kan du teste på query1.recordcount. Hvis den er større end 0 så har du fat i en eller flere records.

SQL kaldet har derefter opbygget en table i memory med de felter (som kolonner) du har angivet. I dette tilfælde BY (der som erikjacobsen ganske rigtigt siger skal hedde noget andet).

Herefter kan du bruge:

query1.first;
while not query1.eof do
  edit1.text := query1.fields[0].fieldname;  // fields[0] er første felt
  query1.next;
end;

Hvis recordcount kun er 1 (fordi du kan fandt én der svarede til din søgning er dette nok:

query1.first;
edit1.text := query1.fields[0].fieldname;

At arbejde med en query, efter at sql er eksekveret, svarer til at arbejde med et Table.

Sjensen
Avatar billede delphi Nybegynder
14. januar 2000 - 10:14 #3
Først og fremmest, så vil jeg sige at lige netop i det nævnte eksempel, eller alle andre situationer, hvor man har en master-tabel (ForretningsTabel) og en detail-tabel (PostNrTabel), der er  endetegnet ved at mastertabellen inderholder et fremmednøgle felt, som  an linkes til et nøglefelt i en detail tabel, så er det i Delphi lidt voldsomt at bruge SQL for at finde et eller flere datafelter i detail - tabellen.  (Pyh, lang sætning...)

I stedet ville jeg i denne situation  anbefale at du laver en master-detail  elation.

Jeg går ud fra at du har to (fx. Paradox) tabeller:

ForretningsTabel, mindst med følgende felter:
- IdNr - nøgle felt, Integer.  Bruges ikke i dette eksempel.
- PostNr - Integer.  Bruges som fremmednøgle.

PostNrTabel, med følgende to felter:
- PostNr - Nøglefelt, Integer.
- PostBy - Alpha (20).

Det er muligt at oprette Master-detail relationerne direkte i din database, men nu vil jeg altså gøre det i Delphi koden.  Det er lidt mere fleksibelt.  Fx. kan man på denne måde arbejde frit med Detailtabellen (fx. se hele dens indhold) uden at tage hensyn til Mastertabellen.


Gør nu følgende i Delphi IDE´et:


Opret et datamodule (Brug altid datamodules, hvor du har ALLE tabeller i din application.  At have tabeller, datasources mv. spredt rundt på forskellige form er noget snavns!)

På datamodulet indsætter du et database object.  Sæt dets properties:
  AliasName = <Dit alias navn>  (Fx. Test)
  DatabaseName = "LocalTest"  (Eller noget andet du ka' li', fx. "local'+Alias)
  LoginPrompt = False
  Name = dbTest (Eller hva' du ka' li', fx. "db"+Alias)
  Connected = True  (Ellers skal du sætte denne (og tabellernes) til true senere)

Indsæt derefter en tabel.  Sæt properties:
  Name = taForretningsTabel  ("ta" + Tabellens navn).
  DataBaseName = LocalTest  (Fra database objectets DatabaseName property)
  TableName = Forretningstabel.db (Vælges fra Combo box - her en paradox tabel)
  Active = True
  (Det er i dette eksempel ikke nødvendigt at oprette fields for denne tabel)

Indsæt en datasource.  Sæt properties:
  Name = dsForretningsTabel  ("ds" + tabel navn)
  DataSet = taForretningsTabel


Indsæt derefter en ny tabel.  Sæt properties:
  Name = taPostNrTabel  ("ta" + Tabellens navn).
  DataBaseName = LocalTest  (Fra database objectets DatabaseName property)
  TableName = PostNr.db (Vælges fra Combo box - her en paradox tabel)
  Active = True

  DobbeltClick på tabellens icon.  Dette frembringer tabellen fieldeditor.
  Højre click i editoren og vælg "Add All Fields" i menuen og luk så editoren.
  Hvis du nu kigger på text versionen af din unit, så vil du se, at der nu er
  fremkommet to fields:

    taPostNrTabelPostNr      : TIntegerField;
    taPostNrTabelByNavn      : TStringField;

  Disse skal bruges til henholdsvis at lave master-detail kædningen og til at
  udlæse det postnummer der passer til den valgte row i forretningstabellen.

  Master-detail kæden oprettes ved at sætte følgende Postnr tabel properties:
  MasterSource = dsForretningsTabel
  MasterFields: I object inspectoren trykkes på '...'.  I Field List designeren
  oprettes et link mellem PostNr og PostNr.  Tryk på Ok.

Indsæt en datasource.  Sæt properties:
  Name = dsForretningsTabel  ("ds" + tabel navn)
  DataSet = taForretningsTabel

Nu er du færdig med dit data module.

Test det ved at lave en ny form.  Vælg her via menuen 'File - Use Unit' og vælg
den unit der indeholder dit datamodule.  Indsæt to DBGrids og hægt dem op til
hver sin datasorce.  Lav en DBNavigator og hægt den op til datasourcen 'dsForretningsTabel'.
Kør programmet.

I øvrigt du kan læse det aktuelle postnr som:

Var
  PostNr : String;

Begin
  PostNr := taPostNrPostNr.AsString;
End;



Jeg kan skrive lige så meget om bruge af SQL/Queries, men nu skal jeg til et møde....


NiP.
Avatar billede delphi Nybegynder
14. januar 2000 - 11:44 #4
Det gik lidt hurtigt med det svar, men nu sidder jeg lige og gennemlæser det.

I starten er der et par K'er der ikke er kommet med.  Således er 'endetegnet' = 'kendetegnet'....

Lidt senere skrev jeg at PostNr tabellen har et felt der hedder PostBy.  Senere bruger jeg konsekvent navnet ByNavn.  Der er tale om det samme felt, så navnet skal (=bør) se'følig være det samme hele vejen.

Mod slutningen skulle der se'følig stå:

I øvrigt du kan læse det aktuelle bynavn som:

Var
  ByNavn : String;

Begin
  PostNr := taPostNrTabelByNavn.AsString;
End;

Der var sikkert andre fejl, men hva'?..

Delphi/NiP
Avatar billede delphi Nybegynder
14. januar 2000 - 11:45 #5
Argh.... BYNAVN := taPostNrTabelByNavn.AsString;

(For meget klippe-klister)

NiP
Avatar billede tknudsen Nybegynder
14. januar 2000 - 12:31 #6
felt navne kan angives med [] - så er sql dortolkeren ligeglad med om der inden i [] står et reserveret ord - feks [by]. På samme måde angives tabeller/feltnavne indeholdende et space - feks [Dyre Tabel] eller [Antal Foedder Felt]. muuuhh...
Avatar billede madshenrik Nybegynder
14. januar 2000 - 13:12 #7
Læser det lige igennem, men kan nu ikke forstår at du mener det skulle være så besværligt med sql kommandorer, har læst flere eks. fra forskellige bøger om det.
Avatar billede delphi Nybegynder
14. januar 2000 - 13:23 #8
Mads>  Hvis du skriver til mig (og det gør du vist), så siger jeg ikke at det er for besværligt at anvende SQL - Jeg ville bare påpege, at Delphi har nogle ganske udemærkede faciliteter til at lave Master-detail links. 
Det jeg beskriver er, pånær to handlinger, præcis det samme man foretager sig, hvis man vil åbne de to tabeller, fx. for at vise dem i et par grids.  De to handlinger er, at man sætter PostNr tabellens MasterSource og MasterFields properties.  Man skal ikke oprette en TQuery.  Man skal ikke ind for at rode med en TQuery's fields.  Man vinder/taber ikke noget hastighedmæssigt (i forhold til en ordentligt konstrueret SQL løsning).

Desuden mener jeg, at metoden er væsentligt nemmere at gennemskue, bl.a. fordi man i Delphi 5's TDataModule grafisk kan se hvorledes tabellerne er bundet sammen.  Men det er se'følig subjectivt.

NiP
Avatar billede madshenrik Nybegynder
14. januar 2000 - 14:03 #9
Har lige læst kommentaren, her er måske noget mere håndgribeligt.
with queryby do
if dbeby.Text <> '' then
dbeby.Text := sql.Add ('select Bynavn from Postnrtabel where' + 'postnr = dbepostnr.Text');
end;
problemet er at delphi mener at det er en string på den ene side af = tegnet og en integer på den anden side, men ellers kan jeg ikke forstå hvorfor dette ikke skulle kunne virke
Avatar billede erikjacobsen Ekspert
14. januar 2000 - 14:46 #10
Skal det ikke være

dbeby.Text := sql.Add ('select Bynavn from Postnrtabel where postnr ='+ dbepostnr.Text);
Avatar billede sjensen Nybegynder
14. januar 2000 - 16:03 #11
Efter:

dbeby.Text := sql.Add ('select Bynavn from Postnrtabel where postnr ='+ dbepostnr.Text);

vil dbeby.text indeholde

select Bynavn from Postnrtabel where postnr =

og efter lighedstegnet værdien ad dbepostnr.text, hvis den har en.

SQL.ADD er blot at tilføje en linie til de strings der udgør selve sql-statementet.

hvis du vil have sat dbeby.text := bynavn fra tabellen hvor postnr i postnrtabel er det samme som værdien af dbeportnr.text ved hjælp af sql så skal du gøre sådan:

tquery1.clear;
tquery1.sql.add('select Bynavn from Postnrtabel where postnr = '+dbeportnr.text);
tquery1.open;
if tquery1.recordcount > 0 then dbeby.text := tquery1.fields[0].value;

Så er den hjemme !

sjensen
Avatar billede delphi Nybegynder
14. januar 2000 - 16:17 #12
Jeg tror, at det grundlæggende problem er, at du har en helt del fejl i din opfattelse af hvorledes SQL's virker.  Overhovedet ikke ondt sagt, for det jeg læser af din spørgsmål/kommentar er, at du har den samme totalt fejlagtige opfattelse, som jeg selv havde engang.  (Der skulle et kursus til for at rette op det...)

For det første:  En SQL query vil ALDRIG returnere en string! En Query vil ALTID returnere en tabel.  Denne tabel kan så være tom eller bestå mange rækker/kolonner eller sågar bare af et enkelt felt, evt. indeholdende en string, men det er altså stadig en tabel.  For at finde resultatet af din query er du altså nødsaget til at gå ind i tabellen, finde det rigtige række og den rigtige kolonne (=det rigtige felt), som du så udlæser i det format du ønsker.

I din kommentar skriver du: dbeby.Text := sql.Add ('select ....

Problemet er med dette er dels, at:

- du bruger en TDBEdit.  Du burde måske anvende en TEdit eller TLabel.  DBEdit er dataaware og bruger du den, så skal du ikke selv skrive i dens Text property, du kan blot nøjes med at sætte dens Datasource/-field parametre, hvorefter den 'automatisk' viser det korrekte indhold når du eksekverer din query.

- Jeg kan se at du med den linie forventer at tilskrivningen af din Qurerys Lines indhold automatisk vil opdatere query'en og returnere en string, som du så placere i din editbox.  Dette er ikke tilfældet! 
For det første, som skrevet ovenfor, så returnerer en Query altså en tabel, ikke en string.
For det andet, så kører (executer) du ikke query en ved blot at tilføje en linie til dens 'udtryk'. 
For det tredie, så er resultatet af at tilføje en linie (med Add-metoden) at du får at vide hvilken placering den nye linie fik i SQL'ens TString-list (som et integer index).  Du kan ikke tilskrive en integer til string variablel.  Ville du gøre ovenstående syntakisk korrekt, så kan du skrive:
dbeby.Text := IntToStr(sql.Add ('select ....);  Resultat: Din editbox vil nu indeholde index nummeret, dvs. hvilket linienr. den nye linie fik - ikke særligt anvendeligt.

Der er mere.... men det er nemmer at lave skrotte det du har lavet og så istedet lave et nyt eksempel....

En Public funktion oprettet i dit datamodule:
Iøvrigt, jeg bruger her en dynamisk oprettet Query.  Man kan se'følig godt bruge en statisk TQuery, men det er nu ikke rigtigt nødvendigt.  Det sparer dog et par linier i funktionen, men der skal så bl.a. tilføjest en linie med MyQuery.SQL.Clear før de/den nye linier indsættes.

Function TDataModule1.GetByNavn(aPostNr: Integer): String;
Var
  MyQuery : TQuery;
Begin
  // Opret en Query
  MyQuery := TQuery.Create(Self);
  Try
    Try
      // Fortæl hvilken database queryen kan finde sine tabeller i.
      MyQuery.DataBaseName := dbTest.DatabaseName;  // dbTest = Din TDatabase.

      // Nedenstående kan være i en linie - det gør ingen forskel,
      // men er i mine øjne mindre læsbart.
      MyQuery.SQL.Add('Select ByNavn');
      MyQuery.SQL.Add('From PostNrTabel');
      MyQuery.SQL.Add('Where PostNr = ' + IntToStr(aPostNr));

      // 'Kør' query'en.
      MyQuery.Open;

      // Hent resultatet.  Her skulle der helst kun være en række!
      If MyQuery.RecordCount=1 Then
        Result := MyQuery.Fields[0].AsString
      Else
        Begin
          // Ups!
          Result := '';  // Tom string, men www.Eksperten.Dk viser det forkert!
          Raise Exception.Create('Fejl i PostNr tabel.  ByNavn ikke fundet.');
        End;
    Finally
      MyQuery.Free;
    End;
  Except
    On E: Exception Do ShowMessage('Error: '+E.Message);
  End;
End;

Test rutinen på en form med en button:
(Bemærk jeg bruger en TEdit, ikke en TDBEdit)

procedure TForm1.Button1Click(Sender: TObject);
Var
  PostNr : Integer;
begin
  PostNr := DataModule1.taForretningsTabelPostNr.AsInteger;
  Edit1.Text := 'Bynavn = '+DataModule1.GetByNavn(PostNr);
end;


Så vil jeg iøvrigt lige sige, at det er stadig LANGT nemmere at bruge Mastersource og MasterFields properties i din PostNrTabel.  Hvis du i dit program har brug for at se alle rækker i din PostNr tabel, så kan du bare:
- Sætte/fjerne MasterSource/MasterFields værdierne afhængigt af om de skal bruges eller ej.
- Bruge en anden TTabel, der peger på samme fysiske tabel, men so ikke har MasterSource sat til taForretningsTabel.  (Sådan ville jeg gøre her.)



NiP

Avatar billede madshenrik Nybegynder
15. januar 2000 - 14:24 #13
Forstår udemærket SQL sproget har efterhånden læst 500-600 sider om emnet. Men har store problemer med at forstå sammenhængen mellem sql og Delphi.
Avatar billede madshenrik Nybegynder
15. januar 2000 - 16:29 #14
Delphi jeg er lige ved at forsøge med de sql kommandorer du har skrevet, men hvor kommer denne sætning fra Function TDataModule1.GetByNavn(aPostNr: Integer): String; skal den være i datamodulets unit ??? eller hvad.
Avatar billede madshenrik Nybegynder
15. januar 2000 - 17:17 #15
Til Delphi
Delphi kommer med følgende fejl meddelelser når man compilerer.
[Error] Datamodule.pas(30): Undeclared identifier: 'GetByNavn'
[Error] Datamodule.pas(37): Undeclared identifier: 'Self'
[Error] Datamodule.pas(41): Undeclared identifier: 'dbkundebase'
[Error] Datamodule.pas(41): Missing operator or semicolon
[Error] Datamodule.pas(47): Undeclared identifier: 'aPostNr'
[Error] Datamodule.pas(58): Unterminated string
[Error] Datamodule.pas(60): 'END' expected but 'RAISE' found
[Error] Datamodule.pas(61): '.' expected but ';' found
[Warning] Datamodule.pas(64): Text after final 'END.' - ignored by compiler
Avatar billede madshenrik Nybegynder
17. januar 2000 - 17:28 #16
Sjensen, jeg har nu fået dette til at virke men tquery1.clear;
tquery1.sql.add('select Bynavn from Postnrtabel where postnr = '+dbeportnr.text);
tquery1.open;
if tquery1.recordcount > 0 then dbeby.text := tquery1.fields[0].value; men kan ikke forstå at dette ikke virker
datamodule.Queryby.SQL.Clear;
datamodule.queryby.sql.add('select Bynavn From Postnrtabel where postnr = '+dbepostnr.text);
datamodule.queryby.open;
if datamodule.queryby.recordcount > 0 then
dbeby.text := datamodule.queryby.fields[0].value
kan du forsklare dette, jeg har prøvet at lave et datamodul men dette virker ikke optimalt kan overhovedet ikke hente noget data derfra men den ser ud til at være der når jeg skriver i kodeeditoren.
Avatar billede delphi Nybegynder
17. januar 2000 - 22:30 #17
Hejsa MadsHenrik.

Jeg har lige været væk et par dage, men nu er jeg på banen igen.

Funktionen GetByNavn, er en metode, som du selv skal oprette.  Jeg skrev vist noget i retning af: "En Public funktion oprettet i dit datamodule:" - Med dette menet jeg:

1: Gå ind i den unit, hvor du definere dit datamodule (for det gør du vel, ik?)
2: Hvis du ikke allerede ser den, så slå om til Pascal kildekoden.
3: Flyt cursoren til lige efter ordet "Public" i din datamodule definition.
4: Tilføj linien:

Function GetByNavn(aPostNr: Integer): String;

5: Hvis du nu ikke allerede har klistret metoden ind her fra siden, så aktiver din 'Code-completion" funktion (Hos mig er det vist <CTRL+Shift+C>)

6: Delphi-IDE'et har nu lavet rammen til din metode - nu skal du bare udfylde med de rigtige linier.

Presto: Nu har du en metode, du kan kalde fra andre steder i din application.  Kald metoden med et postnr som parameter og den returnere med et bynavn hentet i databasen.  Det virker helt fint (jeg har prøvet).

Send en email hvis du vil ha' et mere klart svar eller et lille demo program.

NiP

PS: Jeg ville stadig råde dig til at gennemføre denne opgave ved hjælp af tables og Master-detail links...
Avatar billede madshenrik Nybegynder
18. januar 2000 - 12:42 #18
Delphi jeg skal bruge dette program i en større skriftlig opgave på HHX, som har udgangspunkt i normalisering, sql og brugervenlighed, derfor er det vigtigt at det virker ved hjælp af sql.
Avatar billede delphi Nybegynder
18. januar 2000 - 14:19 #19
Nåeh - så er det jo en anden sag.

Virker det iøvrigt hos dig?

NiP
Avatar billede madshenrik Nybegynder
18. januar 2000 - 17:51 #20
Jeg har fået dette til at virke...
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;
men nu har jeg flyttet alle data controls over i et datamodul som du sagde, og derfor har jeg nu forsøgt med...
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 dette kan jeg ikke få til at virke har du nogen forklaring på dette ????
Avatar billede madshenrik Nybegynder
18. januar 2000 - 18:26 #21
Har nu også forsøgt dette men kan ikke helt finde ud af hvad der skal stå på den anden side af ligmed tegnet...
with datamodul do
begin
Querykundetype.SQL.Clear;
Querykundetype.sql.add('select kundetype From kundetypetabel where kundetypenr = '+datamodul.Forretningstabel.IndexName( kundetypenummer));
Querykundetype.open;
if Querykundetype.recordcount > 0 then
dbeby.text := Querykundetype.fields[0].value;
end;
Avatar billede madshenrik Nybegynder
19. januar 2000 - 15:03 #22
Sjensen jeg har forsøgt med dette men Delphi siger bare at kundetypetabel.kundetypenr er en Undeclared identifier: 'kundetypetabel'
kan du forklare dette.
with datamodul do
begin
Querykundetype.SQL.Clear;
Querykundetype.sql.add('select kundetype From kundetypetabel, forretningstabel where forretningstabel.kundetypenr = '+kundetypetabel.kundetypenr);
Querykundetype.open;
if Querykundetype.recordcount > 0 then
dbetype.text := Querykundetype.fields[0].value;
end;
end;
Avatar billede sjensen Nybegynder
20. januar 2000 - 21:02 #23
Ja, du skal skrive det rigtigt:

'select kundetype From a.kundetypetabel, b.forretningstabel
where b.kundetypenr = '+a.kundetypenr);

NB! Om du kaldet det a og b foran eller noget andet er ligegyldigt. Bare du skriver noget og så er det du bruger ved testen.

sjensen
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester