将数据表自动递增列与数据库同步

Posted

技术标签:

【中文标题】将数据表自动递增列与数据库同步【英文标题】:Syncing DataTable autoincremented columns with Database 【发布时间】:2012-01-15 10:49:34 【问题描述】:

尝试在我的数据库上使用 Update() 方法时会出现 DBConcurrencyException 问题。我在数据库中有一个表,它有一个自动递增的 ID 列,我的 c# 程序中有一个 DataTable 从这个表中获取它的信息(包括我使用 MissingSchemaAction = MissingSchemaAction.AddWithKey 时的自动递增部分)。

如果我创建行并将它们添加到数据表中,数据表会自动为我填充自动递增的 ID 列(从数据库表停止的位置开始),这很好。但是,如果我删除刚刚添加的行(不首先使用 Update() )并添加新行,则数据表自动增量列将根据 DATATABLE 的位置而不是数据库的位置填充一个值,这就是为什么我得到并发错误。

例如:

数据库中的表有这些记录:

1 Apple
2 Orange
3 Pear

它被复制到数据表中,所以当我添加一个名为“grape”的新行时,我得到:

1 Apple
2 Orange
3 Pear
4 Grape

这很好,但是如果不运行 Update() 方法,我会删除葡萄行并添加一个新行“Melon”,我会得到:

1 Apple
2 Orange
3 Pear
5 Melon

当我尝试运行 Update() 时,数据库期望 4 是下一个自动增量值,但实际上是 5。所以我得到了错误。当用户单击“保存”按钮时会发生 Update(),因此理想情况下,我希望他们能够在最终保存之前进行如上所示的大量更改,但这是保持并发性以使用 Update( ) 每行添加/删除后?

【问题讨论】:

一个mysql数据库,抱歉我没提,Update()是DataAdapter的一个方法,用来从数据库中获取信息。 【参考方案1】:

我的第一个想法是,您应该只处理正在删除一行的情况并首先更新它,然后删除它以保持自动增量 ID 同步。

但是,我遇到了同样的情况,这似乎是由托管我的 DataGridView 的第 3 方控件引起的。具体来说,当用户将焦点放在 DataGridView 的“新”行时,就会出现问题,切换到另一个应用程序,然后单击返回 DataGridView。此时,新行的原始 DataRow 实例被删除,并使用递增的 ID 值创建一个新实例。在实际删除该行之前,我无法找到一种方法来处理该行的删除,我也无法弄清楚第 3 方控件正在做什么来触发此操作。

因此,目前我正在以非常严厉的方式处理此问题,通过从数据库中查询正确的自动增量值,并在必要时更正新的 DataRows。如果一切都失败了,这个解决方案似乎有效。 (注意我使用的是 SqlCe 而不是 MySQL)

void OnLoad()

    base.OnLoad(e);
    ...
    _dataTable.TableNewRow += HandleTableNewRow;


void HandleTableNewRow(object sender, DataTableNewRowEventArgs e)

    SetAutoIncrementValues(e.Row);


void SetAutoIncrementValues(DataRow row)

    foreach (DataColumn dataColumn in _dataTable.Columns
        .OfType<DataColumn>()
        .Where(column => column.AutoIncrement))
    
        using (SqlCeCommand sqlcmd = new SqlCeCommand(
            "SELECT AUTOINC_NEXT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" +
            Name + "' AND COLUMN_NAME = '" + dataColumn.ColumnName + "'", _connection))
        using (SqlCeResultSet queryResult =
            sqlcmd.ExecuteResultSet(ResultSetOptions.Scrollable))
        
            if (queryResult.ReadFirst())
            
                var nextValue = Convert.ChangeType(queryResult.GetValue(0), dataColumn.DataType);

                if (!nextValue.Equals(row[dataColumn.Ordinal]))
                
                    // Since an auto-increment column is going to be read-only, apply
                    // the new auto-increment value via a separate array variable.
                    object[] rowData = row.ItemArray;
                    rowData[dataColumn.Ordinal] = nextValue;
                    row.ItemArray = rowData;
                
            
        
    

【讨论】:

【参考方案2】:

预期值为 5 - 每次执行某项操作时,数据库都尝试填充列中的漏洞,这将是非常低效的。一旦使用了 auto_increment,它就永远消失了。

因此,请始终确保您的列足够大以容纳所有记录。例如,如果您使用 TINYINT,那么您的表中只能有 127 条记录。

自动增量存储在表级别,Mysql 从不回头看它是否可以更低。您可以通过执行以下操作手动更改它:

ALTER TABLE tablename AUTO_INCREMENT=2;

但是,如果你这样做并且在路上发生碰撞 - 坏事将会发生。

或者你可以检查它是什么

SHOW CREATE TABLE tablename;
CREATE TABLE `tablename` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cat_id` int(10) unsigned NOT NULL,
`status` int(10) unsigned NOT NULL,
`date_added` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `categories_list_INX` (`cat_id`,`status`),
KEY `cat_list_INX` (`date_added`,`cat_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

然后你会发现最后一个是什么。

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)

【讨论】:

对不起,我在这里还没有说清楚,问题是我在我的 C# 程序(DataTable)中使用了数据库表的内存版本,并且 DataTable 自动增量列得到与数据库自动增量列不同步。

以上是关于将数据表自动递增列与数据库同步的主要内容,如果未能解决你的问题,请参考以下文章

Kafka Connect JDBC Source MySQL 增量同步

Redis 主从同步配置

Rsync远程同步数据备份

Windows和Linux使用WinSCP脚本(备份)自动数据同步

如何使用Microsoft Azure Sql数据库自动同步本地SQl数据库?

mysql数据库的基本介绍与操作(第三篇)