Avatar billede koden12 Nybegynder
27. marts 2004 - 00:32 Der er 19 kommentarer og
1 løsning

Find bestemte ord

Jeg vil gerne bede om hjælp til et problem.
Jeg har en database med ADO. dayabasen hedder meninger.
I den er der tabel en id(autonummer)
en meningerom (tekst)
I den tabel meningerom er der meget tekst, nu vil jeg gerne
kunne finde alle ord der fx hedder politik i ALLE rækkerne.
Dvs ikke kun det først ord den finder med politik med alle rækker igennem hele databasen i tabellen meningerom der indeholder ordet politik.
Kan det lade sig gøre med ADO ?
Jeg er ikke så god til Query så hvis den skal bruges bedes i skrive det tydeligt.
Det ville være mega fint om jeg fx kunne skrive ordet politik en en tekst1.txt box og så fx klikke på en knap hvorefter alle dem den finder bliver sat ind i en listbox eller sådan noget.
Har nogen mod på det ?

VH
koden12
Avatar billede hrc Mester
27. marts 2004 - 08:16 #1
Jeg ville foretrække SQL da filtre normalt er ineffektive og kan give forvirringer. En Query ved man hvad er.

  select id,tekst from meninger where tekst like "'*politik*"

Hvis du skulle gøre brug af TParameters mener jeg at sætningen ser sådan ud:

  select id,tekst from meninger where tekst like :Pattern

Så ender det med noget med i retning af (med et TListView i ReportMode m. to kolonner defineret):

procedure SearchTekster(aListView : TTListView; aPattern : string);
begin
  try
    // Burde ikke sættes her idet den er statisk, kun for at illustrere
    Query1.Sql := 'select id,tekst from meninger where tekst like :Pattern';

    // Antager at aPattern indeholder 'politik' - der søges afhængigt af store og
    // små bogstaver. Ved ikke hvordan man rammer "POLitik" og lignende - det kan
    // betyde at du skal gemme strengen i "uppercase" og konvertere tilbage når du
    // viser strengen.

    Query1.Parameters.ParamByName('Pattern').AsString := '"*' + aPattern + '*"';
    Query1.Open;

    aListView.Items.BeginUpdate;
    try
      aListView.Items.Clear;
      while not Query1.EOF do begin
        with aListView.Items.Add do begin
          Caption := IntToStr(Query1.FieldByName('Id').AsInteger);
          SubItems.Add(Query1.FieldByName('Tekster').AsString);
        end; // with
        Query1.Next;
      end; // while
    finally
      aListView.Items.EndUpdate;
    end; // try
  finally
    Query1.Close; // Sikrer at Queryen er lukket igen.
  end; // try
end;
Avatar billede koden12 Nybegynder
27. marts 2004 - 12:18 #2
Pyh hcr, du er godt nok langt fremme. Den ser meget fin ud.. men du må give lidt til
til en "Begynder?" til at forstå..
aPatten er det tekst1.tekst ?
De der end // try hvad betyder de ?
procedure SearchTekster er det en proceduer du bare har lavet ?
Query1 er det ADOQuery og virker de ned ?
aListView er det listbox1 ?
Hvor kaldes proceduren og hvordan ?
I ADOQuery sættes teksten ind hvor ?
Hvad for en tekst sættes ind eller SQL kommeando sættes ind ?

VH
koden12
Avatar billede koden12 Nybegynder
27. marts 2004 - 12:20 #3
Der skal stå "virker de ens" ..
Der skal stå "lidt tid"
Listbox1.txt hedder det vist !

VH
koden12
Avatar billede koden12 Nybegynder
27. marts 2004 - 13:57 #4
Det giver stygt mange fejl da jeg ikke ved hvad :
<<<< 'Pattern').AsString := '"*' + aPattern + '*"';<<<<<<

Pattern og APattern er for noget.


VH
koden12
Avatar billede hrc Mester
27. marts 2004 - 16:47 #5
Ja, ja. Det vil naturligvis give en masse fejl. Jeg ved ikke hvad du har testet det opimod.

1. aPattern er den parameter som man sender med til proceduren: SearchTekster(aListView : TTListView; aPattern : string); Det er igen en defacto standard at parametre har et 'a' foran så man altid ved at det er noget der "kommer udenfor" proceduren. På samme måde plejer man at sætte 'f' foran variable der står i private-delen på klasser.

Nå men, SearchTekster skal have to parametre: et TListView er den komponent du ser, hvis du klikker på "Min computer". Der kan du skifte mellem details, ikoner. Meningen er, at resultaterne skal proppes deri.
aPattern er søgeteksten.

