无法在 Delphi XE8 中使用 MSSQL 时间戳作为参数

Cannot use MSSQL Timestamp as a parameter in Delphi XE8

我们正在将我们的一个项目从 Delphi XE 升级到 XE8。我们的审计代码使用 MSSQL(在本例中为 2012)数据库中的 TIMESTAMP 字段,并从 table 中选择,并将其用作 WHERE 子句中的参数。

我们现在不再得到任何结果运行以下代码:

procedure TForm2.Button1Click(Sender: TObject);
begin
  ADODataset1.CommandText := 'SELECT * FROM CURRENCYAUDIT';
  ADODataset2.CommandText := 'SELECT * FROM CURRENCYAUDIT WHERE Audit_Timestamp = :Timestamp';
  ADODataset2.Parameters.Refresh;

  ADODataset1.Open;
  if ADODataset1.FieldByName('audit_timestamp').IsNull or ADODataset1.IsEmpty then
  begin
    showmessage('nothing to compare');
  end;

  ADODataset2.Parameters[0].Value := ADODataset1.FieldByName('audit_timestamp').Value;
  ADODataset2.Open;
  caption := inttostr(ADODataset2.RecordCount);
end;

其中 CurrencyAudit 是包含非空时间戳 audit_timestamp 字段的任何旧 MSSQL table。

表单的标题为 0,未显示任何消息。

知道如何让它工作吗?尝试了 AsString(无意义的字符串,0 个结果)、AsSQLTimestamp(参数不接受)和 AsBytes(0 return)。不幸的是,.Value 的 return 仅评估为 'variant array of byte',这对 visualise/see 它是什么没有帮助。

编辑:运行 它作为 .AsBytes 并在调试器中查看,我可以看到 XE verison returning 0,0,0,0,0,8,177,22 而XE8 returning 17,32,0,0,0,0,0,0。检查(真实)数据库的其他字段显示记录是相同的。看起来像是从 DB

读取 TIMESTAMP 时的错误

我正在使用两个 AdoQueries。以下内容在 D7 中对我来说工作正常,在 AdoQuery2 中正确返回 1 行,但在 XE8 中返回 0 条记录,因此显然与您 运行 遇到的 XE8 问题相同。

var
  S : String;
  V : Variant;
begin
  AdoQuery1.Open;
  S := AdoQuery1.FieldByName('ATimeStamp').AsString;
  V := AdoQuery1.FieldByName('ATimeStamp').AsVariant;
  Caption := S;
  AdoQuery2.Parameters.ParamByName('ATimeStamp').Value := V;
  AdoQuery2.Open;

只是为了测试,我运行将我的 AdoQuery1 和 AdoQuery2 针对同一台服务器 table。

更新: 我有一个与你的答案中的方法类似的方法,它避免了对 Int64ToByteArray 的需要,但代价是一些稍微混乱(且效率较低) ) Sql,这可能不合您的口味。

在我的源 AdoQuery 中,我有这个 Sql

select *, convert(int, atimestamp) as inttimestamp from timestamps

在目的地一个

select *  from timestamps where convert(int, atimestamp) = :inttimestamp

这当然避免了在第二个 AdoQuery 上需要 varBytes 参数,因为可以获取时间戳列值的整数版本并将其分配给 inttimestamp 参数。

顺便说一句,在你原来的 q

  if ADODataset1.FieldByName('audit_timestamp').IsNull or ADODataset1.IsEmpty then

这两个表达式最好反过来写。除非 ADODataset1 有持久字段,如果它在打开时不包含任何记录,引用 audit_timestamp 应该引发 "Field not found" 异常。

似乎 EMBT 破坏了 TIMESTAMP 到字节数组的转换。 bytearray 的 XE 版本是正确的,手动将数据提取为 int64,然后手动构建 bytearray(是否有开箱即用的功能?)并将其用作 XE8 中的参数。

我不知道这是否是其他二进制数据类型的类似问题。希望不会!

工作代码:

procedure TForm2.Button1Click(Sender: TObject);
var
  TestArray: TArray<Byte>;
  j: integer;
  function Int64ToByteArray(const inInt: uint64): TArray<Byte>;
  var
    i: integer;
    lInt: int64;
  begin
    SetLength(result, 8);
    lInt := inint;
    for i := low(result) to high(result) do
    begin
      result[high(result)-i] := lInt and $FF;
      lInt := lInt shr 8;
    end;

  end;

begin
  ADODataset1.CommandText := 'SELECT  *, cast(audit_timestamp as bigint) tmp FROM CURRENCYAUDIT';
  ADODataset2.CommandText := 'SELECT * FROM CURRENCYAUDIT WHERE Audit_Timestamp = :Timestamp';
  ADODataset2.Parameters.Refresh;

  ADODataset1.Open;
  if ADODataset1.FieldByName('audit_timestamp').IsNull or ADODataset1.IsEmpty then
  begin
    showmessage('nothing to compare');
  end;

  ADODataset2.Parameters[0].Value := Int64ToByteArray(ADODataset1.FieldByName('tmp').asInteger);
  ADODataset2.Open;
  caption := inttostr(ADODataset2.RecordCount);
end;

我还检查了我的整个(真实)table 并确保所有其他字段匹配以确保它不是一次性的!

我会用 EMBT 开张罚单让他们坐视不管 5 年;-)

https://quality.embarcadero.com/browse/RSP-11644