如何有效避免重复的密钥异常

时间:2014-10-21 15:20:02

标签: c# sql-server database exception

我遇到了以下问题:

我正在尝试将sql-server中的表与多个外部数据库保持同步。 这些外部数据库没有共享的唯一主键,因此本地表具有简单的整数PK。

现在让本地表保持最新状态:

  1. 查询外部数据库。
  2. 将数据转换为本地表的有效数据。
  3. 使用insert来尝试将数据写入本地表。
  4. 如果insert返回重复的条目异常,则将通过选择查询搜索PK,并且数据将通过更新查询写入表。
  5. 使用插入或更新的行的PK修改另一个表。
  6. 现在这种方法很好但对我而言似乎效率很低。大多数情况下,数据已经在本地表中,并导致插入上出现重复的键异常。这意味着需要处理许多异常,这很昂贵。此外,由于数据库管理PK,必须使用选择查询来查找要更新的行。

    我该如何避免这种影响?我不想使用存储过程,因为我喜欢通过代码保持查询可管理并包含在版本控制中。

    我看过合并,但我看到太多人报告了它的问题。

    我认为我需要使用一种形式的upsert,但我不知道如何通过数据库管理PK来实现这一点。

    tl; dr:我需要的是一个查询,它允许我插入或更新一行(取决于是否重复),它总是返回行的PK。

1 个答案:

答案 0 :(得分:2)

我有一个我过去做过的实现,我喜欢。您可能会或可能不会觉得它有用。

这就是它的工作方式......我使用一个适用于两者的模型对象将外部和本地数据加载到内存中。例如......

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }
    public string Address { get; set; }

    // This comparer will be used to find records that exist or don't exist.
    public class KeyFieldComparer : IEqualityComparer<Person>
    {
        public bool Equals(Person p1, Person p2)
        {
            return p1.FirstName == p2.FirstName && p1.LastName == p2.LastName;
        }

        public int GetHashCode(Person p)
        {
            return p.FirstName.GetHashCode() ^ p.LastName.GetHashCode();
        }
    }

    // This comparer will be used to find records that are outdated and need to be updated.
    public class OutdatedComparer : IEqualityComparer<Person>
    {
        public bool Equals(Person p1, Person p2)
        {
            return p1.FirstName == p2.FirstName && p1.LastName == p2.LastName && (p1.PhoneNumber != p2.PhoneNumber || p1.Address != p2.Address);
        }

        public int GetHashCode(Person p)
        {
            return p.FirstName.GetHashCode() ^ p.LastName.GetHashCode();
        }
    }
}

我们需要有一些方法来唯一地识别我认为你拥有的记录。在这个例子中,它是FirstNameLastName(我知道这不是很独特,但为了简单起见,我们假装它运作良好)。当列表加载到内存中时,IEqualityComparer<>将完成查找过时记录和新记录的工作。

现在我们只是简单地将现有的过时记录和全新的记录分开......

List<Person> local = loadLocalRecords();
List<Person> external = loadExternalRecords();

var newRecordsToInsert = external.Except(local, new Person.KeyFieldComparer());

var outdatedRecordsToUpdate = local.Intersect(external, new Person.OutdatedComparer());

我希望这是有道理的。如果你有问题,我可以回答问题。这种方法的好处在于它能够以最少的数据库命中率完成工作(我认为)。不好的是它必须将所有内容加载到内存中,这对您来说可能并不实用。但是你的桌子大小必须很大才能成为一个问题。超过几百万条记录,具体取决于列数。