将数据表自动递增列与数据库同步
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 增量同步
Windows和Linux使用WinSCP脚本(备份)自动数据同步