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

Thread: MultiPaste

  1. #1

    MultiPaste

    Hallo hallo,

    Gisteren las ik op een blog van Nick Hodges over de multipaste (edit->multipaste) functie in Delphi. Echt super makkelijk als je bijvoorbeeld een SQL statement wilt converteren naar een formaat dat je kunt gebruiken in Delphi. Voorheen liep ik te rotzooien met GExperts die wel een PastStringAs functie heeft, maar daarna moest ik altijd nog een replace doen omdat het niet direct in het juiste formaat stond.

    Code:
    SELECT 
      t.typname
    FROM
      pg_type t
    naar

    Code:
    ' SELECT ' +
    ' t.typname ' +
    ' FROM ' +
    ' pg_type t ' + // Laatste + moet je wel handmatig weghalen
    Last edited by luigi; 22-Mar-19 at 16:33. Reason: VideoRipper :)

  2. #2
    Fornicatorus Formicidae VideoRipper's Avatar
    Join Date
    Mar 2005
    Location
    Vicus Saltus Orientalem
    Posts
    5,708
    Waarschijnlijk is multipaste in latere Delphi's aanwezig?

    Ik heb werkelijk geen idee waar dit over gaat; nou ja: ik heb een vermoeden, maar da's dan ook alles want ik
    zie niet echt het voordeel van hetgeen je hierboven schrijft.

    Direct SQL-scripts in code schrijven vind ik persoonlijk al een beetje "Ieuw", maar soms kun je niet anders,
    omdat je geen datamodule tot je beschikking hebt waar je het makkelijk in kwijt kunt en dan beperk ik het toch
    echt tot de eenvoudige 'SELECT COUNT(*) AS Aantal FROM SomeTable'-queries.
    Echt langere queries gaan gewoon in een query (met eigen luxe editor) op een datamodule.

    Maar goed: bovenstaande is een mening die blijkbaar niet iedereen is toegedaan.

    Overigens denk ik dat jouw voorbeeld niet goed gevonden gaat worden door je database, maar dit terzijde.
    (een komma achter t.typname, geen spaties tussen SELECT, veldnaam FROM en tabelnaam).
    TMemoryLeak.Create(Nil);

  3. #3
    Het is inderdaad vooral makkelijk om een SQL statement om te zetten naar een Delphi formaat. Wat het doet is dat je voor en na iedere regel op je clipboard een tekst kunt plaatsen. Kan bijvoorbeeld ook makkelijk zijn bij het maken van unittests waarbij je (met bijvoorbeeld modelmaker code explorer) de namen van de methodes kunt kopiëren er een [Test] Procedure voor kunt zetten en een ';' er na.

    In principe ben ik er ook geen voorstander van om SQL rechtstreeks in mijn unit te zetten. Sinds kort gebruik ik echter een ORM, waarbij je voor wat ingewikkeldere queries deze wel in je unit moet zetten. Om toch overzicht te bewaren zet ik deze queries in een eigen unit als constante.

    Overigens denk ik dat jouw voorbeeld niet goed gevonden gaat worden door je database, maar dit terzijde.
    (een komma achter t.typname, geen spaties tussen SELECT, veldnaam FROM en tabelnaam).
    Weet niet waar je het over hebt

  4. #4
    Een ORM dat me dwingt om code te inlinen in Pascal zou er bij mij niet in komen. Gelukkig is het ook niet echt waar, want je kan het natuurlijk in een resource zetten, of op een andere manier uit een externe, leesbare, bron in die string krijgen.
    1+1=b

  5. #5
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Dat is wat ik vaak doe: van een "sql" bestand delphi code genereren. heb je meteen een check of de sql deugt, na bijv. een database verandering. Verder maakt het natuurlijk niets uit of een SQL als string in je source-code staat of als string in een DFM toch?

  6. #6
    't Is toch niet te onderhouden in Delphi? Als je je statement aan wilt passen moet je alle plusjes en kwootjes eruit gaan halen, je aanpassing testen, en het dan allemaal weer terugzetten. Tenzij je bereid bent om je hele statement op een enkele regel te schrijven, en het minder dan 250 tekens is, dan hoef je alleen maar eventuele kwootjes te ontdubbelen.
    1+1=b

  7. #7
    Ben het inmiddels met GolezTrol eens dat de sql als constante met plusje en quotjes erg onpraktisch is in gebruik. Heb inmiddels een simpele componentje gemaakt om mijn sql in te zetten, het heeft slecht één property die vergelijkbaar is met de sql property van een normaal query component. Dit werkt inderdaad een stuk fijner.

    Voor de multipaste zijn trouwens ook andere toepassingen.

  8. #8
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    @Golez: het is juist zeer onderhoudbaar. En leesbaar. Een dfm met sql-statements en (nog erger) persistent fields komt er bij mij niet meer in. Teveel ultra-boring + foutgevoelig werk gehad aan die constructies.

  9. #9
    Teveel ultra-boring + foutgevoelig werk gehad aan die constructies.
    Dat heb ik nu juist als de queries in code staan.

    Snap het probleem ook niet als je een tool zoals Delphi gebruikt dan zet je ze gewoon in je query component. Heb je meer dan parameters nodig, kies dan een query component die met macro's overweg kan.

    Heb me al zo vaak de rambam gezocht in code van anderen waar in de component een SQL staat maar op een (onlogische) plek wordt vervangen door een query in code. Zie het dan maar eens uit te vogelen.

    Maar goed, ieder zijn ding.

  10. #10
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Het is in mijn geval een typed descendent van - in mijn geval - TFDQuery, die zelf zijn velden en parameters aanmaakt. En ja: het is een public const string.
    En ja, die string genereer ik, maar ik genereer meer. En het is natuurlijk eigenlijk een noodoplossing.
    Maar het heeft voor mij meer voordelen dan nadelen. Het designtime-geklik door de persistent fields, nooit meer ParamByName() of FieldByName(). Als ik mijn pre-builder draai weet ik dat alle datasets een valide sql hebben en dat alle velden er zijn - of niet meer zijn en dan compileert mijn applicatie niet meer, wat een groot voordeel is :-)
    En je weet altijd welk type dataset je voor je neus hebt. Het werkt natuurlijk alleen voor statische datasets, waar die sql niet verandert, maar dat is bij dfm's ook.
    Als je de screenshots ziet van de datamodules die ik moet onderhouden op mijn werk, snap je het misschien :-)
    Last edited by Anoniem; 29-Apr-19 at 10:13.

  11. #11
    of FieldByName()
    Hoe doe jij dan iets met velden in je query?

    Als je de screenshots ziet van de datamodules die ik moet onderhouden op mijn werk, snap je het misschien :-)
    Je maakt me nieuwsgierig

  12. #12
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Wel, die maak ik met mijn pre-builder ahv een sql file:

    Dit staat in sql-file Dmo.Klant.Classes.sql
    Code:
    [TQryKlant]
    select
      k.*
    from
      klant k
    where
      k.actief >= :actief
    order by
      k.klantnaam
    En de pre-builder maakt ahv een database de volgende unit aan, door de sql gewoon uit te voeren (met enige restricties). Heel aardige bijkomstig voordeel is dat ik DataSet.Veld kan gebruiken.
    QryKlant.Naam.Value := 'Eric';
    En dat ik echt OOP kan werken. En private datasets kan gebruiken (in plaats van alles published in een .dfm).

    Code:
    unit Dmo.Klant.Classes;
    
    interface
    
    uses System.Classes, Data.DB, Data.SqlTimSt, FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Comp.DataSet, FireDAC.Comp.Client;
    
    type
      TQryKlant = class(TFDQuery)
      public
        const _sql: ansistring = 
          'select' + sLineBreak + 
          '  k.*' + sLineBreak + 
          'from' + sLineBreak + 
          '  klant k' + sLineBreak + 
          'where' + sLineBreak + 
          '  k.actief >= :actief' + sLineBreak + 
          'order by' + sLineBreak + 
          '  k.klantnaam';
        constructor Create(aOwner: TComponent); override;
      public
        ID                 : TIntegerField;
        ACTIEF             : TBooleanField;
        KLANTNUMMER        : TIntegerField;
        AANHEF             : TWideStringField;
        KLANTNAAM          : TWideStringField;
        ADRES              : TWideStringField;
        POSTCODE           : TWideStringField;
        PLAATS             : TWideStringField;
        MAIL               : TWideStringField;
        LESSENPERJAAR      : TIntegerField;
        LESDAG             : TIntegerField;
        LESTIJD            : TTimeField;
        LESMINUTEN         : TIntegerField;
        BEDRAG             : TCurrencyField;
        KORTING            : TCurrencyField;
        MOBIEL             : TWideStringField;
        TELEFOON           : TWideStringField;
        INFO               : TWideMemoField;
        TENAAMSTELLING     : TWideStringField;
        BANKREKENING       : TWideStringField;
        INCASSO            : TBooleanField;
        PERIODIEKEFACTUUR  : TBooleanField;
        BETALERNAAM        : TWideStringField;
        FACTUURSJABLOON_ID : TIntegerField;
        ParamACTIEF: TFDParam;  // Boolean
      end;
    
    implementation
    
    constructor TQryKlant.Create(aOwner: TComponent);
    begin
      inherited;
      ResourceOptions.ParamCreate := False;
      Fields.LifeCycles := [lcPersistent];
      SQL.Text := string(_sql);
      ID := TIntegerField.Create(Self); 
      with ID do begin
        FieldName := 'ID';
        ProviderFlags := [pfInUpdate,pfInWhere,pfInKey];
        Required := True;
        DataSet := Self;
      end;
      ACTIEF := TBooleanField.Create(Self); 
      with ACTIEF do begin
        FieldName := 'ACTIEF';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := True;
        DataSet := Self;
      end;
      KLANTNUMMER := TIntegerField.Create(Self); 
      with KLANTNUMMER do begin
        FieldName := 'KLANTNUMMER';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := True;
        DataSet := Self;
      end;
      AANHEF := TWideStringField.Create(Self); 
      with AANHEF do begin
        FieldName := 'AANHEF';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      KLANTNAAM := TWideStringField.Create(Self); 
      with KLANTNAAM do begin
        FieldName := 'KLANTNAAM';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      ADRES := TWideStringField.Create(Self); 
      with ADRES do begin
        FieldName := 'ADRES';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      POSTCODE := TWideStringField.Create(Self); 
      with POSTCODE do begin
        FieldName := 'POSTCODE';
        Size := 16;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      PLAATS := TWideStringField.Create(Self); 
      with PLAATS do begin
        FieldName := 'PLAATS';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      MAIL := TWideStringField.Create(Self); 
      with MAIL do begin
        FieldName := 'MAIL';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      LESSENPERJAAR := TIntegerField.Create(Self); 
      with LESSENPERJAAR do begin
        FieldName := 'LESSENPERJAAR';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      LESDAG := TIntegerField.Create(Self); 
      with LESDAG do begin
        FieldName := 'LESDAG';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      LESTIJD := TTimeField.Create(Self); 
      with LESTIJD do begin
        FieldName := 'LESTIJD';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      LESMINUTEN := TIntegerField.Create(Self); 
      with LESMINUTEN do begin
        FieldName := 'LESMINUTEN';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      BEDRAG := TCurrencyField.Create(Self); 
      with BEDRAG do begin
        FieldName := 'BEDRAG';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      KORTING := TCurrencyField.Create(Self); 
      with KORTING do begin
        FieldName := 'KORTING';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      MOBIEL := TWideStringField.Create(Self); 
      with MOBIEL do begin
        FieldName := 'MOBIEL';
        Size := 16;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      TELEFOON := TWideStringField.Create(Self); 
      with TELEFOON do begin
        FieldName := 'TELEFOON';
        Size := 16;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      INFO := TWideMemoField.Create(Self); 
      with INFO do begin
        FieldName := 'INFO';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DisplayValue := dvFull;
        DataSet := Self;
      end;
      TENAAMSTELLING := TWideStringField.Create(Self); 
      with TENAAMSTELLING do begin
        FieldName := 'TENAAMSTELLING';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      BANKREKENING := TWideStringField.Create(Self); 
      with BANKREKENING do begin
        FieldName := 'BANKREKENING';
        Size := 32;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      INCASSO := TBooleanField.Create(Self); 
      with INCASSO do begin
        FieldName := 'INCASSO';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := True;
        DataSet := Self;
      end;
      PERIODIEKEFACTUUR := TBooleanField.Create(Self); 
      with PERIODIEKEFACTUUR do begin
        FieldName := 'PERIODIEKEFACTUUR';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := True;
        DataSet := Self;
      end;
      BETALERNAAM := TWideStringField.Create(Self); 
      with BETALERNAAM do begin
        FieldName := 'BETALERNAAM';
        Size := 64;
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
      FACTUURSJABLOON_ID := TIntegerField.Create(Self); 
      with FACTUURSJABLOON_ID do begin
        FieldName := 'FACTUURSJABLOON_ID';
        ProviderFlags := [pfInUpdate,pfInWhere];
        Required := False;
        DataSet := Self;
      end;
    
      ParamACTIEF := Params.Add('ACTIEF',ftBoolean, 0, ptInput);
    end;
    
    end.

  13. #13
    Hmmm ok.

    Maar als je zo'n opzet wilt, kun je dan niet beter een van de vele ORM's gaan gebruiken?

  14. #14
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Misschien ja. Want ik ben nog op zoek naar iets "strongly typed" dat in multitier kan werken. Voorlopig is er geen interesse en geld voor bij de klant :-)

  15. #15
    En vooral: als je die SQL in aparte files hebt staan, en je genereert daar je code uit, dan heb je natuurlijk effectief niet het probleem van SQL in code. Mijn argument van onderhoudbaarheid ging over het onderhoud van queries in Delphi-code. Met genereerde (en opnieuw te genereren) code, is dat probleem er natuurlijk niet.
    1+1=b

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)

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
  •