3

我遇到了快速报告显示来自 ADOquery 的不正确数据的问题。我使用以下 sql.text

SELECT * FROM JOB_DATA 
INNER JOIN CUSTOMER ON JOB_DATA.CUST_CODE = CUSTOMER.CUST_CODE 
WHERE JOB_DATA.SHIP_DATE Between [Date1] And [Date2] 
ORDER by SHIP_DATE

快速报告仅显示 SHIP_DATE = null 的数据。

如果我抛出一个 TDBgrid 并将其附加到附加到同一个 ADOquery 的数据源,那么 dbgrid 会显示正确的信息。

我没有想法,有什么建议吗?

要回答有关日期来源的问题:

var
  date1:string;
  date2:string;
  sql_str:string;
begin
  date1:=inputbox('Date Range','Enter Beginning Date','');
  Try
    StrToDate(date1);
  Except 
    On EConvertError Do
    Begin
      MessageDlg('Please enter a valid date.  Format xx/xx/xx',
         mtError, [mbOK], 0);
      //ShowMessage('Please enter a valid date.  Format `enter code here`xx/xx/xx');
      Exit;
    End;
  End;

  date2:=inputbox('Date Range','Enter Ending Date','');
  Try
    StrToDate(date2);
  Except 
    On EConvertError Do
    Begin
      MessageDlg('Please enter a valid date.  Format xx/xx/xx',
          mtError, [mbOK], 0);
      //ShowMessage('Please enter a valid date.  Format `enter code here`xx/xx/xx');
      Exit;
    End;
  End;

  sql_str:= 'SELECT * FROM JOB_DATA INNER JOIN CUSTOMER ON ' +
            'JOB_DATA.CUST_CODE = CUSTOMER.CUST_CODE ' +
            'WHERE JOB_DATA.SHIP_DATE Between ';
  sql_str:= sql_str+ ''' ';
  sql_st:=sql_str + date1;
  sql_str:= sql_str+ '''';
  sql_str:= sql_str+ ' AND ';
  sql_str:= sql_str+ ''' ';
  sql_str:= sql_str+ date2;
  sql_str:= sql_str+ ' ''';

  with ADOQuery5 do 
  begin
    Close;
    SQL.Clear;
    SQL.text:= sql_str;
    Open;
  end;
  frxreport2.ShowReport();
end;

ADOquery 附加到 frxDBDataset2,它附加到 frxReport2。我没有做任何事情来改变查询中的结果。

不,我在报告中没有代码,它都是从向导生成的。

4

2 回答 2

2

FastReport 不能仅显示记录 where SHIP_DATEis ,因为如果和被正确分配NULL,您的查询不应该根据您的WHERE子句返回它们。这意味着您的数据集和 FastReport 未正确连接,或者您的代码中为子句分配日期值的某些内容是错误的,并且日期未正确提供给查询。Date1Date2BETWEEN

开始查找的第一个地方是确保所有报告列都正确分配了正确TfrxDataSet和正确的数据库列。(单击报告项目(文本对象或任何可能的对象),并检查其DataSetDataField属性以确保它们正确。)

如果这不是问题,则可能是您构建查询的方式,这可能没有正确格式化 ADO 的日期。(您只是使用任何格式来传递StrToDate调用而不引发异常。)

您设置 SQL 的方式确实不可取。当您尝试管理在代码中引用自己时,它是不可读且不可维护的。

您应该使用参数,这首先可以保护您免受 SQL 注入,但也允许数据库驱动程序为您正确格式化引用的值和日期并保持可读性。(您也可以为参数使用可读的名称,这样当您在六个月后看到它们时,您就会知道它们的含义。)

var
  // Your other variable declarations here
  StartDate, EndDate: TDateTime;
