在Calculate Fields事件中避免重叠日期时重复计算PRIJAVA字段
解决Delphi Calculate Fields中日期重叠时PRIJAVA字段重复计算的问题
要实现日期重叠时PRIJAVA字段仅计算一次,核心思路是识别出同一日期重叠组的记录,只在组内某一条记录中计算PRIJAVA,其余记录该字段设为0。以下提供两种可行方案:
方案一:预处理标记字段(推荐,性能更优)
先给数据集添加一个布尔类型的计算字段,用来标记当前记录是否需要计算PRIJAVA,后续在CalculateFields中根据标记值执行逻辑。
步骤1:添加标记计算字段
在数据集的创建事件中添加用于标记的字段:
procedure TDataModule1.CENIKCreate(Sender: TObject); var NeedCalcField: TBooleanField; begin NeedCalcField := TBooleanField.Create(Self); NeedCalcField.FieldName := 'NeedCalculatePrijava'; NeedCalcField.DataSet := CENIK; NeedCalcField.Calculated := True; end;
步骤2:预处理标记重叠组记录
在数据集打开后,遍历所有记录,给每个日期重叠组仅标记一条需要计算PRIJAVA的记录:
procedure TDataModule1.CENIKAfterOpen(DataSet: TDataSet); var CurrentFrom, CurrentTo: TDateTime; GroupID: Variant; // 替换为你的实际分组字段(如订单ID),限定同一业务组内的重叠判断 IsMarked: Boolean; begin CENIK.DisableControls; try // 初始化所有标记为False CENIK.First; while not CENIK.Eof do begin CENIK.FieldByName('NeedCalculatePrijava').AsBoolean := False; CENIK.Next; end; // 遍历记录,标记需要计算的重叠组首条记录 CENIK.First; while not CENIK.Eof do begin GroupID := CENIK.FieldByName('GROUP_ID').Value; CurrentFrom := CENIK.FieldByName('FROM_DATE').AsDateTime; CurrentTo := CENIK.FieldByName('TO_DATE').AsDateTime; IsMarked := False; // 检查当前组是否已有重叠记录被标记 CENIK.First; while not CENIK.Eof and not IsMarked do begin if (CENIK.FieldByName('GROUP_ID').Value = GroupID) and CENIK.FieldByName('NeedCalculatePrijava').AsBoolean and // 判断日期重叠:当前记录与已有记录的日期范围存在交集 ((CurrentFrom <= CENIK.FieldByName('TO_DATE').AsDateTime) and (CurrentTo >= CENIK.FieldByName('FROM_DATE').AsDateTime)) then begin IsMarked := True; end; CENIK.Next; end; // 若当前组无已标记的重叠记录,则标记当前记录 if not IsMarked then begin CENIK.FieldByName('NeedCalculatePrijava').AsBoolean := True; end; CENIK.Next; end; finally CENIK.EnableControls; end; end;
步骤3:修改CalculateFields逻辑
根据标记字段的值决定是否计算PRIJAVA:
procedure TDataModule1.CENIKCalcFields(DataSet: TDataSet); begin CENIK.FieldByName('OSEB').AsInteger := Form10.cxComboBox1.EditValue; CENIK.FieldByName('SUM').AsCurrency := CENIK.FieldByName('PRICE').AsCurrency * CENIK.FieldByName('DAYS').Value; CENIK.FieldByName('DDV').AsCurrency := CENIK.FieldByName('SUM').AsCurrency * 22 / 100.0; CENIK.FieldByName('TT').AsCurrency := 3.13 * CENIK.FieldByName('DAYS').Value * CENIK.FieldByName('OSEB').Value ; // 仅标记为需要计算的记录才生成PRIJAVA值,其余设为0 if CENIK.FieldByName('NeedCalculatePrijava').AsBoolean then CENIK.FieldByName('PRIJAVA').AsCurrency := CENIK.FieldByName('OSEB').Value * 2.50 else CENIK.FieldByName('PRIJAVA').AsCurrency := 0; CENIK.FieldByName('TOTAL').AsCurrency := CENIK.FieldByName('SUM').AsCurrency + CENIK.FieldByName('PRIJAVA').AsCurrency + CENIK.FieldByName('TT').AsCurrency; end;
方案二:实时判断(适合小数据量场景)
如果数据集记录量不大,可以在CalculateFields事件中直接判断当前记录是否是重叠组的第一条,无需预处理:
procedure TDataModule1.CENIKCalcFields(DataSet: TDataSet); var CurrentFrom, CurrentTo: TDateTime; GroupID: Variant; IsFirstInOverlap: Boolean; CurrentRecNo: Integer; begin CENIK.FieldByName('OSEB').AsInteger := Form10.cxComboBox1.EditValue; CENIK.FieldByName('SUM').AsCurrency := CENIK.FieldByName('PRICE').AsCurrency * CENIK.FieldByName('DAYS').Value; CENIK.FieldByName('DDV').AsCurrency := CENIK.FieldByName('SUM').AsCurrency * 22 / 100.0; CENIK.FieldByName('TT').AsCurrency := 3.13 * CENIK.FieldByName('DAYS').Value * CENIK.FieldByName('OSEB').Value ; // 保存当前记录位置,避免遍历后丢失 CurrentRecNo := DataSet.RecNo; GroupID := CENIK.FieldByName('GROUP_ID').Value; // 替换为你的实际分组字段 CurrentFrom := CENIK.FieldByName('FROM_DATE').AsDateTime; CurrentTo := CENIK.FieldByName('TO_DATE').AsDateTime; IsFirstInOverlap := True; CENIK.DisableControls; try // 遍历当前记录之前的所有记录,检查是否有同组且日期重叠的记录 CENIK.First; while not CENIK.Eof and (CENIK.RecNo < CurrentRecNo) do begin if (CENIK.FieldByName('GROUP_ID').Value = GroupID) and ((CurrentFrom <= CENIK.FieldByName('TO_DATE').AsDateTime) and (CurrentTo >= CENIK.FieldByName('FROM_DATE').AsDateTime)) then begin IsFirstInOverlap := False; Break; end; CENIK.Next; end; // 回到原记录位置 DataSet.RecNo := CurrentRecNo; finally CENIK.EnableControls; end; // 根据判断结果计算PRIJAVA if IsFirstInOverlap then CENIK.FieldByName('PRIJAVA').AsCurrency := CENIK.FieldByName('OSEB').Value * 2.50 else CENIK.FieldByName('PRIJAVA').AsCurrency := 0; CENIK.FieldByName('TOTAL').AsCurrency := CENIK.FieldByName('SUM').AsCurrency + CENIK.FieldByName('PRIJAVA').AsCurrency + CENIK.FieldByName('TT').AsCurrency; end;
注意事项
- 请将代码中的
GROUP_ID、FROM_DATE、TO_DATE替换为你实际使用的分组字段和日期字段名 - 若无需按业务组区分重叠,可直接删除所有与
GroupID相关的判断逻辑 - 方案一预处理的方式性能更优,适合大数据量场景;方案二实现简单,但数据量大时会影响运行效率
内容的提问来源于stack exchange,提问作者user763539
相关产品推荐
相关产品推荐