2. Query1 er en TADOQuery (Access) eller en TQuery (mod BDE/Paradox-tabeller). Parameteren :Pattern er lidt speciel. En TQuery-variant har noget der hedder parametre og der kan du indsætte værdier. I dette tilfælde er der kun een og den hedder Pattern. Når SQL-selecten køres så indsættes værdien af Pattern dér i teksten hvor :Pattern står.

3. Min kæphest: Try-finally og try-except. En genial konstruktion som forbedrer kodesikkerheden væsentligt

3a. Try-Finally: En smart function der sikrer at en kodestump afvikles uanset hvordan operationen forløb

var
  StringList : TStringList;
begin
  Screen.Cursor := crHourglass; // På med timeglasset
  StringList := TStringList.Create; // Opret en liste instans
  try
    // Gør et eller andet med strenglisten der tager lang tid og som måske kan gå ned.
  finally
    StringList.Free; // Frigiv den igen
    Screen.Cursor := crDefault; // Sæt cursor tilbage til normal
  end;
end;

Her sætte jeg cursoren til timeglasset inden en tung operation. Ved at have linien "Screen.Cursor := crDefault;" inde i finally delen kan jeg være 100% sikker på, at den vil blive sat tilbage igen - også selvom min lange operation eventuelt fejler (og raiser en exeption). Det er det samme med StringListen som er sikker på at blive frigivet fra hukommelsen.

3b. Try-except: Hvis noget går galt, så ender man her.

Samme konstruktion men hvis noget fejler i Delphi, så kastes en exception. Du kan tænke på det som en slags "lad-mig-komme-ud-herfra"/goto-tingest. Dit Delphiprogram har sådan en try-except som fanger de fejl der må komme og den sørger for at vise en fejlmeddelelse på skærmen. Det kan bare godt være at man gerne selv vil prøve at reparere fejlen.

function StrToIntSafe(aString : string);
begin
  try
    result := StrToInt(aString);
  except
    result := 0;
  end;
end;

Hvis aString er tom eller indeholder tegn og ikke tal, så sker der en exception hvor den kører StrToInt. Programmet springer ned til except-delen og afvikler den kode der står der. I dette tilfælde sætter den returneringsresultetet til 0. Alle andre gange springer programmet over denne kodestump.

Der er noget mere i konceptet, men hvis du forstår min omfangsrige tekst, så må du indrømme at det er da smart.

Endelig kan man kombinere det:

begin
  Screen.Cursor := crHourglass;
  try
    DataSet.BeginTransaction;
    try
      Query.Open; // Gør et eller andet;
      DataSet.EndTransaction;
    except
      DataSet.RollbackTransaction;
      raise;
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

Begin-, End- og rollbackTransaction er en databasefunktion der sikrer at enten er data skrevet til tabellen eller så er den ikke. Hvis det er nyt for dig, så stol på at det også er smart. Hvis operationen gik galt, så ruller den operationen tilbage og raiser i øvrigt exceptionen igen. Udenfor sidder en garanti for at cursoren er normal når proceduren forlades. Pyha. Det var meget tekst.

Grunden til jeg næsten altid skriver "end; // try" eller "end; // for" skyldes at hvis man har mange af den slags inden i hinanden, så mister man let overblikket. Det er bare en forklarende kommentar som jeg sætter efter "end;".

4. Ja. Det var bare en procedure jeg lige griflede ned.

5. Man kan godt sige at TListView er en smartere variant af TListBox - men det er en tilsnigelse og det vil ikke virke om du prøvede med en TListBox.

5. Jeg ville placere koden i en ButtonClick event:

procedure TfrmMain.Button1Click(Sender : TObejct);
begin
  ListView1.Items.BeginUpdate;
  try // Her starter endnu en try
    SearchTekster(ListView1,Edit1.Text);
  finally
    ListView1.Items.EndUpdate;
  end; // Slut på Try-finally
end;

6. Når man åbner en Query, så afvikles SQL-scriptet. Her henter den alle de linier der indeholder aPattern. Derefter gennemløbes det fundne og placeres i listviewet.

  while not Query1.EOF do begin
    with aListView.Items.Add do begin // Indsæt linie i TListView
      Caption := IntToStr(Query1.FieldByName('Id').AsInteger)); // i liniens caption sæt ID
      SubItems.Add(Query1.FieldByName('Tekster').AsString); // placér tekster ved siden af.
    end; // with
    Query1.Next; // Ryk en linie frem i Query'en.
  end; // while
