Avatar billede friiiiis Novice
08. marts 2011 - 18:14 Der er 9 kommentarer og
1 løsning

DateTime og SQLServer

Hej,

jeg prøver at lave en simepl SELECT til en SQLserver:

'Select * from TableAntalSpor where DatoFra <=  + DateTimeForSQL(now+1) and DatoTil >=  DateTimeForSQL(now-1)'

Hvor DateTimeForSQL er:

DateTimeForSQL(const dateTime :TdateTime):string;
begin
  Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', dateTime);
end

Jeg har også prøvet at fjerne ":" og erstattet det med "." men uanset hva så vil sql ikke accepterer det?

min connectionstring er: "'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;......
Avatar billede hrc Mester
08. marts 2011 - 18:21 #1
Lad være med at lave din select i "hånden". SQL-server laver en "execution-plan" hver eneste gang du laver sådan et script. Det bruger proceskraft og er en forkert fremgangsmåde.

Lad også være med at bruge *. Angiv de felter du skal bruge - og hvis du skal bruge alle, så angiv dem.

Den rigtige er at bruge parametere:
with ADOQuery.Create(nil) do
  try
    SQL.Add('select *');
    SQL.Add('from dbo.TableAntalSpor');
    SQL.Add('where (datofra <= :datofra)');
    SQL.Add('  and (datotol >= :datotil)');
    Parameters.ParamByName('datofra').Value := incDay(Today);
    Parameters.ParamByName('datotil').Value := incDay(Today - 1);
    SQL.Open;
    while not Eof do
    begin
      Next;
    end;
  finally
    Free;
  end;

Her opretter SQL-serveren en plan for denne ene query og den genbruger den
Avatar billede arne_v Ekspert
08. marts 2011 - 18:23 #2
Du kan ikke bruge en Delphi funktion inden i en streng med SQL.
Avatar billede hrc Mester
08. marts 2011 - 18:24 #3
Håber jeg kan komme med i kampen om de 0 points...

En endnu sikrere procedure er at tvinge parameteren til den rette datatype:

with Query.Parameteres.ParamByName('datotil') do
begin
  DataType := ftDateTime;
  Value := incDay(Today,-1);
end;

Behøver jeg nævne at IncDay og Today findes i DateUtils-unitten.
Avatar billede friiiiis Novice
08. marts 2011 - 18:38 #4
Ja, der lærte jeg så ret så eftertrykkeligt noget nyt...

Jeg opretter et ny spørgsmål nu (Datetime_sqlserver) med point... hrc - du løste det så du får point (Håber det er ok Arne_v)

hrc: Jeg forstår ikke "Lad være med at lave din select i "hånden" - hvad er alternativet til SQL.add(...)??
Avatar billede hrc Mester
09. marts 2011 - 08:21 #5
Der er ikke et alternativ til SQL.Add men forskellen er, om du laver en select sådan: 'select * from dbo.testtabel where id = 22' eller om du laver den sådan: 'select * from dbo.testtabel where id = :id'. Sidstnævnte bruger parametre og SQL-serveren behøver kun lave en execution plan. Desuden slipper man for alt det sjov mht. dato-konverteringer. Det er måden man gør det på, simpelthen.
Avatar billede kroning Nybegynder
09. marts 2011 - 09:35 #6
Jeg har aldrig brugt ParamByName, jeg troede at parametrene blev indsat inden sql sætningen blev sendt til serveren og det derfor ikke gjorde nogen forskel.
Når metoden med ParamByName bruges hvordan ser sql sætningen der sendes til serveren så ud ?
Avatar billede hrc Mester
09. marts 2011 - 18:42 #7
kroning: Jeg mener at parametrene bliver lavet om til SQL-server parametre :EtEllerAndet bliver til et simpelt @ eller sådan noget. Og herefter "prepared". Det var nogle konsulenter fra et firma der ynder at lave mirakler; og de var ret sikre i deres ord. Denne artikel kommer ind på det: http://msdn.microsoft.com/en-us/magazine/cc163799.aspx (Commands and Parameters). Spørgsmålet er om hvad ADOQuery gør. Jeg mener at den laver en prepare i "Open" men Delphien er udenfor rækkevidde.

Jeg tror jeg vil lave en test. En tabel med 1 mio records. Tid på at hente dem en efter en, med og uden parametre. Måske også tjekke execution plan i Management Studio.

