【问题标题】:Non auto increment primary keys in a legacy Visual Fox Pro application旧版 Visual Fox Pro 应用程序中的非自动递增主键
【发布时间】:2013-06-19 15:18:39
【问题描述】:

首先让我说我根本无法修改数据库架构。

我无权访问与数据库交互的任何现有代码。它是一个已编译的程序集,我无法反编译。

我有一个数据库表,其值在代码中逻辑上用作主键,但不会自动递增。

现在是转折点,有时键增加正数,有时增加负数。 :(

找不到任何 hilo 代码的证据。

除了执行 SELECT TOP 1、添加值并希望不会发生重叠之外,我不知道如何处理这个问题。

【问题讨论】:

    标签: asp-classic visual-foxpro dbase


    【解决方案1】:

    如果您的表是数据库容器 (*.DBC) 的一部分而不是空闲表,那么您可能会找到代码来自动增加数据库容器中的键。

    如果您打开表格并使用 MODIFY STRUCTURE 命令,您可以在第三个选项卡上看到表格的属性。如果表是数据库容器的成员,则将显示数据库的名称以及任何规则,包括触发器和记录规则,这些规则在每次写入表时执行。

    在添加自动递增字段之前,我通过添加如下记录规则来实现自动递增字段:

    PROCEDURE TABLE_RR
        LOCAL nOldArea,nUnique
    
        IF PK=0     &&Only update primary key field when not already set   
            STORE SELECT() TO nOldArea
    
            &&Open table again in another work area, we can't move the record pointer in a Rule
            IF !USED("TABLE_RR")
                USE TABLE.DBF IN 0 NOUPDATE AGAIN ALIAS TABLE_RR
            ENDIF
    
            &&Find current maximum value of primary key field, add 1 as next primary key field
            SELECT TABLE_RR
            SET ORDER TO PKTable
            GO BOTT
            STORE 1+PK TO nUnique
    
            &&Update primary key field for our record
            SELECT(nOldArea)
            REPLACE PK WITH nUnique
    
         ENDIF
    
         &&Record rule must return true or changes are rejected
         RETURN .T.
    
    ENDPROC
    

    将这样的规则存储在数据库容器存储过程代码区是很正常的习惯和做法,但是,只要正在运行的应用程序能够找到该函数,它就会执行。显然,上面的代码示例假定表名为 TABLE,有一个名为 PK 的数字(最好是 Int)字段,索引标签为 PKTable。

    因为代码在记录规则中,它在任何更新期间都会在记录上执行,因此在记录被锁定时,不会发生冲突。 PK 中没有值的任何记录都会得到 1 + 该字段的当前最高值。记录规则旨在验证记录并拒绝无效值,并且必须返回 true (.T.) 才能接受写入。过程中可能会出现其他代码,包括将当前值设置为时间戳字段等。

    记录规则可以通过ALTER TABLE命令应用于表:

    ALTER TABLE 'TABLENAME' SET CHECK PROCEDURENAME()
    

    或通过 MODIFY STRUCTURE 中的交互式表格编辑器

    【讨论】:

    • 感谢您的完整回答:“首先让我说我根本无法修改数据库架构。”
    【解决方案2】:

    不知道你需要什么,但是如果你需要额外的PK,你可以使用记录号:

    SELECT RECNO() FROM table1
    

    【讨论】:

    • 我将如何插入该值?
    • "首先让我说我根本无法修改数据库架构。" :'(
    【解决方案3】:

    虽然 Steve 的解决方案非常好,尤其是利用数据库容器和分配规则,但您显然(有点)受困于您的情况。如果您尝试添加记录并需要创建自己的自动增量,您可能必须通过过程调用并分配它来完成。这只是一个示例,但可能会对您有所帮助。

    FUNCTION AddMyNewRecord()
       */ localized variable so you don't accidentally mangle some otherwise 
       */ coincidental variable somewhere else.  Preserve work area at start
       local lnSelect, llSaveOk, lnAttempts, lnNewPKID
       lnSelect = select()
       llSaveOk = .f.    
    
       select YourAddressTable
       if NOT FLOCK()
          lnAttempts = 0
          do while lnAttempts < 10 AND NOT FLOCK()
             wait window "Attempting lock for address table..." timeout 1
          enddo
       endif 
    
       if NOT FLOCK()
          messagebox( "Unable to lock file to add next record primary key." )
       else
          */ We have a lock, now try to detect both the highest and lowest
          */ EXISTING key for the table
          use in select( "C_TryNextKey" )
          select MIN( YAT.PK ) as MinKey,;
                 MAX( YAT.PK ) as MaxKey ;
             from ;
                YourAddressTable YAT;
             into ;
                cursor C_TryNextKey readwrite 
    
          */ Determine if you need higher or lower key sequence
          if CriteriaForPositiveKey
             lnNewPKID = C_TryNextKey.MaxKey +1
          else
             */ must be getting next LOWER sequence
             lnNewPKID = C_TryNextKey.MinKey -1
          endif 
    
          */ close temp cursor from getting respective high / low key
          use in select( "C_TryNextKey" )
    
          */ NOW, we can add the new record
          Select YourAddressTable
          APPEND BLANK
          */ replace PK and all the other fields you need to do too
          Replace PK with lnNewPKID,;
                  Address1 with lcSomeVariableYouHaveForAddress1,;
                  Address2 with lcSomeVariableForAddress2,;
                  AnyOtherFields with lcOtherFieldsToBeSet
    
          */ Unlock the address table
          UNLOCK
    
          */ set flag so we know it worked vs not.
          llSaveOk = .t.
       endif  
    
       */ Return back where it started
       select( lnSelect )
    
       */ Return if the save was ok or not.
       return llSaveOk
    endproc 
    

    这不会修改现有的数据库,也不会修改表结构。但是,它会在插入新记录内容之前尝试锁定表。然后查看最低和最高 ID。您将不得不分析数据以找出为什么有些人有负面与正面,并更新我在上面的代码中放置的“占位符”条件。最后,解锁桌子。

    如果这有意义,那太好了,如果您需要调整,请告诉我,我会尽力为您提供更多指导。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-18
      • 2011-09-02
      • 1970-01-01
      • 2014-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多