Avatar billede koden12 Nybegynder
27. marts 2004 - 17:06 #6
Mange tak.
Jeg har fundet på en selv i mellemtiden..
Den er nogenlunde (jeg vil helt klart prøve din! )
Den som jeg har lavet gentager sig selv og det er noget skidt..
Du kan se den her :

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
if datamodul.DataModule2.ADOTable1.Locate('Bruges',Edit1.text,[loCaseInsensitive,loPartialKey]) then

if edit1.text<>'' then
if datamodul.DataModule2.adotable1.Locate('Bruges',edit1.text,[Lopartialkey]) then
listbox1.Items.Add(datamodul.DataModule2.ADOTable1.FieldByName('Bruges').AsString);
datamodul.DataModule2.ADOTable1.Next;



end;


VH
koden12
Avatar billede koden12 Nybegynder
27. marts 2004 - 22:28 #7
Jeg beklager men der er alt for meget "smartSprog" i den til at jeg forstår det.
Jeg har siddet mange , mange timer med den nu..

ADOQuery1.Sql := 'select nummer,tekst from kode where tekst like :Pattern';
Giver denne fejl :
[Error] findkodemodul.pas(86): Incompatible types: 'TStrings' and 'String'

datamodul.DataModule2.ADOQuery1.Parameters.ParamByName('Pattern').AsString := '"*' + aPattern + '*"';

Giver den her fejl :
[Error] findkodemodul.pas(87): Undeclared identifier: 'AsString'

Ingen af dem ved hvad patten er for noget.
Heller ikke jeg..
Skal den ikke have en

VAR

Det ville hjælpe meget hvis du ikke lavde det om til ditten og datten men bare
kaldte det , det som det hedder.
At fx
ListView1: TListView;
Ikke skal hedder :
aListView : TTListView
Det tager timer at finde ud af hvorfor den fejler..
Desuden vil det aldrig komme til at hedde  TTListView
Men højst TListView

Jeg kan ikke få det til at virke
og jeg kan ikke gennemskue den..

VH
koden12
Avatar billede koden12 Nybegynder
27. marts 2004 - 22:40 #8
Parameters.ParamByName

Jeg har ingen anelse hvad det er for noget ?

VH
koden12
Avatar billede janbb Juniormester
28. marts 2004 - 03:15 #9
koden12:

  Er det det at de koder er ADODataSet-kommandoer og du skal ha såån
  en komponent på din form, der driller ?.

  Nej det er næppe tilfældet, men bare for en sikkerheds skyld vil jeg nævne det
selvom det evt får mig til at virke lidt naiv :)
Avatar billede hrc Mester
29. marts 2004 - 02:18 #10
Koden12: Det var aldrig tiltænkt at mit eksempel var køreklart, men lad os tage det nogenlunde i den rækkefælge som du melder dem:

ADOQuery1.Sql := 'select nummer,tekst from kode where tekst like :Pattern;';

Her er tale om en lille sjuskfejl fra min side (og jeg beklager). ADOQuery.Sql er en TStringList så du skal lige tilføje et .Text, altså ADOQuery.Sql.Text := ''. Det er noget du kan gøre i delphi's "Object Inspector", linie SQL (tryk på ...-knappen) og indtast sætningen: select nummer,tekst from kode where tekst like :Pattern;

Når du har tastet ovenstående ind (du skal naturligvis også have en database parat som indeholder tabellen og dens felter), så prøv at klikke på Parameters. Der skulle gerne være en parameter der hedder Pattern.

Det smarte er her, at sætningen er den samme, men at du ændrer værdien af Pattern. Det sker med linien ADOQuery1.Parameters.ParamByName('Pattern').Value := aPattern;

Du brokker dig over at jeg kalder en parameter for noget med a. Det kan ikke være mere simpelt, men det kræver rutine. Rutine opnår man ved at være stringent med hvordan man gør tingene. Jeg sætter ALTID et a foran en parameter og sætter ALTID et f foran variable som gemmer sig i en klasses private del.

Hvis en variabel hedder noget med a, så kan du være 100% sikker på at den findes i procedurehovedet, eksempelvis denne:

  procedure SearchTekster(aListView : TTListView; aPattern : string);

aListView er ikke nødvendigvis det samme som ListView1. Det er noget jeg bestemmer når jeg kalder SearchTekster.

Nej. Der skal ikke være en VAR i procedurehovedet. Når jeg kalder SearchRec:

  SearchRec(ListView1,'politik');

så sender jeg i virkeligheden pointeren til ListView1 med over (og en pointer er bare RAM-adressen hvor ListView1-objektet ligger). I realiteten betyder det, at aListView = ListView1. Mht. aPattern så kunne jeg forbedre det lidt ved at tilføje et const foran:

  procedure SearchTekster(aListView : TTListView; const aPattern : string);

