【问题标题】:Fastest way to return a primary key value SQL Server 2005返回主键值的最快方法 SQL Server 2005
【发布时间】:2011-01-20 11:49:27
【问题描述】:

我有一个包含主键 (int) 和唯一值 (nvarchar(255)) 的两列表

当我在这个表中插入一个值时,我可以使用 Scope_identity() 来返回我刚刚插入的值的主键。但是,如果该值已经存在,我必须执行额外的选择以返回主键以进行后续操作(将该主键插入到第二个表中)

我认为必须有更好的方法来做到这一点 - 我考虑使用覆盖索引,但表只有两列,我在覆盖索引上阅读的大部分内容表明它们仅在表明显更大的情况下才有帮助比指数。

有没有更快的方法来做到这一点?即使覆盖的索引与表的大小相同,它会更快吗?

【问题讨论】:

  • 你的意思是“如果值已经存在”...你的意思是你首先尝试找到具有相同'Column2'值的记录?

标签: sql sql-server indexing


【解决方案1】:

构建索引不会为您带来任何好处,因为您已经将值列创建为唯一的(在后台构建索引)。实际上,全表扫描与您的方案中的索引扫描没有什么不同。

我假设您希望有一种“如果不存在则插入”的行为。没有办法绕过第二次选择

if not exists (select ID from where name = @...)
   insert into ...
   select SCOPE_IDENTITY()
else 
   (select ID from where name = @...)

如果该值恰好存在,则查询通常会被缓存,因此第二个 ID 选择应该不会影响性能。

【讨论】:

  • 另外,这是 2 个选择,因此如果记录存在,则开销会加倍。
  • 虽然你不会得到一个“全表扫描”,而是一个“非聚集索引 SEEK”。差别很大。
  • @erik,我承认我在 sql server 上的能力不是很强,你能详细说明一下吗?
【解决方案2】:
[Update statment here]
IF (@@ROWCOUNT = 0)
BEGIN
  [Insert statment here]
  SELECT Scope_Identity()
END
ELSE
BEGIN
  [SELECT id statment here]
END

我不知道性能,但它没有太大的开销

【讨论】:

    【解决方案3】:

    正如已经提到的,这确实不应该是一个缓慢的操作,特别是如果您索引两个列。但是,如果您决心减少此操作的费用,那么我认为您没有理由不能完全删除该表而直接使用唯一值而不是在该表中查找它。像这样的 1-1 映射(理论上)是多余的。我说理论上是因为使用 nvarchar 而不是 int 可能会对性能产生影响。

    【讨论】:

    • 有很多理由不使用 varchar(255) 字段作为键...主要是其他表的主键。
    • 很明显,这最终可能会成为一次重大改造。但是,如果有人希望优化索引搜索,那么肯定需要考虑数据重构。
    【解决方案4】:

    我会发布这个答案,因为其他人似乎都说如果记录存在,您必须查询表两次......这不是真的。

    第 1 步)在另一列上创建唯一索引:

    我推荐这个作为索引:

    -- 我们包含了“ID”列,这样一旦“WHERE”子句完成,SQL 就不必再看太远了。

    CREATE INDEX MyLilIndex ON dbo.MyTable (Column2) INCLUDE (ID)
    

    步骤 2)

    DECLARE @TheID INT
    SELECT @TheID = ID from MyTable WHERE Column2 = 'blah blah'
    
    IF (@TheID IS NOT NULL)
    BEGIN
        -- See, you don't have to query the table twice!
        SELECT @TheID AS TheIDYouWanted
    END
    ELSE
        INSERT...
        SELECT SCOPE_IDENTITY() AS TheIDYouWanted
    

    【讨论】:

    • 他问题的核心不是语法,主要是索引。
    • 我没有提到语法更改...我告诉他不要查询表两次(无论索引如何)。我只发布了这个答案,因为其他人建议他打了两次桌子。另外,我现在已经包含了他应该具有的确切索引。
    【解决方案5】:

    为第二个条目创建唯一索引,然后:

    if not exists (select null from ...)
       insert into ...
    else 
       select x from ...
    

    您无法摆脱索引,而且开销并不大——SQL Server 支持最多 900 字节的索引列,并且不进行区分。

    模型的需求比任何感知到的性能问题都更重要,符号化字符串(这就是你正在做的事情)是减少数据库大小的常用方法,这间接(并且通常)意味着更好的性能。

    -- 编辑--

    为了安抚蒂莫西:

    declare @x int = select x from ...
    
    if (@x is not null)
       return x 
    else
       ...
    

    【讨论】:

    • 如果记录确实存在,则运行 2 个选择语句。你不需要这样做。
    • 这是秋千和环形交叉路口,很可能会被优化掉。
    【解决方案6】:

    您可以使用OUTPUT clause 在同一语句中返回值。这是一个例子。

    DDL:

    CREATE TABLE ##t (
        id int PRIMARY KEY IDENTITY(1,1),
        val varchar(255) NOT NULL
    )
    GO
    
    -- no need for INCLUDE as PK column is always included in the index
    CREATE UNIQUE INDEX AK_t_val ON ##t (val)
    

    DML:

    DECLARE @id int, @val varchar(255)
    
    SET @val = 'test' -- or whatever you need here
    
    SELECT @id = id FROM ##t WHERE val = @val
    
    IF (@id IS NULL)
    BEGIN
        DECLARE @new TABLE (id int)
    
        INSERT INTO ##t (val) 
        OUTPUT inserted.id INTO @new -- put new ID into table variable immediately
        VALUES (@val)
    
        SELECT @id = id FROM @new
    END
    
    PRINT @id
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多