Page 1 of 2 1 2 LastLast
Results 1 to 15 of 23

Thread: Query's in een loop, fout

  1. #1

    Query's in een loop, fout

    Geachte lezer,

    Waarschijnlijk doe ik iets fundamenteel niet goed maar ik krijg een foutmelding wanneer ik een hoop SELECT query's uitvoer.

    Ik ga door een oude paradox tabel heen van het begin tot het einde. Ik wil data overzetten naar een nieuwe tabel in MYSQL. Eerst wil ik nakijken of het betreffende record al bestaat alvorens het in te voeren in de nieuwe tabel.

    Ik vermoed dat ik iets fundamenteels fout doe. Na ongeveer 3900 x krijg ik de volgende foutmelding (3900 is elke keer +/-1 hetzelfde!)

    First chance exception at $7C7E2AFB. Exception class TDBXError with message 'Can't connect to MySQL server on '19mysq2.168.0.100' (10048)'. Process EMMS.exe (4444)

    Het query component staat gekoppeld aan een DBX SQL verbinding. De SQL voor de Query voor ik elke keer in:

    Query.SQL.CLEAR; Query.SQL.APPEND('SELECT....');

    In het begin sloeg dezelfde fout toe na ongeveer 800 query's. Toen had ik na elke QUERY een recordcount om te kijken of een dubbele voorkomt.

    Weet iemand wat ik verkeerd doe?

    Bij voorbaat dank!

    Rogier

  2. #2
    Wat mij opvalt is de hostnaam: '19mysq2.168.0.100'
    Is die juist?

  3. #3
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Interessant. Maak je gebruik van transactions? Als je 1 query gebruikt met met parameters, heb je daar ook last van. Een test om te kijken of het aan de DBX of MySQL ligt, is de Query opslaan in een textfile en deze inlezen in je client.
    Delphi is great. Lazarus is more powerfull

  4. #4
    Hoi,

    Nog eens geprobeerd. Ik denk dat ik per ongeluk een copy-paste foutje heb gemaakt:

    First chance exception at $7C7E2AFB. Exception class TDBXError with message 'Can't connect to MySQL server on '192.168.0.100' (10048)'. Process EMMS.exe (5960)

    Rogier

  5. #5
    @J Kuiper

    Transactions: eeeh, geen idee. Ik dacht dat dat alleen handig was bij een batch invoer of update query's.
    Het Query component staat gekoppeld aan de DBX sql connection.

    De SQL voer ik in met query.sql.append. Ik gebruik geen parameters, dus in feite voer ik elke keer in "SELECT Nummer FROM tabel where klantnummer = 11111 LIMIT 1"

    Daarna Query.open

    en dit dan in een loop.

  6. #6
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Oke. Je maakt gebruik van een directe DBX verbinding TSQLQuery, terwijl de doelstelling van DBX is om gebruik te maken van een Dataprovider met een clientdataset. Maar er is wel een mogelijkheid om dat rechtstreeks te doen, al heb ik dat nooit zo gedaan.
    Kan je een voorbeeldcode meegegeven zodat ik kan zien wat je doet?
    Delphi is great. Lazarus is more powerfull

  7. #7
    Hier wat code, heb er even wat andere dingetjes uitgesloopt.
    Ik heb de veldnamen bij de database code er maar uitgehaald, anders wordt het zo lang.

    Mijn dank is groot.

    Delphi Code:
    1. procedure THS.Scannen
    2. var
    3.     klantnummer : integer;
    4.     email: string;    
    5.     aanwezig : boolean;
    6. begin        
    7.     dm.MailTabelRelator.Active := true;
    8.     dm.Hoofdtabel.Active := true;
    9.     dm.MailTabelRelator.First;
    10.     dm.Actietabel.Active := true;
    11.     dm.Q_aanwezig.Active := true;    
    12.     while not dm.MailTabelRelator.Eof do
    13.     begin      
    14.         email := dm.MailTabelRelatorEmail.Value;                
    15.         email := stringreplace(email,'''','',[rfReplaceAll, rfIgnoreCase]); // haal ' eruit
    16.         aanwezig := hs.EmailAanwezig(email);        
    17.         dm.Q_aanwezig.SQL.Clear;
    18.         dm.Q_aanwezig.SQL.Append('SELECT * FROM hoofmailtabel');
    19.         dm.Q_aanwezig.SQL.Append('WHERE Email = "' + email + '" LIMIT 1');
    20.         dm.Q_aanwezig.Open;
    21.                
    22.         dm.MailTabelRelator.Next;
    23.     end;
    24.     dm.MailTabelRelator.Active := false;
    25.     dm.Hoofdtabel.Active := false;
    26.     dm.Actietabel.Active := false;
    27.     dm.Q_aanwezig.Active := false;
    28. end;
    29.  
    30. object DM: TDM
    31.   OldCreateOrder = False
    32.   Height = 684
    33.   Width = 749
    34.   object MySQLVerbinding: TSQLConnection
    35.     ConnectionName = 'MySQLConnection'
    36.     DriverName = 'MySQL'
    37.     GetDriverFunc = 'getSQLDriverMYSQL'
    38.     LibraryName = 'dbxmys.dll'
    39.     LoginPrompt = False
    40.     Params.Strings = (
    41.       'DriverName=MySQL'
    42.       'HostName=192.168.0.100'
    43.       'Database=relator3'
    44.       'User_Name=rogier'
    45.       'Password=geheim')
    46.     VendorLib = 'libmysql.dll'
    47.     Connected = True
    48.     Left = 48
    49.     Top = 40
    50.   end
    51.   object HoofdMailTabel: TSimpleDataSet
    52.     Aggregates = <>
    53.     Connection = MySQLVerbinding
    54.     DataSet.CommandText = 'hoofmailtabel'
    55.     DataSet.CommandType = ctTable
    56.     DataSet.MaxBlobSize = -1
    57.     DataSet.Params = <>
    58.     Params = <>
    59.     Left = 48
    60.     Top = 104
    61.     object HoofdMailTabelNummer: TIntegerField
    62.       FieldName = 'Nummer'
    63.       Required = True
    64.     end
    65.     object HoofdMailTabelEmail: TStringField
    66.       FieldName = 'Email'
    67.       Required = True
    68.       Size = 45
    69.     end
    70.  
    71. etc, etc...
    72.  
    73.   object Q_aanwezig: TSQLQuery
    74.     Active = True
    75.     MaxBlobSize = -1
    76.     Params = <>
    77.     SQL.Strings = (
    78.       'SELECT * FROM hoofmailtabel'
    79.       'WHERE Email = "rogier@test.nl" LIMIT 1')
    80.     SQLConnection = MySQLVerbinding
    81.     Left = 240
    82.     Top = 104
    83.     object Q_aanwezigNummer: TIntegerField
    84.       FieldName = 'Nummer'
    85.       Required = True
    86.     end
    87.     object Q_aanwezigEmail: TStringField
    88.       FieldName = 'Email'
    89.       Required = True
    90.       Size = 45
    91.     end
    Last edited by Marcel; 26-Aug-09 at 00:02.

  8. #8
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Vreemd dat dat werkt. Meestal (bij mij dan) kan je een tabel via DBX niet direct benaderen. Deze wordt op read only gezet en accepteert geen wijzigingen. Daardoor moet je met DBExpress altijd gebruik maken van een clientdataset en een dataprovider.
    Je moet het dan zo zien
    text Code:
    1. TSQLQuery <--> TDataSetProvider <--> Tclientdataset

    Wat ik heb gedaan is het volgende:
    1. Plaats een TSQLConnection, TSQLQuery, TDatasetprovider en een TClientdataset op je form.
    2. Zet in TSQLQuery.SQL.Text alleen je select (bijvoorbeeld : SELECT * FROM tabel)
    3. Zet een button op je form
    4. Maak je gebruik van de onderstaande code:
    delphi Code:
    1. procedure TForm1.Button1Click(Sender: TObject);
    2. var teller : integer;
    3. begin
    4.   // Open de connectie naar MySQL
    5.   SQLConnection1.Open;
    6.   // open de tabel, waarvan gekopieerd moet worden
    7.   table1.active := true;
    8.   // open de clientdataset. Deze is de container van de tabel
    9.   // die de veldwaarden krijgt van de bron tabel
    10.   clientdataset1.Active := true;
    11.   teller := 1;
    12.   while not table1.eof do
    13.   begin
    14.     //controleer of de naam al bestaat
    15.     if not clientdataset1.Locate('naam',
    16.          vararrayof([table1.FieldByName('naam').AsString]),[]) then
    17.     begin
    18.       // copieer de hele regel naar de container tabel
    19.       // nu zijn het 29 velden, maar dat kan je aanpassen naar de
    20.       // hoeveelheid velden in je brontabel
    21.       clientdataset1.AppendRecord([table1.Fields[0].Value, table1.Fields[1].Value,
    22.          table1.Fields[2].Value, table1.Fields[3].Value, table1.Fields[4].Value,
    23.          table1.Fields[5].Value, table1.Fields[6].Value, table1.Fields[7].Value,
    24.          table1.Fields[8].Value, table1.Fields[9].Value, table1.Fields[10].Value,
    25.          table1.Fields[11].Value,table1.Fields[12].Value,table1.Fields[13].Value,
    26.          table1.Fields[14].Value,table1.Fields[15].Value,table1.Fields[16].Value,
    27.          table1.Fields[17].Value,table1.Fields[18].Value,table1.Fields[19].Value,
    28.          table1.Fields[20].Value,table1.Fields[21].Value,table1.Fields[22].Value,
    29.          table1.Fields[23].Value,table1.Fields[24].Value,table1.Fields[25].Value,
    30.          table1.Fields[26].Value,table1.Fields[27].Value,table1.Fields[28].Value]);
    31.       //stuur de gewijzigde gegevens naar de MySQL tabel
    32.       clientdataset1.ApplyUpdates(0);
    33.     end;
    34.     label1.Caption := inttostr(teller);
    35.     inc(teller);
    36.     application.ProcessMessages;
    37.     table1.Next;
    38.   end;
    39.   table1.Active := false;
    40.   clientdataset1.Active := false;
    41.   label1.Caption := 'klaar';
    42.   SQLConnection1.Close;
    43. end;
    Dit copieert 25000 records met 29 velden in ongeveer 10 minuten naar de MySQL database. Geen onderbrekingen.
    Wil je het sneller, dan kan je de applyupdates() als laatste voor sluiting van de tabel plaatsen.

    Kijk hier eens naar en probeer het eens.
    Soms kan DBX best wel lastig zijn, omdat je afhankelijk bent van een derde party. Als alternatief kan je AnyDAC of Zeos gebruiken (gratis). Devart heeft ook een goede tool, maar daar moet je iets voor betalen.
    Delphi is great. Lazarus is more powerfull

  9. #9
    Hartelijk bedankt!

    Ik ga het morgen op kantoor testen. Ik heb wel nog een paar vragen / opmerkingen.

    - Ik merk steeds meer dat het jezelf aanleren van een programmeertaal zijn beperkingen heeft. Nu lees ik over datasetproviders, etc, etc en moet zeggen dat het lijkt alsof ik wat fundamentele kennis tekort kom.
    - Waar hebben jullie de basis geleerd wat betreft databses in Delphi? Tot nu toe heb ik alles met BDE gedaan. Eenvoudige tabellen zonder datasetproviders etc. Met Mysql werken lijkt toch wel anders.

    Om terug te komen op mijn vraag / de case / uw codevoorbeeld:

    - zoals gezegd, het werkte tot 4000 pogingen. Het betrof SELECT queryś. Er werd dus niets weg geschreven naar de database. Komt het misschien door Delphi 2009 dat het deels wel werkte en bij u niet? Ik heb sowieso vele problemen met Delphi 2009 en vroeger gekochte of gedownloade componenten.

    - ik vermoed dat uw oplossing fundamenteel goed is. Ik vraag me echter af wat ik fundamenteel fout heb gedaan. Is het af vuren van zoveel opdrachten teveel van het goede?

    Nogmaals bedankt voor de antwoorden. Hier ben ik oprecht blij mee. Morgen ga ik testen!

    Rogier

  10. #10
    Marcel

  11. #11
    Member
    Join Date
    Mar 2003
    Location
    Netherlands
    Posts
    638
    Met een TSQLQuery op zichzelf kan je ook alleen maar lezen/updates en inserts uitvoeren. Wil je via een Grid of DBEdits aan de slag dan zou je met providers moeten gaan werken. Hij maakt ook gebruik van de TSimpleDataSet. Dit component bevat de provider,clientdataset en SQLTable/Query component al in zich en daarmee kan je al data verwerken.

    Wat je misschien kan proberen is in plaats van een TSimpleDataSet een TSQLQuery gebruiken en dan direct je INSERT/UPDATE statements schrijven. Je kan dan een bulk opbouwen van bijvoorbeeld 100 records en die in 1x committen richting je database. Dat werkt een stuk stabieler.

    Verder zou ik kijken naar de MySQL (server) configuratie en je server load. Misschien dat je server de load niet aankan of dat je MySQL omgeving niet goed geconfigureerd is (index cache size etc).
    Alex "leXTer" van der Vliet
    Delphi Programmer

    And may the source be with you.

  12. #12
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Met een TSQLQuery op zichzelf kan je ook alleen maar lezen/updates en inserts uitvoeren.
    Hoe dan alex. Overal lees ik dit als je gebruik maakt van DBExpress:

    This may be the time to tell you that all dbExpress datasets (TSQLDataSet,
    TSQLTable, TSQLQuery and TSQLStoredProc – if returning a dataset) are very special, in
    that their contents is read-only and unidirectional. This means that you can read the
    records in a TSQLTable, but only from the first one to the last one (you cannot navigate
    back), and you cannot make any changes to this data.

    Als jij een voorbeeld hebt om de componenten rechtstreeks te benaderen, ben ik daar best in geïnteresseerd.
    Wat je misschien kan proberen is in plaats van een TSimpleDataSet een TSQLQuery gebruiken en dan direct je INSERT/UPDATE statements schrijven.
    Dat wist ik niet en heb dat nergens op google of documentatie gelezen.

    @RogierVLD
    Ik merk steeds meer dat het jezelf aanleren van een programmeertaal zijn beperkingen heeft
    Ja en Nee. Ik ben ook begonnen zonder e.v.t. cursussen en heb jaren geleden 1 boek aangeschaft, die mij de basis uitlegde van Oriented Programeren in Delphi. Daarna ben ik aan de slag gegaan met een doelstelling om een programma te schrijven, waar men echt aan had. Dat moest ook wel, het oude DOS programma liep qua database uit zijn voegen.
    Dat geeft een stok achter de deur. Alleen wil je dan niet weten hoe je code er dan uit ziet. Maar je leert er wel van.
    Waar hebben jullie de basis geleerd wat betreft databses in Delphi? Tot nu toe heb ik alles met BDE gedaan. Eenvoudige tabellen zonder datasetproviders etc. Met Mysql werken lijkt toch wel anders.
    Ik heb jaren met de BDE gewerkt (dbase bestanden) en daar was niets mis mee. Nu werk ik met een (voor sommige mensen niet) echte databaseserver, waardoor je informatie op een iets andere wijze moet verwerken. Veel verschil zit er niet met de BDE. Je kan nog steeds een DBEdit / DBGrid gebruiken om gegevens te raadplegen / wijzigen.
    Maar er zijn verschillende manieren om je data op te halen. Een daarvan is DBExpress. Dit is ontworpen om onafhankelijk gebruik te maken van databasecomponenten, zonder te weten wat voor database-engine erachter zit. Er zijn nog niet zoveel mensen, die daar gebruik van maken, omdat het werken daarmee nogal wat handigheid met zich meebrengt. Gelukkig zijn er nog third-party components, die je wat directer naar je database brengt en wat makkelijker zijn om te gebruiken.
    Komt het misschien door Delphi 2009 dat het deels wel werkte en bij u niet?
    Zeg maar jij, hoor . Je basis voor programmeren is een goede stabiele IDE. Mijn d2007 update 3 werkte heel stabiel (daarvoor veel problemen met DBX) en met D2009 ben ik ook erg tevreden. Zorg dat je gekochte componenten altijd compatible zijn met je huidige Delphi versie.
    ik vermoed dat uw oplossing fundamenteel goed is. Ik vraag me echter af wat ik fundamenteel fout heb gedaan. Is het af vuren van zoveel opdrachten teveel van het goede?
    Of het fundamenteel goed is, kunnen er nog vele discussies van komen. Mijn code werkt gewoon, omdat het getest is en voor dat doeleinde geschikt is om te doen.

    Kennis is macht. Als je op het forum een aantal topics leest, komen daar antwoorden te staan, waarvan ik nog geen weet heb. Ik ben geen goeroe, maar kan met mijn kennis toch (hopelijk) genoeg mensen helpen voor hun problemen. Staar je dus niet blind. Je komt er wel en dat heeft zijn tijd nodig.
    Veel jongeren denken dat ze auto kunnen rijden, omdat ze weten waar het gaspendaal zit. In de praktijk kost het jaren om een auto goed te kunnen besturen. Zo is het met programmeren ook.

    Wat ik je wel meegeven is dit: heb plezier in wat je doet, maar belangrijker is de feeling van het programmeren. Er zijn mensen, die maar naar een bepaalde richting gaan zonder te weten wat ze doen. Kortom, laat een arts geen bedrijfsapplicaties maken. Het feit dat ik Delphi redelijk onder de knie hebt, komt omdat ik vroeger veel hebt gespeeld met andere (4GL)talen. De basis is voor al deze talen hetzelfde.

    Succes
    Delphi is great. Lazarus is more powerfull

  13. #13
    @John en Alex, Bedankt!

    @John
    Jouw oplossing werkt prima en snel. Nogmaals bedankt.

    @Alex
    Wat is een directe query? Een TSQLQuery via een SQLconnecction en verder niets? Dat had ik dus min of meer gedaan, alleen dan in een loop. Of zie ik nu iets verkeerds? (alleen SELECT SQL's)

    @Alex en John
    - Het comitten of "applyen" van updates werkt toch alleen bij schrijf handelingen(updates/inserts/deletes)?

    - Kan je ook iets instellen van automatische apply wanneer je bv in een DBGRID rommelt?

    - Is het volgende juist: stel je hebt een aantal wijzigingen in een grid aangebracht of een aantal records toegevoegd en je drukt daarna op applyupdates, dat dit een "transactie" genoemd wordt?

    - Hoe kan je het beste te werk gaan wanneer er "dubbelen" zijn of index conflicten. Ik lees in de help dat je bij applyupdates moet instellen hoeveel "fouten" er maximaal mogen zijn en dat je daarna met Reconcile() moet werken.
    Heeft iemand een simpel code voorbeeld? (de Delphi help vind ik op dit gebied redelijk lastig om eerlijk te zijn). Is dit ook een soort dataset met de soort errors?

    Wederom bedankt!

    Rogier

  14. #14
    Member
    Join Date
    Mar 2003
    Location
    Netherlands
    Posts
    638
    Je kan een TSQLQuery gebruiken om een select uit te voeren en dan door de records heen te gaan. De TSimpleDataSet heb je eigenlijk alleen maar nodig als je bijvoorbeeld een Grid eraan wilt koppelen.

    Een directe query is bijvoorbeeld UPDATE TABEL SET VELD1=:WAARDE1, VELD2=:WAARDE2 WHERE ID=:NUMMER

    Via de parameters zet je dan de juiste waardes die je wilt meegeven en dat vuur je dan richting je database. Dit zou met een TSQLQuery moeten werken. Wat je ook kan doen is je Query direct via je DBConnection uitvoeren door middel van de ExecuteDirect functie, Deze ondersteunt ook parameters.

    Transacties kan je gebruiken binnen (updates/inserts/deletes) (DML) statements maar ook voor select statements. Het committen van een transactie is puur om de transactie af te sluiten om daarna een nieuwe transactie te kunnen starten. Het voordeel van transacties is dat wanneer het updaten/posten van data fout gaat je een rollback kan doen tot voor het starten van je transactie zodat je geen foute data krijgt.

    ApplyUpdates en CommitUpdates voer je alleen uit wanneer je data is gewijzigd binnen je applicatie. Deze twee procedures zorgen ervoor dat dbExpress de wijzigingen doorstuurt naar de database. Deze functies zou je zelf moeten uitvoeren om onnodige transacties te voorkomen dus nee ik zou niet naar iedere wijziging binnen een grid een applyUpdates uitvoeren. Stel je hebt een grid van 10 velden en je gaat na iedere wijziging binnen een rij een Apply/Commit updates uitvoeren dan zou je voor 1 veld 10 updates krijgen. Doe je dat nu aan het einde van je wijziging dan zou je 1 update voor 10 velden krijgen wat dan weer veel sneller is.

    Voor je andere stelling; Wanneer je ApplyUpdates uitvoert worden de wijzigingen op je ClientDataSet vastgezet. Daarna moet je nog een CommitUpdates uitvoeren om deze naar je database te sturen. Dit zijn geen transacties. Transacties zou je zelf voor je query uit moeten voeren en af moeten sluiten.

    Dubbele records kan je ten eerste al voorkomen door ze altijd een unieke key te geven. Maar dubbele data zal je zelf moeten controleren door middel van bijvoorbeeld een SELECT statement. Wanneer een record al bestaat kan je dan eventueel een melding teruggeven.
    Alex "leXTer" van der Vliet
    Delphi Programmer

    And may the source be with you.

  15. #15
    John Kuiper
    Join Date
    Apr 2007
    Location
    Almere
    Posts
    8,747
    Je kan een TSQLQuery gebruiken om een select uit te voeren en dan door de records heen te gaan. De TSimpleDataSet heb je eigenlijk alleen maar nodig als je bijvoorbeeld een Grid eraan wilt koppelen.
    Alex, dat is theorie en zal dat graag in de praktijk willen zien.

    @RogierVld
    Ik ben blij dat ik je kon helpen.
    Wat Alex zegt is ten dele waar. Je gaat inderdaad niet bij elke wijziging van een veld een applayupdates() uitvoeren. Dan heeft het hele concept geen nut. Ik weet niet wat je gebruikt om een record te posten, maar ja zal het na een TSQLquery.post kunnen doen on in de TQuery.AfterPost event. Maar let op met inserten met (primary) keys met auto increment. Omdat de hele configuratie van de TSQLQuery.dataset overgaat naar de TClientdataset, weet deze ook dat er sleutelvelden zijn. Daardoor moet je zelf een tijdelijke unieke key verzinnen om meerdere records te inserten in je TClientdataset. Anders krijg je de melding: Duplicate key. Maar nu komt het grote probleem (in ieder geval bij MySQL). De tijdelijke key wordt inserted in de echte key is er van auto increment geen sprake

    Het gebruik van TClientdataset is niet makkelijk. Er zijn nog weinig mensen, die er daadwerkelijk mee werken en dan hebben ze nog hun eigen manier van invullen omtrend een insert met een key. Eigenlijk is auto increment met Clientdataset uit ten boze. Unieke key is belangrijk in databasewereld en helpt je door heel snel te zoeken naar je gegevens.

    TClientdataset wordt veel gebruikt i.c.m. met datasnap (midas). Dan maak je minimaal een 3-tier applicatie, waarin je communicatie via de remote server gaat (zie artikelen)
    Delphi is great. Lazarus is more powerfull

Page 1 of 2 1 2 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •