【问题标题】:SQL INSERT: Duplicate Key Value Specified?SQL INSERT:指定了重复的键值?
【发布时间】:2014-02-26 04:28:53
【问题描述】:

我在 LIBRARY1.TRACKINGTABLE 的 AS400 系统上有一个跟踪表。此表有 5 个字段:

  1. SSN 9,0(例如 123456789)非空
  2. DATE 8,0(例如 20131202)非空
  3. TIME 6,0(例如 133000)非空
  4. PRINT_NEW Z(例如 2013-12-02-11.23.47.965000)(使用 CURRENT_TIMESTAMP)非空
  5. PRINT_OLD Z(例如 2013-12-02-11.23.47.965000)(使用 CURRENT_TIMESTAMP)可空

在我的应用程序中,在处理过程中,我会提示用户是否希望将已处理的文档提交到图像文件服务器。如果在对话框中单击“是”,则所有已处理的文档都会从其在 C: 中的本地文件夹移动到共享网络文件夹。

然后我有一个包含在已处理文档中的所有 SSN 的数组。对于数组中的每个 SSN,我调用一个名为 updateAddrChangHistory(ssn) 的函数来更新我的跟踪表:

            // PROMPT USER TO COMMIT
            DialogResult dResult = MessageBox.Show("Mail Merge Completed. Individual Documents Saved to C:\\TEMP\\to fyi\\. \n\n Commit generated documents to NetFYI?", "Confirm Commit", MessageBoxButtons.YesNo);
            if (dResult == DialogResult.Yes)
            {
                moveLocalToCommitFYI();

                // If selected letter is New/Old Address Letter, and thus tracked in table LIBRARY.TRACKINGTABLE,
                // update the tracking table using SSN's stored in SSNsArray.
                if (cmbLetterType.SelectedIndex < 2)
                {
                    // Now that user has "theoretically" printed and Commited the processed documents, update Change History Table
                    foreach (string ssn in SSNsArray)
                    {
                        if (ssn != null && ssn.Length > 0)
                        {
                            updateAddrChngHistory(ssn);
                        }
                    }
                }
                SSNsArray = new string[0]; // Clear the array for next processing
                updatePrintedCntLabel();
            }

然后根据处理的文档类型,我要么插入新记录(表示已打印新地址字母)或更新记录(表示已为同一成员打印旧地址字母)。

    public void updateAddrChngHistory(string SSN)
    {
        // Update/Modify Tracking Table
        string formattedDate = dtpDate.Value.ToString("yyyyMMdd");
        //string formattedTime = DateTime.Now.TimeOfDay.ToString("HHmmss");
        string query = "";
        switch (docType)
        {
            case "oldAddr":
                query = "UPDATE LIBRARY.TRACKINGTABLE SET PRINT_OLD = CURRENT_TIMESTAMP WHERE SSN = " + SSN + " AND PRINT_OLD IS NULL";
                break;
            case "newAddr":
                query = "INSERT INTO LIBRARY.TRACKINGTABLE (SSN, DATE, TIME, PRINT_NEW, PRINT_OLD) VALUES (" + SSN + ", " + formattedDate + ", " + System.DateTime.Now.ToString("HHmmss") + ", CURRENT_TIMESTAMP, NULL)";
                break;
            case "nameChg":

                break;

             case "nameChgWAR":

                break;
        }

        mdl.InsertUpdateData(query);
        // Close Connection
        mdl.closeConn();
    }

mdl.InsertUpdateDate()是类文件函数:

namespace MergeDoc
{
    public class MergeDocClassLibrary
    {
        OdbcConnection conn = new OdbcConnection();

        public void InsertUpdateData(string query)
        {
            // PROD
            string connString = "DRIVER=Client Access ODBC Driver (32-bit); SYSTEM=XX.XX.X.XX; UID=XYXYXYZ; PWD=YXZYXZY";
            // DEV
            //string connString = ""

            OdbcCommand cmd = new OdbcCommand(query, conn);

            // Set connection using connectionString
            conn.ConnectionString = connString;
            // Open Connection
            conn.Open();
            // Execute command and store in OBDC DataReader
            cmd.ExecuteReader();
        }
    }
}

我的问题如下