Nu må du ikke fortvivle. Jeg tror at grunden du er gået lidt i selvsving skyldes, at det er lettere end du tror. Der var de to fejl du nævnte og har man aldrig arbejdet med varianter af TStrings så er det ikke logisk at jeg havde glemt at sætte .Text bagefter. Resten er ligeud af landevejen.

Proceduren gør følgende (den rettede):

procedure SearchTekster(aListView : TTListView; const aPattern : string);
begin
  try
    // Antager at aPattern indeholder 'politik' - der søges afhængigt af store og
    // små bogstaver. Ved ikke hvordan man rammer "POLitik" og lignende - det kan
    // betyde at du skal gemme strengen i "uppercase" og konvertere tilbage når du
    // viser strengen.

    // Sæt værdien ind i Pattern parameteren
    Query1.Parameters.ParamByName('Pattern').Value := QuotedStr('*' + aPattern + '*');

    Query1.Open; // Kør select sætningen med den værdi som Pattern indeholder

    aListView.Items.BeginUpdate; // For hurtigere opdatering af ListView'et
    try
      aListView.Items.Clear; // Ryd listen

      // Gennemløb de fundne linier
      while not Query1.EOF do begin

        // Opret et TListItem (en linie) og fyld data i
        with aListView.Items.Add do begin
          Caption := IntToStr(Query1.FieldByName('Id').AsInteger);
          SubItems.Add(Query1.FieldByName('Tekster').AsString);
        end; // with

        Query1.Next; // Tag næste fundne record
      end; // while
    finally
      aListView.Items.EndUpdate; // Slut på opdatering.
    end; // try
  finally
    Query1.Close; // Sikrer at Queryen er lukket igen.
  end; // try
end;

Skal du have en variant som passer til et ListBox så kommer den her

procedure SearchTekster2(aStrings : TStrings; const aPattern : string);
begin
  try
    // Sæt værdien ind i Pattern parameteren
    Query1.Parameters.ParamByName('Pattern').Value := QuotedStr('*' + aPattern + '*');

    Query1.Open; // Kør select sætningen med den værdi som Pattern indeholder

    aStrings.BeginUpdate; // For hurtigere opdatering af ListView'et
    try
      aStrings.Clear; // Ryd listen

      // Gennemløb de fundne linier
      while not Query1.EOF do begin

        // Fyld teksten i en TListBox (ikke plads til ID)
        aStrings.Add(Query1.FieldByName('Tekster').AsString);

        Query1.Next; // Tag næste fundne record
      end; // while
    finally
      aStrings.EndUpdate; // Slut på opdatering.
    end; // try
  finally
    Query1.Close; // Sikrer at Queryen er lukket igen.
  end; // try
end;

I dit program kalder du proceduren på følgende måde:

procedure Button1OnClick(Sender : TObject);
begin
  SearchTekster2(ListBox1.Items,'politik');
  // Fordi ListBox1.Items er TStringList som er en nedarving af TStrings
  // (Ignorér det for nuværende)
end;
Avatar billede hrc Mester
29. marts 2004 - 04:19 #11
Ja, ja. Den var lige ved at knække mig. Der er masser der har problemer med at få en ADOQuery med "like" til at virke. Det skyldes jokertegnet '*' som fungerer i Access hvis man skriver SQL der. I Delphi (og PHP for den sags skyld) er det '%' der er joker.

Der er i øvrigt også den tegnvise joker: Hvis man vil søge hann? (for Hanne og Hanna) så virker ? i Access, mens man her skal bruge _ (understregning). Desuden narrede de hersens parametre mig. Jeg troede at der skulle anførselstegn på de strenge jeg fodrede Pattern ('"%politik%"'), men dem sætter den åbenbart selv.

Den endelige funktion (bemærk at jeg har placeret den som en del af DM, hvilket du også burde):

procedure TDM.SearchTekster2(aStrings: TStrings; const aPattern: string);
begin
  aStrings.BeginUpdate; // For hurtigere opdatering af ListView'et
  try
    aStrings.Clear; // Ryd listen
    ADOQuery.Parameters.ParamByName('Pattern').Value := '%' + aPattern + '%';
    ADOQuery.Open;
    while not ADOQuery.EOF do begin
      aStrings.Add(ADOQuery['Tekster']);
      ADOQuery.Next;
    end; // while
  finally
    ADOQuery.Close;
    aStrings.EndUpdate; // Slut på opdatering.
  end; // try