Den største fordel med parametre er, at man slipper for at fumle med dato-formater. Ulempen med ADO er at man gemmer til en variant.
Hvis man har oprettet en Query med ParamByName('test').Value og ikke angivet datatype, så giver det fejl hvis man tildeler en værdi der er IsNull. Derfor har jeg en class helper på TADOQuery hvor den parameteren sættes med alle oplysninger. Det virker fint og jeg vil poste helperen i morgen.
Avatar billede hrc Mester
09. marts 2011 - 19:53 #8
Der var lige denne her: http://www.codinghorror.com/blog/2005/04/give-me-parameterized-sql-or-give-me-death.html

Sender lige min class helper. Det eneste man skal gøre er at inkludere unitten de steder hvor man vil benytte SetParam proceduren.

unit UADOQueryHelper;

interface

uses
  DB, ADODB;

type
  TAODQueryHelper = class helper for TADOQuery
  public
    procedure SetParam(const aParam: string;
                      const aDataType: TDataType;
                      const aValue: variant;
                      const aSetNull: boolean = false);
  end;

implementation

uses
  Variants;

{ TAODQueryHelper }

procedure TAODQueryHelper.SetParam(const aParam: string;
                                  const aDataType: TDataType;
                                  const aValue: variant;
                                  const aSetNull: boolean);
begin
  with Parameters.ParamByName(aParam) do
  begin
    DataType := aDataType;
    if aSetNull then
      Value := NULL
    else
      Value := aValue;
  end;
end;

{
Scenarium: Indsæt record i test, testtype_ref er fremmednøgle som kan være null
// her sættes den til fordi variablen testtype_ref er 0

procedure TfrmMain.Button1Click(Sender: TObject);
var
  testtype_ref: integer;
  Query: TADOQuery;
begin
  Query := TADOQuery.Create(nil);
  try
    testtype_ref := 0; // Nulles

    Query.Connection := MyADOConnection;

    Query.SQL.Add('insert into dbo.test');
    Query.SQL.Add('(navn, testtype_ref)');
    Query.SetParam('values (:navn, :testtype_ref)');
    Query.SetParam('navn',ftString,'Hello world');

    // Sæt param = null hvis argument er true
    Query.SetParam('testtype_ref',ftInteger,testtype_ref,testtype_ref = 0);

    Query.ExecSQL;
  finally
    Query.Free;
  end;
end;
}

end.
Avatar billede friiiiis Novice
09. marts 2011 - 22:16 #9
hrc: Kan du forklarer mig:

//=== her går det ikke ====================

'Select * From TabelAntalSPor where DatoFRA  <  :Fra  and ' +
' DatoTil  > :Fra ';
SQL.add(s);
Parameters.ParamByName('Fra').Value := encodedateTime(2010,11,29,23,00,00,000);
open;
showmessage(inttostr(recordcount)); // her vises fejlagtigt 0

//====================

MEN: Hvis der skrives

'Select * From TabelAntalSPor where DatoFRA  <  :Fra1  and ' +
' DatoTil  > :Fra2 ';
SQL.add(s);
Parameters.ParamByName('Fra2').Value := encodedateTime(2010,11,29,23,00,00,000);
Parameters.ParamByName('Fra1').Value := encodedateTime(2010,11,29,23,00,00,000);
open;
showmessage(inttostr(recordcount)); // Så virker det...

hvorfor det??
Avatar billede hrc Mester
09. marts 2011 - 22:48 #10
Det er nemt nok. Der er ikke et mellemrum mellem and og DatoTil. Igen bør du nok droppe plusset hvor du kan. Lav i stedet queryen sådan her:

SQL.Add('select *');
SQL.Add('from dbo.TabelAntalSPor'); // Angiver ejerskab (dbo)
SQL.Add('where ('DatoFRA >= :FraDato));
SQL.Add('  and (DatoTil <= :FraDato)');

Hver gang du add'er så tilføjes der en ny linje og det hjælper når man gemmer SQL'en via SQL.SaveToFile(<fil>). Det er tit godt fordi man kan teste en dynamisk oprettet SQL i management studio (kigge på execution plan og andre smarte ting). Det at gemme queryen til en fil, får desværre ikke parametrene med, men det har jeg en procedure der gør. Den kan jeg godt kaste ind ... i morgen, for ungerne have have deres madpakker.

Jeg navngiver konsekvent parametrene det samme som feltnavnene. Ditto konsekvent angiver jeg hver "and" på hver sin linje og sætter paranteser omkring (sidstnævnte for syns skyld). Det er noget jeg "bare" gør, udelukkende fordi jeg har fundet at det virker bedst.
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