You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何解决非主键字段触发MySql Duplicate field异常

解决MySQL插入非主键字段报Duplicate Field错误的方案

兄弟,这个问题我太熟了!咱们一步步拆解原因,然后给你搞定:

核心原因分析

你遇到的Duplicate field错误,本质不是代码层面的问题——大概率是你的数据库daily_production表的Date字段被设置了「唯一约束(UNIQUE KEY)」。哪怕它不是主键,只要有唯一约束,数据库就不允许插入重复的Date值,这才是报错的根源。

另外你的DataGridView重复检查逻辑也有两个小漏洞:

  1. 找到重复时没把bolDuplicateWasFound设为True,逻辑不严谨(不过你用了Exit Sub,暂时没影响,但还是得修正);
  2. 双重循环会重复检查同一对行,效率太低,而且没考虑数据库里已经存在的旧数据——你只检查了DataGridView内部的重复,没检查数据库里的现有记录,这才是最容易触发报错的点!

具体解决步骤

1. 先确认数据库的唯一约束

登录MySQL,执行这条命令查看表结构:

SHOW CREATE TABLE daily_production;

看输出里有没有类似UNIQUE KEY Date (Date)的语句:

  • 如果有:说明这个字段确实有唯一约束。如果业务上允许重复Date,就删除约束:
    ALTER TABLE daily_production DROP INDEX Date;
    
    如果业务要求Date必须唯一,那咱们就继续往下优化代码,确保插入的Date在数据库中是唯一的。

2. 修复DataGridView内部的重复检查逻辑

把你原来的双重循环改成用HashSet做单次遍历,效率更高,逻辑更清晰:

Dim bolDuplicateWasFound As Boolean = False
Dim existingDates As New HashSet(Of String)()

For x As Integer = 0 To DataGridView1.Rows.Count - 1
    ' 跳过DataGridView的空行(如果允许新增行的话)
    If DataGridView1.Rows(x).IsNewRow Then Continue For
    
    Dim currentDate As String = DataGridView1.Rows(x).Cells("Date").Value.ToString()
    If existingDates.Contains(currentDate) Then
        MessageBox.Show("Duplicate Found: " & currentDate)
        bolDuplicateWasFound = True
        Exit For
    Else
        existingDates.Add(currentDate)
    End If
Next

If bolDuplicateWasFound Then
    Exit Sub
End If

3. 添加数据库现有数据的重复检查

只检查DataGridView内部还不够,必须确认要插入的Date没在数据库里存在过。这里用批量查询的方式,效率更高:

' 先收集所有要插入的Date
Dim datesToInsert As New List(Of String)()
For x As Integer = 0 To DataGridView1.Rows.Count - 1
    If DataGridView1.Rows(x).IsNewRow Then Continue For
    datesToInsert.Add(DataGridView1.Rows(x).Cells("Date").Value.ToString())
Next

' 检查数据库中是否存在这些Date
If datesToInsert.Count > 0 Then
    Dim checkCommand As New MySqlCommand()
    checkCommand.Connection = db.getConnection
    ' 构造参数化查询(如果Date是VARCHAR类型),避免SQL注入
    Dim paramPlaceholders As String = String.Join(",", Enumerable.Repeat("@date", datesToInsert.Count))
    checkCommand.CommandText = $"SELECT Date FROM daily_production WHERE Date IN ({paramPlaceholders})"
    
    ' 添加参数
    For i As Integer = 0 To datesToInsert.Count - 1
        checkCommand.Parameters.Add($"@date{i}", MySqlDbType.VarChar).Value = datesToInsert(i)
    Next
    
    Try
        db.getConnection.Open()
        Dim reader As MySqlDataReader = checkCommand.ExecuteReader()
        While reader.Read()
            Dim duplicateDate As String = reader("Date").ToString()
            MessageBox.Show("Date already exists in database: " & duplicateDate)
            bolDuplicateWasFound = True
        End While
        reader.Close()
    Finally
        If db.getConnection.State = ConnectionState.Open Then
            db.getConnection.Close()
        End If
    End Try
    
    If bolDuplicateWasFound Then
        Exit Sub
    End If
End If

注意:如果你的Date字段是MySQL的DATE类型,记得把MySqlDbType.VarChar改成MySqlDbType.Date,避免字符串转换问题。

4. 插入时添加异常捕获(兜底方案)

哪怕做了前面的检查,还是可能因为并发操作(比如别人同时插入了相同的Date)导致报错,所以必须加异常处理:

Try
    db.getConnection.Open()
    Dim command As MySqlCommand
    For i As Integer = 0 To DataGridView1.Rows.Count - 1
        If DataGridView1.Rows(i).IsNewRow Then Continue For
        
        command = New MySqlCommand("INSERT INTO `daily_production`(`Year`, `Month`, `Date`, `White`, `Yellow`) VALUES (@yea,@mon,@dat,@whit,@yel)", db.getConnection)
        command.Parameters.Add("@yea", MySqlDbType.VarChar).Value = DataGridView1.Rows(i).Cells(0).Value.ToString()
        command.Parameters.Add("@mon", MySqlDbType.VarChar).Value = DataGridView1.Rows(i).Cells(1).Value.ToString()
        command.Parameters.Add("@dat", MySqlDbType.VarChar).Value = DataGridView1.Rows(i).Cells(2).Value.ToString()
        command.Parameters.Add("@whit", MySqlDbType.VarChar).Value = DataGridView1.Rows(i).Cells(3).Value.ToString()
        command.Parameters.Add("@yel", MySqlDbType.VarChar).Value = DataGridView1.Rows(i).Cells(4).Value.ToString()
        
        command.ExecuteNonQuery()
    Next
    MessageBox.Show("Data Inserted Successfully", "TDP Thermoline", MessageBoxButtons.OK, MessageBoxIcon.Information)
Catch ex As MySqlException
    If ex.Number = 1062 Then ' MySQL重复键错误的代码
        MessageBox.Show("Duplicate Date found in database. Please check your data.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    Else
        MessageBox.Show("Insert failed: " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If
Finally
    ' 确保连接关闭,不要随便Dispose(除非你确定以后不再用这个连接对象)
    If db.getConnection.State = ConnectionState.Open Then
        db.getConnection.Close()
    End If
End Try

最后提醒

  • 不要随便调用db.getConnection.Dispose(),如果你的连接是从连接池获取的,Close()就足够了,Dispose()会直接销毁连接,影响连接池的复用。
  • 如果YearMonth可以通过Date字段计算出来,建议不要单独存储,避免数据不一致的问题。

内容的提问来源于stack exchange,提问作者Madu

火山引擎 最新活动