end;
Avatar billede koden12 Nybegynder
29. marts 2004 - 10:12 #12
WAW , du får da lavet noget..Det var ikke min hensigt sådan at øge din arbejdsbyrde med SÅ meget.
Jeg kigger på den , lige nu har jeg travlt.
DM hvad er det ?
(Danmarks Mesterskab? : )
Jeg tester det af senere.
VH
Koden12
Avatar billede koden12 Nybegynder
29. marts 2004 - 15:46 #13
Jeg er Utrolig taknemlig over at du gider... virkelig..
Tak..

Koden12
Avatar billede koden12 Nybegynder
29. marts 2004 - 16:05 #14
ADOQuery1.Sql := 'select nummer,tekst from kode where tekst like :Pattern;';

Den der linje har jeg prøvet at sætte ind forskellige steder.
I Form1.aktivate

I sql flere steder.

Den passer kun hvis jeg sætter den ind i SQL under SQL Strings..
Så laver den , den der Pattern..
Ellers ikke.

VH
koden12
Avatar billede koden12 Nybegynder
29. marts 2004 - 16:30 #15
DEN VIRKER !!!

Den med listbox ...


Den anden med :

SearchTekster(ListView1.Items,Edit1Text);
finally
ListView1.Items.EndUpdate;
end;

Giver fejl :

[Error] sogtestpas.pas(73): Incompatible types: 'TStrings' and 'TListItems'

Men jeg er godt tilfreds med at listbox virker.
Det var jo den jeg ønskede : )

Sikker en masse jeg lærte... mange.. mange tak !!

VH
koden12

PS
Skulle forresten have været til semanar på SAS hotellet kl 13.00 med
Borland som er kommet til DK.
Jag var inviteret : )
Desværre nåede jeg det ikke.
De skulle præsenterer deres nye Delphi med ASP kode inde (eller sådan noget)

**********
Jeg vil forsat gerne vide hvad DM er ?

Koden12
Avatar billede hrc Mester
29. marts 2004 - 21:56 #16
Nå, var vennerne i Danmark. Det er vel også kun rimeligt de kigger forbi en gang imellem, idet deres firma jo er grundlagt vha. et dansk produkt, Poly Pascal. En lille annekdote i øvrigt. Da Poly-Pascal gutterne forsøgte at sælge deres produkt i USA til kr1500 så gik salget af h. til. Så satte de prisen til kr4500 og så var successen hjemme. Tale om omvendt logik.

DM er det navn jeg har givet mit TDataModule - bare en forkortelse idet jeg ikke gider at skrive DetteErMitDataModul.ADOQuery1 osv. Det er lettere med DM.

Har du bemærket at "like" tingesten er ligeglad med store og små bogstaver (der skal nok være noget med de danske bogstaver)?

Dine problemer med at få ListView'en til at du skyldes at du skriver SearchTekster(ListView1.Items...) I parameterhovedet kan du se at den forventer en TListView og det er ikke det du sender med. Fjern .Items og så tror jeg det virker.
Avatar billede koden12 Nybegynder
29. marts 2004 - 22:52 #17
Den er genial.. hverken mere eller mindre..Selv om
Listviev kan jeg ikke få til at virker.. men pyt..Med eller uden Item..
***
Sjov historie : )
Poly_pascal er rigtig nok en dansk opfindelse..Flere ting er lavet med den.
Her forleden dag opdagede jeg at Stellar Offices et lille regnskab program DK
er lavet i Poly-Pacal...
I dag må den være milliarder værd..

****
den søgning har jeg søgt : ) i mange år..

VH
koden12
Avatar billede hrc Mester
29. marts 2004 - 23:07 #18
'Like' var ikke let at få til at virke, det medgiver jeg blankt. Brug den med omtanke da det er en tung operation for en database. Hvis du engang kommer til at arbejde på en stresset database, så er denne operation sikkert forbudt og så må man finde andre veje (plukke vigtige ord ud af teksten og placere dem i et indeks eller lignende).

Mht. ListViewet skal du sætte det i report mode: ViewStyle := vsReport. Dernæst skal du dobbeltklikke på komponenten og lave to kolonner - så tror jeg den virker.
Avatar billede koden12 Nybegynder
29. marts 2004 - 23:13 #19
Jeg kender kun ? like fra php pg Mysql..

Param var helt ny for mig..

VH
koden12
Avatar billede koden12 Nybegynder
30. marts 2004 - 11:31 #20
Jeg kan godt nok ikke få den til at virke med Datamodul selv om det er i USES og hele balladen..Det har jeg måtte pille af, desværre..

VH
koden12
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