VFP插入、索引更新

发布于 2024-07-26 10:39:16 字数 508 浏览 6 评论 0原文

所以主程序是用C#编写的。 将新记录插入 VFP 数据库表中。 通过 生成记录的下一个 ID 花费的时间太长

select max(id)+1 from table

,因此我将该代码放入 VFP 中的编译 dll 中,并通过 C# 调用该 COM 对象。

COM 对象在大约 250 毫秒内返回新 ID。 然后我通过 OLEDB 进行更新。 我遇到的问题是,在 COM 对象返回新插入的 ID 后,我无法立即通过 OLEDB

select id form  table where id = *newlyReturnedID*

返回 0 行从 C# 找到它。 如果我等待未知的时间段,查询将返回 1 行。 我只能假设它立即返回 0 行,因为它尚未将新创建的 ID 添加到索引中,因此 select 无法找到它。

还有其他人遇到过类似的事情吗? 如果是这样,你是如何处理的?

DD

So the main program is in C#. Inserting new records into a VFP database table. It was taking too long to generate the next ID for the record via

select max(id)+1 from table

, so I put that code into a compile dll in VFP and am calling that COM object through C#.

The COM object returns the new ID in about 250ms. I then just do an update through OLEDB. The problem I am having is that after the COM object returns the newly inserted ID, I cannot immediately find it from C# via the OLEDB

select id form  table where id = *newlyReturnedID*

returns 0 rows back. If I wait an unknown time period the query will return 1 row. I can only assume it returns 0 rows immediately because it has yet to add the newly minted ID into the index and therefore the select cannot find it.

Has anyone else ever run into something similar? If so, how did you handle it?

DD

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

遇见了你 2024-08-02 10:39:16

警告:您的代码在多用户环境中存在缺陷。 两个人可以同时运行查询并获得相同的 ID。 如果列具有主键或候选键,其中之一将在 INSERT 上失败,这是键字段的最佳实践。

我的建议是让 ID 成为自动递增的整数字段(我不喜欢它们),或者更好的是创建一个键表。 表中的每条记录都对应一个分配键的表。 我使用与此类似的结构:

       Structure for: countergenerator.dbf
       Database Name: conferencereg.dbc
     Long table name: countergenerator
   Number of records: 0
        Last updated: 11/08/2008
Memo file block size: 64
           Code Page: 1252
          Table Type: Visual FoxPro Table

Field  Name                  Type                 Size   Nulls       Next       Step  Default  
----------------------------------------------------------------------------------------------------------------
    1  ccountergenerator_pk  Character            36         N                        guid(36)  
    2  ckey                  Character (Binary)   50         Y                          
    3  ivalue                Integer               4         Y                          
    4  mnote                 Memo                  4         Y                        "Automatically created"  
    5  cuserid               Character            30         Y                        
    6  tupdated              DateTime              8         Y                        DATETIME()  

Index Tags: 
1. Tag Name: PRIMARY
 - Type: primary
 - Key Expression: ccountergenerator_pk
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

2. Tag Name: CKEY
 - Type: regular
 - Key Expression: lower(ckey)
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

现在 DBC(或另一个程序)中存储过程的代码是这样的:

函数 NextCounter(tcAlias)

LOCAL lcAlias, ;
lnNextValue, ;
lnOldReprocess,;
lnOldArea

lnOldArea = SELECT()

IF PARAMETERS() < 1
lcAlias = ALIAS()

IF CURSORGETPROP("SOURCETYPE") = DB_SRCLOCALVIEW
*-- 尝试获取基表
lcAlias = LOWER(CURSORGETPROP("表"))
lcAlias = SUBSTR(lcAlias, AT("!", lcAlias) + 1)
万一
别的
lcAlias = LOWER(tcAlias)
ENDIF

lnOrderNumber = 0
lnOldReprocess = SET('REPROCESS')

*-- 锁定直到用户按 Esc
将重新处理设置为自动

如果 !USED("countergenerator")
在 0 共享别名 countergenerator 中使用 EventManagement!countergenerator
ENDIF

SELECT 计数器生成器

IF SEEK(LOWER(lcAlias), "countergenerator", "ckey")
如果 RLOCK()
lnNextValue = countergenerator.iValue
将 countergenerator.iValue 替换为 countergenerator.iValue + 1
开锁
万一
别的
* 使用起始值创建新记录。
在反生成器中附加空白
分散 Memvar 备忘录
m.cKey = LOWER(lcAlias)
m.i值 = 1
m.mNote =“由存储过程自动创建。”
m.tUpdated = DATETIME()
收集 MEMVAR 备忘录