begin
  Date1 := InputBox(Whatever);
  try
    StartDate := StrToDate(Date1);
  except
    // Handle EConvertError
  end;
  Date2 := InputBox(Whatever);
  try
    EndDate := StrToDate(Date2);
  except
    // Handle EConvertError
  end;

  sql_str := 'SELECT * FROM JOB_DATA J'#13 +
             'INNER JOIN CUSTOMER C'#13 +
             'ON J.CUST_CODE = C.CUST_CODE'#13 +
             'WHERE J.SHIP_DATE BETWEEN :StartDate AND :EndDate';

  with ADOQuery5 do 
  begin
    Close;
    // No need to clear. If you're using the same query more than once,
    // move the SQL assignment and the Parameter.DataType somewhere
    // else, and don't set them here.
    // The query can be reused just by closing, changing parameter values,
    // and reopening.
    SQL.Text := sql_str;
    with Parameters.ParamByName('StartDate') do
    begin
      DataType := ftDate;
      Value := StartDate;
    end;
    with Parameters.ParamByName('EndDate') do
    begin
      DataType := ftDate;
      Value := EndDate;
    end;
    Open;
  end;
  frxReport2.ShowReport;
end;
于 2013-08-07T22:53:27.090 回答
0

当我开始遇到 ADO 问题时,我会记录这些信息。

您需要创建自己的记录器...但这是开玩笑的...请注意,它将记录传递给查询的参数值,包括 SQL。

procedure TLogger.SetUpConnectionLogging(aParent: TComponent);
var
  a_Index: integer;
begin
  for a_Index := 0 to aParent.ComponentCount - 1 do
    if aParent.Components[a_Index] is TAdoConnection then
    begin
      TAdoConnection(aParent.Components[a_Index]).OnWillExecute :=  WillExecute;
      TAdoConnection(aParent.Components[a_Index]).OnExecuteComplete :=  ExecuteComplete;
    end;
end;

procedure TLogger.ExecuteComplete(Connection: TADOConnection;
  RecordsAffected: Integer; const Error: Error; var EventStatus: TEventStatus;
  const Command: _Command; const Recordset: _Recordset);
var
  a_Index: integer;
begin
  AddLog('AdoConnection ExecuteComplete', True);
  AddLog('Execution In MilliSeconds', IntToStr(MilliSecondsBetween(Time, FDif)));
  AddLog('Execution In Seconds', IntToStr(SecondsBetween (Time, FDif)));
  AddLog('Execution In Minutes', IntToStr(MinutesBetween (Time, FDif)));
  AddLog('CommandText', Command.CommandText);
  if Assigned(Command) then
  begin
    AddLog('Param Count', IntToStr(Command.Parameters.Count));
    for a_Index := 0 to Command.Parameters.Count - 1 do
    begin
      AddLog(Command.Parameters.Item[a_Index].Name, VarToWideStr(Command.Parameters.Item[a_Index].Value));
    end;
    AddLog('CommandType', GetEnumName(TypeInfo(TCommandType),Integer(Command.CommandType)));
  end;
  AddLog('EventStatus', GetEnumName(TypeInfo(TEventStatus),Integer(EventStatus)));
  if Assigned(RecordSet) then
  begin
    AddLog('CursorType', GetEnumName(TypeInfo(TCursorType),Integer(Recordset.CursorType)));
    AddLog('LockType',  GetEnumName(TypeInfo(TADOLockType),Integer(Recordset.LockType)));
  end;
  AddLog('RecordsAffected',  IntToStr(RecordsAffected));
  AddLog('AdoConnection ExecuteComplete', False);
end; 

procedure TLogger.WillExecute(Connection: TADOConnection;
  var CommandText: WideString; var CursorType: TCursorType;
  var LockType: TADOLockType; var CommandType: TCommandType;
  var ExecuteOptions: TExecuteOptions; var EventStatus: TEventStatus;
  const Command: _Command; const Recordset: _Recordset);
begin
  AddLog('Connection WillExecute', True);
  AddLog('Connection Name', Connection.Name);
  AddLog('CommandText', CommandText);
  AddLog('CommandType', GetEnumName(TypeInfo(TCommandType),Integer(CommandType)));
  AddLog('EventStatus', GetEnumName(TypeInfo(TEventStatus),Integer(EventStatus)));
  AddLog('CursorType', GetEnumName(TypeInfo(TCursorType),Integer(CursorType)));
  AddLog('Connection WillExecute', False);
  FDif :=  Time;
end;
于 2013-08-08T21:20:28.520 回答