【问题标题】:SQL Server - How to insert a record and make sure it is uniqueSQL Server - 如何插入记录并确保它是唯一的
【发布时间】:2008-11-06 06:43:43
【问题描述】:

我试图找出将记录插入单个表的最佳方法,但前提是该项目尚不存在。在这种情况下,KEY 是一个 NVARCHAR(400) 字段。对于这个例子,让我们假设它是牛津英语词典中的一个单词的名称/在此处插入您最喜欢的词典。另外,我猜我需要将 Word 字段设置为主键。 (该表也将具有唯一标识符 PK)。

所以..我可能会得到这些我需要添加到表中的词...

例如。

  • 酒吧
  • 皮尤
  • 等等……

所以传统上,我会尝试以下(伪代码)

SELECT WordID FROM Words WHERE Word = @Word
IF WordID IS NULL OR WordID <= 0
    INSERT INTO Words VALUES (@Word)

即。 如果单词不存在,则插入它。

现在 .. 我担心的问题是我们得到了很多点击 .. 那么这个词是否有可能从另一个进程中插入 SELECT 和 INSERT .. 然后会抛出约束错误? (即Race Condition)。

然后我认为我可以做到以下几点......

INSERT INTO Words (Word)
SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

基本上,插入一个不存在的单词。

抛开语法错误不谈,我不确定这是好是坏,因为它如何锁定表(如果是的话)并且在表上的性能不高,以至于它获得大量读取和大量写入。

那么 - 您的 Sql 专家认为/做什么?

我希望有一个简单的插入并“捕获”任何抛出的错误。

【问题讨论】:

    标签: sql-server insert unique


    【解决方案1】:

    您的解决方案:

    INSERT INTO Words (Word)
        SELECT @Word
    WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)
    

    ... 差不多了。您可以将其简化为:

    INSERT INTO Words (Word)
        SELECT @Word
    WHERE NOT EXISTS (SELECT * FROM Words WHERE Word = @Word)
    

    ...因为 EXISTS 实际上不需要返回任何记录,因此查询优化器不会费心查看您要求的字段。

    但是,正如您所提到的,这并不是特别高效,因为它会在 INSERT 期间锁定整个表。除此之外,如果你给 Word 添加一个唯一索引(它不需要是主键),那么它只需要锁定相关页面。

    您最好的选择是模拟预期负载并使用 SQL Server Profiler 查看性能。与任何其他领域一样,过早优化是一件坏事。定义可接受的性能指标,然后在做任何其他事情之前进行衡量。

    如果这仍然不能为您提供足够的性能,那么数据仓库领域的许多技术可以提供帮助。

    【讨论】:

    • EXISTS (SELECT * FROM ...) 和 EXISTS (SELECT 1 FROM ...) 之间是否存在性能差异?我总是倾向于使用后者,以避免明星 - 这是有益的还是都一样?
    • 我怀疑一切都是一样的,但您必须在 SQL Server Profiler 中启动它才能确定。
    • 我也从不在我的选择中使用 *,但总是指定返回字段/值。
    • @Roger:另外,这不是过早的优化(我完全同意你的说法)。这是优化我现有的应用程序,它正在超时和死锁,它与这个 sql 语句有关:(
    • 只是为了继续这个讨论......你能把你的 sql 代码写成 LINQ 2 SQL 吗?
    【解决方案2】:

    我想我已经找到了一个更好(或至少更快)的答案。 创建如下索引:

    CREATE UNIQUE NONCLUSTERED INDEX [IndexTableUniqueRows] ON [dbo].[table] 
    (
        [Col1] ASC,
        [Col2] ASC,
    
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    

    包括所有定义唯一性的列。重要的部分是 IGNORE_DUP_KEY = ON。这会将非唯一插入变成警告。 SSIS 会忽略这些警告,您仍然可以使用 fastload。

    【讨论】:

      【解决方案3】:

      如果您使用的是 MS SQL Server,则可以在需要唯一的表列上创建唯一索引(记录在 here):

      CREATE UNIQUE [ CLUSTERED | NONCLUSTERED ] INDEX <index_name>
          ON Words ( word [ ASC | DESC ])
      

      根据您的情况指定ClusteredNonClustered。此外,如果您希望对其进行排序(以实现更快的搜索),请指定 ASCDESC 作为排序顺序。

      如果您想了解有关索引架构的更多信息,请参阅here

      否则,您可以使用UNIQUE CONSTRAINTS,如记录的here

      ALTER TABLE Words
      ADD CONSTRAINT UniqueWord
      UNIQUE (Word); 
      

      【讨论】:

        【解决方案4】:

        我遇到了类似的问题,我就是这样解决的

        insert into Words
        ( selectWord , Fixword)
        SELECT word,'theFixword'
        FROM   OldWordsTable
        WHERE 
        (
            (word LIKE 'junk%') OR
             (word LIKE 'orSomthing') 
        
        )
        and word not in 
            (
                SELECT selectWord FROM words WHERE selectWord = word
            ) 
        

        【讨论】:

          【解决方案5】:

          虽然唯一约束肯定是一种方法,但您也可以将其用于插入逻辑: http://www.sqlteam.com/article/application-locks-or-mutexes-in-sql-server-2005

          基本上你不会在下面的桌子上放任何锁,因此不用担心读取 而您的存在性检查将正常执行。

          它是 sql 代码中的互斥体。

          【讨论】:

            【解决方案6】:

            我不能说MS SQL的细节,但是SQL中的一个主键是为了确保唯一性。因此,根据通用 SQL 术语的定义,主键是表中唯一的一个或多个字段。虽然有不同的方法来强制执行此行为(用新条目替换旧条目而不是拒绝新条目)如果 MS SQL 都没有强制执行此行为的机制并且它不是拒绝新条目。只需确保将主键设置为 Word 字段,它应该工作。

            不过,我再次声明,这一切都来自我从 MySQL 编程和我的数据库课程中获得的知识,因此,如果我对 MS SQL 的复杂性不满意,请道歉。

            【讨论】:

              【解决方案7】:
              declare @Error int
              
              begin transaction
                INSERT INTO Words (Word) values(@word)
                set @Error = @@ERROR
                if @Error <> 0 --if error is raised
                begin
                    goto LogError
                end
              commit transaction
              goto ProcEnd
              
              LogError:
              rollback transaction
              

              【讨论】:

              • 老兄——我在开篇文章中说过,我不想插入一个愚蠢的插入然后检查错误。这不是很聪明的海事组织。
              猜你喜欢
              • 2020-12-29
              • 1970-01-01
              • 2018-05-09
              • 1970-01-01
              • 2015-12-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-01-02
              相关资源
              最近更新 更多