如何使用 Delphi 7 ADOQuery.ExecSQL 在 SQL Server 上更新 DateTime,保留毫秒?

Posted

技术标签:

【中文标题】如何使用 Delphi 7 ADOQuery.ExecSQL 在 SQL Server 上更新 DateTime,保留毫秒?【英文标题】:How to update a DateTime on SQL Server preserving milliseconds using Delphi 7 ADOQuery.ExecSQL? 【发布时间】:2012-12-26 07:24:58 【问题描述】:

如何使用 Delphi 7 ADOQuery.ExecSQL 在 SQL Server 2012 上更新 DateTime 以保留毫秒?

SQL 服务器似乎降低了秒精度,因此我在 SQL Server 2012 DateTime2[7] 字段上没有毫秒或微秒。 MS Access 不会使用此 ADO 查询减少毫秒数。以下 ADOQuery 代码似乎适用于我关心的所有 DataTypes,除了 SQL Server DateTime

我使用的连接字符串是: Provider=SQLNCLI11.1;Integrated Security=SSPI;User ID="";Initial File Name="";Server SPN="";Data Source=myPC\SQLSERVER2012EXP;Initial Catalog=MyDatabase

这是我的代码:

function ExecuteNonQry(Conn:TADOConnection;Sql:string;Params:Variant): Integer;
 Execute a query that does not return a recordset.  Returns number of rows 
  affected. Params can be any unique name, they are all dealt with in order
  of appearance in the parameter array and in the sql string.
  E.g. SQL 
    'INSERT INTO customers (id,name,country) VALUES (:id,:name,:country)';
  E.g. calling code:
    sql := 
     'INSERT INTO Sessions (SessionType_cod , HasErrors, Start_Timestamp) ' + 
     ' VALUES (:1, :2, :3)';
    params := VarArrayOf([ 1, true, Now ]);
    ExecuteNonQry( GvConnection1, sql, params );

    Note: Do not use ADO with Paradox - will be slow and possibly error prone.

    Tested the following DataTypes with "Insert Into" and "Update" queries
    **********************************************************************
    Delphi          MS Access    SQL Server      Paradox
    -----------------------------------------------------
    ftInteger       Long         int
    ftString        Text(255)    nvarchar(255)
    ftString        Memo         nvarchar(max)
    ftBoolean       Boolean      bit
    ftDateTime      Date         datetime2(7)
    ftDouble        Double       float

var
  qry : TADOQuery;
begin
  assert( Conn <> nil);
  assert( Sql <> '');
  qry := TADOQuery.Create(nil);
  qry.DisableControls;
  qry.SQL.Text := Sql;
  AddParametersToQuery(qry, Params);
  qry.Connection := Conn;
  result := qry.ExecSQL;
end;

procedure AddParametersToQuery(var Qry: TADOQuery; Params: Variant);
 Version 1b.  (Uses Delphi function to replace "DIRegEx" dependencies)
  Add parameters (type and value) to ADO query. ADOQuery must have SQL 
  text set and Params is a variant array. 
  Limitations: SQL Server drops DateTime second precision digits for 
  milliseconds or microseconds.
  E.g. Sql: 
    'INSERT INTO Sessions (SessionType_cod , HasErrors, Start_Timestamp) 
     VALUES (:1, :2, :3)';
  or  Sql: 
    'INSERT INTO Sessions (SessionType_cod , HasErrors, Start_Timestamp) 
     VALUES (:mykey, :HasErrors, :myDateTime)';

const
  regPattern = ':';
var
  str: String;
  val: Variant;
  i: Integer;
  sl: TStrings;