IF RLOCK()
lnNextValue = countergenerator.iValue
将 countergenerator.iValue 替换为 countergenerator.iValue + 1
开锁
万一
ENDIF

选择(lnOldArea)
将 REPROCESS 设置为 lnOldReprocess

RETURN lnNextValue
ENDFUNC

RLOCK() 确保不存在记录争用,并且速度足够快,不会造成进程瓶颈。 这比您当前采取的方法安全得多。

里克·舒默
VFP MVP

Warning: your code is flawed in a multi-user environment. Two people could run the query at the same time and get the same ID. One of them will fail on the INSERT if the column has a primary or candidate key, which is a best practice for key fields.

My recommendation is to either have the ID be a auto-incrementing integer field (I'm not a fan of them), or even better, create a table of keys. Each record in the table is for a table that gets keys assigned. I use the a structure similar to this:

       Structure for: countergenerator.dbf
       Database Name: conferencereg.dbc
     Long table name: countergenerator
   Number of records: 0
        Last updated: 11/08/2008
Memo file block size: 64
           Code Page: 1252
          Table Type: Visual FoxPro Table

Field  Name                  Type                 Size   Nulls       Next       Step  Default  
----------------------------------------------------------------------------------------------------------------
    1  ccountergenerator_pk  Character            36         N                        guid(36)  
    2  ckey                  Character (Binary)   50         Y                          
    3  ivalue                Integer               4         Y                          
    4  mnote                 Memo                  4         Y                        "Automatically created"  
    5  cuserid               Character            30         Y                        
    6  tupdated              DateTime              8         Y                        DATETIME()  

Index Tags: 
1. Tag Name: PRIMARY
 - Type: primary
 - Key Expression: ccountergenerator_pk
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

2. Tag Name: CKEY
 - Type: regular
 - Key Expression: lower(ckey)
 - Filter: (nothing)
 - Order: ascending
 - Collate Sequence: machine

Now the code for the stored procedure in the DBC (or in another program) is this:

FUNCTION NextCounter(tcAlias)

LOCAL lcAlias, ;
lnNextValue, ;
lnOldReprocess, ;
lnOldArea

lnOldArea = SELECT()

IF PARAMETERS() < 1
lcAlias = ALIAS()

IF CURSORGETPROP("SOURCETYPE") = DB_SRCLOCALVIEW
*-- Attempt to get base table
lcAlias = LOWER(CURSORGETPROP("TABLES"))
lcAlias = SUBSTR(lcAlias, AT("!", lcAlias) + 1)
ENDIF
ELSE
lcAlias = LOWER(tcAlias)
ENDIF

lnOrderNumber = 0
lnOldReprocess = SET('REPROCESS')

*-- Lock until user presses Esc
SET REPROCESS TO AUTOMATIC

IF !USED("countergenerator")
USE EventManagement!countergenerator IN 0 SHARED ALIAS countergenerator
ENDIF

SELECT countergenerator

IF SEEK(LOWER(lcAlias), "countergenerator", "ckey")
IF RLOCK()
lnNextValue = countergenerator.iValue
REPLACE countergenerator.iValue WITH countergenerator.iValue + 1
UNLOCK
ENDIF
ELSE
* Create the new record with the starting value.
APPEND BLANK IN countergenerator
SCATTER MEMVAR MEMO
m.cKey = LOWER(lcAlias)
m.iValue = 1
m.mNote = "Automatically created by stored procedure."
m.tUpdated = DATETIME()
GATHER MEMVAR MEMO

IF RLOCK()
lnNextValue = countergenerator.iValue
REPLACE countergenerator.iValue WITH countergenerator.iValue + 1
UNLOCK
ENDIF
ENDIF

SELECT (lnOldArea)
SET REPROCESS TO lnOldReprocess

RETURN lnNextValue
ENDFUNC

The RLOCK() ensures there is no contention for the records and is fast enough to not have bottleneck the process. This is way safer than the approach you are currently taking.

Rick Schummer
VFP MVP

云胡 2024-08-02 10:39:16

VFP 需要FLUSH 其工作区。

VFP needs to FLUSH its workareas.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文