微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

更新一组记录中的键字段时出现重复键错误

如何解决更新一组记录中的键字段时出现重复键错误

我有两个表之间的主从关系。 Troq 是主表,Troq_Alma 是详细表。 Troq_Alma 主键由 Troq 主键(Troq.cod)和“Num_Orden”组成。这个“Num_Orden”字段不仅是表格PK的一部分,而且是一个Troq的多个Alma中该特定Alma的优先级。 (Troq_Alma 是将不同 Troq 与其相关 Alma 关联起来的表)

并且在某个时刻,用户需要修改此优先级,因此修改一组记录的主键(与一个特定 troq 相关的一组 almas)。在表单中有两个按钮(Spinbuttons)与一个 TDBGrid 相关。如果我按下“向上”按钮,选定的 Troq_Alma 应将“Num_Orden”减 1,“Num_Orden”中具有该值的 Troq_alma 将其值增加 1。因此,在 dbgrid 中的表的“图像”中,没有重复的密钥。

当然,当我应用此更新时:TFDQ_Troq_Alma.ApplyUpdates(-1);

我收到重复键错误,我认为这是非常合乎逻辑的,因为 Firedac 会尝试进行此更新,第一次修改将引发重复键错误,直到对以前具有该主要记录的其他记录进行修改键,因为它仍然具有主键。

我真的不知道这个问题是否有任何“公平”的解决方案,我唯一想到的就是首先为一个特定的 Troq 的所有“num_orden”添加一些数量,然后进行更新,然后再次更新为最初的修改值,这是一项非常奇怪的工作,但是,根据我对 delphi 和 Firedac 的短暂了解,我似乎真的没有找到任何其他方法解决它。

使用 Firedac 使用针对 Postgres 11.8 数据库的缓存更新在 Delphi XE6 上工作。

如果您感兴趣,这里是两个旋转按钮(向上和向下)的代码

procedure TFRM_Mant_TROQ.SpinBut_Troq_AlmaDownClick(Sender: TObject);
var iValActNumOrd,iValNumOrd2 : Integer;
    RegActual: TBookMark;
    iValMax: Integer;
begin
    RegActual := DM_Mant_Troq.FDQ_Troq_Alma.GetBookmark;
    iValMax := DM_DatosComun.MaxVal_FDQ(DM_Mant_Troq.FDQ_Troq_Alma,'num_orden');
    DM_Mant_Troq.FDQ_Troq_Alma.GotoBookmark(RegActual);
    iValActNumOrd := DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value;
    if iValActNumOrd < iValMax  then begin
        DM_Mant_Troq.FDQ_Troq_Alma.Next;
        iValNumOrd2 := DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value;
        DM_Mant_Troq.FDQ_Troq_Alma.Edit;
        DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value := iValActNumOrd;
        // DM_Mant_Troq.FDQ_Troq_Alma.Post;
        DM_Mant_Troq.FDQ_Troq_Alma.GotoBookmark(RegActual);
        DM_Mant_Troq.FDQ_Troq_Alma.Edit;
        DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value := iValNumOrd2;
        DM_Mant_Troq.FDQ_Troq_Alma.Post;
    end;
end;

procedure TFRM_Mant_TROQ.SpinBut_Troq_AlmaUpClick(Sender: TObject);
var iValActNumOrd,iValNumOrd2 : Integer;
    RegActual: TBookMark;
    iValMin: Integer;

begin
  RegActual := DM_Mant_Troq.FDQ_Troq_Alma.GetBookmark;
  iValMin := DM_DatosComun.MinVal_FDQ(DM_Mant_Troq.FDQ_Troq_Alma,'num_orden');
  DM_Mant_Troq.FDQ_Troq_Alma.GotoBookmark(RegActual);
  iValActNumOrd := DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value;
  if iValActNumOrd > iValMin then begin

      DM_Mant_Troq.FDQ_Troq_Alma.Prior;
      iValNumOrd2 := DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value;
      DM_Mant_Troq.FDQ_Troq_Alma.Edit;
      DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value := iValActNumOrd;
      DM_Mant_Troq.FDQ_Troq_Alma.GotoBookmark(RegActual);
      DM_Mant_Troq.FDQ_Troq_Alma.Edit;
      DM_Mant_Troq.FDQ_Troq_Alma.FieldByName('NUM_ORDEN').Value := iValNumOrd2;
      DM_Mant_Troq.FDQ_Troq_Alma.Post;
  end;
end;

这段代码可能可以改进,但正如我所提到的,我相信这段代码工作正常,我的观点是如何指示 firedac 以“最终避免”这种唯一的密钥验证。

实际上 Troq.Cod、Num_Orden 不是表 Troq_Alma 的主键,但是这两个字段上有一个唯一索引,我真的很喜欢这个唯一索引以实现数据库完整性。

解决方法

如果我们接受您有充分的理由更改唯一密钥,那么您需要确保避免密钥违规。看起来您正在处理本地缓存在 TFDQuery 中的数据,然后应用更新。

这种方法的问题在于 TFDQuery 会记住字段的加载值和当前值,因此您的中间更改会丢失,从而导致密钥冲突。

避免该问题的一种快速方法是在每次更改后执行 ApplyUpdate。如果您远离主店,这对您来说可能是个问题。

如果您需要为索引字段分配一个临时值,您需要确保该值是唯一的。如果数据模式允许,您可以否定 Integer 字段的值(如果它未定义为无符号)。尽管可能还有其他记录具有该值,但如果您的惯例是仅临时分配这些值,则应避免密钥冲突。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。