begin
  assert( Qry.SQL.Text <> '');

  // in some cases this is necessary.
  Qry.Parameters.ParseSQL(Qry.SQL.Text, true);        

  sl := TStringList.Create;
  try
    // find all param wordssql text such as '1, 2, 3'
    sl := ExtractWordsToStrings(':', Qry.SQL.Text);   

    // loop through any matches found
    for i := 0 to sl.Count -1 do            
    begin
      str := sl[i];
      val := GetVarParam(i, Params);
      // in some cases this is necessary.
      Qry.Parameters.ParamByName(str).DataType := 
          VarTypeToDataType(Ord(VarType(val)) );
      Qry.Parameters.ParamByName(str).Value := val;
    end;
  finally
    sl.Free;
  end;
end;

更新

MS Access 和 SQL Server 的修改代码:

if (VarType(val) = varDate) And IsSqlServerProvider(Conn.Provider) then begin
  // needed for SQL Server
  Qry.Parameters.ParamByName(str).Value := 
     FormatDateTime('yyyymmdd hh:nn:ss.zzz', val)   
end
else
begin
  // in some cases this is necessary.
  Qry.Parameters.ParamByName(str).DataType := 
     VarTypeToDataType(Ord(VarType(val))); 
  Qry.Parameters.ParamByName(str).Value := val;
end;

【问题讨论】:

要保留毫秒,您可以简单地使用 TDateTime 的本机类型声明数据库中的列:double。这将意味着获得 TFloatField,但 IIRC 他们有一个 AsDateTime 成员。 【参考方案1】:

通过 Ado 的 DateTime2(7) 被视为 TWideStringField。 日期时间将被视为 TDateTimeField。 如果分配 TDatetime,内部转换将忽略 DateTimeToStr(dt) 等毫秒。 您可以使用自己的转换为 WideString 来处理这个问题。

Function MyDateTimeString(d:TDateTime):String;
begin
  Result := FormatDateTime('yyyymmdd hh:nn:ss.zzz',d);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
dt:TdateTime;
begin
  dt := now;
  Caption := FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz',dt);
  Adoquery1.Paramcheck := true;
  Adoquery1.SQL.Text := 'Insert into Tab (a,b,DT) Values (:a,:b,:DT)';
  Adoquery1.Parameters.ParseSQL(Adoquery1.SQL.Text,true);
  Adoquery1.Parameters.ParamByName('a').Value := 1;
  Adoquery1.Parameters.ParamByName('b').Value := 2;
  Adoquery1.Parameters.ParamByName('DT').Value := MyDateTimeString(dt);
  Adoquery1.ExecSQL;
end;

使用 then 以下将导致四舍五入:

  Adoquery1.Parameters.ParamByName('DT').DataType := ftDateTime;
  Adoquery1.Parameters.ParamByName('DT').Value := dt;

【讨论】:

这确实适用于 SQL 服务器,谢谢!我希望 ParamByName 也适用于 MS Access。有什么想法让它对两者都有效吗?如果失败了,我想我将不得不通过解析连接字符串并相应地设置参数来确定连接的类型。我还没有找到一个很好的 ADO 枚举类型来查找您的连接类型,所以它是连接字符串... var i:Integer; begin for I := 0 to AdoConnection1.Properties.Count - 1 do begin Listbox1.Items.Add(AdoConnection1.Properties[i].Name+'='+VarToStr(AdoConnection1.Properties[i].Value)) end; Showmessage(AdoConnection1.Properties.Item['Provider Friendly Name'].Value );结尾; //或者更好地使用'Provider Name' 谢谢。 TADOConnection.Provider 属性中的“Provider Name”字符串运行良好。

以上是关于如何使用 Delphi 7 ADOQuery.ExecSQL 在 SQL Server 上更新 DateTime,保留毫秒?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Delphi 在控制台应用程序中激活玻璃效果(Windows Vista/7)

Delphi 7 SOAP 身份验证和会话 ID 如何

如何使用 Delphi 7 ADOQuery.ExecSQL 在 SQL Server 上更新 DateTime,保留毫秒?

如何将字符串或信息发送到我在 Delphi 7 中使用 Createprocess 打开的进程

Vista / 7下的本机提示/工具提示与Delphi 7?

delphi 7 中数组如何当参数