SQL INSERT:指定了重复的键值?

时间:2014-02-25 16:51:16

标签: c# sql .net ibm-midrange

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

  1. SSN 9,0(来自123456789)NON-NULL
  2. 日期8,0(例如20131202)NON-NULL
  3. TIME 6,0(ex.133000)NON-NULL
  4. PRINT_NEW Z(ex.2013-12-02-11.23.47.965000)(使用CURRENT_TIMESTAMP)NON-NULL
  5. PRINT_OLD Z(例如2013-12-02-11.23.47.965000)(使用CURRENT_TIMESTAMP)NULLABLE
  6. 在我的应用程序中,在处理过程中,我提示用户是否希望将处理过的文档提交到图像文件服务器。如果单击“是”,则会将所有已处理的文档从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();
                }
    

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

        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将使其进入跟踪表。每次,在处理的某个时刻,他们都会收到以下错误(AFTER文档成功移动到图像服务器处理文件夹):

    Duplicate Key Error

    我可以说,这源于我的INSERT语句。然而,我无法弄清楚的是,为什么有几条记录成功进入表中,然后突然之间会出现这种错误。更令人沮丧的是,我似乎无法在我自己的机器上复制错误(即使清除跟踪表并处理与我的用户完全相同的记录)。

    有没有其他人有此错误的经验?有什么想法来解决这个问题?

    SSN是已处理的SSN,DATE是从我的应用程序datepicker控件中选择的日期,TIME是当前系统时间,print new是当前日期时间。这四个字段都是NON-NULL,应该共同创建一个完全唯一的值。

2 个答案:

答案 0 :(得分:3)

此代码存在很多问题

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

为什么要打开连接?

你可以使用一些错误捕获
您将知道错误,并且应该能够获得更多细节

您不应该使用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
{ } 

答案 1 :(得分:2)

这是SQL问题的一个更清晰的消息。您不能添加具有与现有行相同的SSN的新行。现有的行可能已存在10年,或者它可能是您刚刚在抛出异常的行之前添加的行。

switch()可能会让您陷入困惑的路径。无论某些外部进程声明“旧”或“新”地址,此代码都需要更新现有行并添加不存在的行。想象一下这种情况。外部进程看到SSN 123进来。它检查文件,123不在那里。因此它将交易标记为“新”。然后,5个文档之后,处理SSN 123的另一个传入文档。外部进程检查文件,123不在那里,因此它也将此事务标记为“新”。当你到处调用updateAddrChngHistory时,SSN 123的第一个实例被INSERT了很好,但是后来有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)