我的最终用户在虚拟机上。当他们运行应用程序时,假设处理 97 条新的地址信记录,其中 72 条将进入跟踪表。每次,在处理的某个时刻,他们都会收到以下错误(文档成功移动到图像服务器处理文件夹之后):

据我所知,这源于我的 INSERT 语句。但是,我无法弄清楚的是,为什么有几条记录成功地进入了表中,然后突然之间发生了一些事情导致了这个错误。更令人沮丧的是,我似乎无法在我自己的机器上复制错误(即使在清除跟踪表并处理与我的用户完全相同的记录时)。

有没有其他人遇到过这个错误?有关如何解决此问题的任何想法?

SSN 是处理后的 SSN,DATE 是从我的应用程序日期选择器控件中选择的日期,TIME 是当前系统时间,print new 是当前日期时间。这四个字段都是非空的,应该一起创建一个完全唯一的值。

【问题讨论】:

  • SSN 你是主键吗?
  • 我正在尝试检查。数据库管理员创建了该表,我不熟悉如何检查 AS400 表。我在 VS2012 中设置了连接。

标签: c# sql .net ibm-midrange


【解决方案1】:

这段代码有很多问题

应该在打开之前测试打开的连接。

为什么要保持连接打开?

您可以使用一些错误捕获
您将知道错误存在并且应该能够获得更多详细信息

您不应该使用 ExecuteReader() 进行更新或插入
更新或插入应该是 ExecuteNonQuery()

根据更新和插入语句,SSN 很可能是 PK
如果 docType 是 newAddr 而不检查 SSN 是否存在,您似乎正在执行插入。

if(string.IsNullOrEmpty(query)) return;
try 
{
    OdbcCommand cmd = new OdbcCommand(query, conn);
    conn.ConnectionString = connString;
    conn.Open();
    cmd.ExecuteReader();
}
catch (OdbcException Ex) 
{
    Debug.WriteLine(Ex.Message);
    throw Ex;
}
finally
{ } 

【讨论】:

  • 我在大部分代码中都进行了一些错误检查。连接实际上是通过对同一类的不同过程调用而关闭的。抱歉,我试图只包含问题的相关代码。
  • 在不同的过程中打开和关闭不是一个好的代码实践。至少应该测试打开的连接。
  • “据我所知,这源于我的 INSERT 语句。”这就是你在那里测试异常的原因。
【解决方案2】:

这是更清晰的 SQL 问题消息之一。您不能添加与现有行具有相同 SSN 的新行。现有行可能已经存在了 10 年,也可能是您刚刚添加的行之前引发异常的行。

switch() 可能会让你走上一条令人困惑的道路。无论某些外部进程声明“旧”或“新”地址,此代码都需要更新现有行并添加不存在的行。想象一下这种情况。外部进程看到 SSN 123 进入。它检查文件并且 123 不在其中。因此它将交易标记为“新”。然后,在 5 个文档之后,处理 SSN 123 的另一个传入文档。外部进程检查文件,123 不在其中,因此它也将此事务标记为“新”。当您开始调用 updateAddrChngHistory 时,SSN 123 的第一个实例可以正常插入,但在 5 个文档之后,SSN 123 的下一个实例是重复的,并且 INSERT 失败。这只是发生这种情况的一种方式。

如果您不想捕获重复键异常,则需要在 SQL 中对其进行测试:

INSERT INTO LIBRARY.TRACKINGTABLE 
  (SSN, DATE, TIME, PRINT_NEW, PRINT_OLD) 
  VALUES (" + SSN + ", " + formattedDate + ", " + 
         System.DateTime.Now.ToString("HHmmss") + ", CURRENT_TIMESTAMP, NULL)
  where ssn not in (select ssn from library.trackingtable)

【讨论】:

  • 感谢巴克的简明解释。经过进一步审查,看起来 SSN、日期和时间都是表中的关键字段。另一个问题是我插入了系统日期和系统时间,当我应该插入存储在更改日志中的日期/时间时,我查询查询中返回的每条记录以处理要处理的文档。我相信 Datec/Timec 的不匹配导致了我遇到的几个问题。如果这确实是解决方案,将报告!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-20
  • 1970-01-01
  • 2014-04-22
  • 2016-08-08
  • 2021-11-19
  • 1970-01-01
相关资源
最近更新 更多