【问题标题】:SQL - Get or Create in foreign table in one insert statementSQL - 在一个插入语句中获取或创建外部表
【发布时间】:2019-11-20 10:12:08
【问题描述】:

我有两张桌子

Session
- Id
- UserId
- CodeId (foreign key to Code.Id)

和

Code
- Id
- Text (string of 50 - 100 characters)

当然我可以做到以下几点:

  1. 尝试从代码中获取 ID
  2. 如果返回空,则插入新行并获取 ID
  3. 插入会话

你会得到类似这样的东西:

DECLARE @codeId INT;

SET @codeId = (SELECT Id FROM Code WHERE [Text] = @code);

IF ( @codeId IS NULL )
BEGIN
    INSERT INTO Code ([Text]) VALUES (@code)
    SET @codeId = SCOPE_IDENTITY()
END

INSERT INTO Session (UserId, codeId) VALUES(@userId, @codeId)
  • 是否也可以通过更高效的调用来做到这一点?
  • 我应该关注比赛条件吗?

免责声明: 在拥有大量用户的生产站点上,这将被称为很多,并且在这种环境中已经证明性能很重要。

补充说明

  • Code.Text 上会有一个唯一索引
  • 我预计Session 中有数百万行
  • 我预计Code 中大约有 50.000 到 200.000 行

【问题讨论】:

  • 您希望代码中大约有多少行?你会有文本索引吗?该索引可以是唯一的吗?
  • 我建议将nolock 添加到您的选择(SELECT Id FROM Code WITH(NOLOCK) WHERE [Text] = @code);
  • 嗨@SerkanArslan,感谢您的留言-您能详细说明原因吗?
  • @DirkBoer 如果代码表由于插入而有任何锁定,它会阻止等待选择。由于潜在的会话数,它看起来是可能的。

标签: sql sql-server foreign-keys sql-insert


【解决方案1】:

这是一个棘手的问题 - 我肯定会在这里创建一个测试工具来验证您的假设。

我要做的第一件事是颠倒顺序 - 如果您有“数百万”个孩子和“数十万”个父母,那么您的孩子很可能已经有匹配的父母行。

所以,类似:

INSERT INTO Session (UserId, codeId) 
SELECT @userId, [Text]
FROM Code
where [Text] = @code

if @@ROWCOUNT = 0
begin
    INSERT INTO Code ([Text]) VALUES (@code)
    SET @codeId = SCOPE_IDENTITY()

    INSERT INTO Session (UserId, codeId) VALUES(@userId, @codeId)
END

【讨论】:

    【解决方案2】:

    假设您拥有正确的索引,我认为不会有更有效的方法。

    您应该关注比赛条件吗?好吧,使用您给定的代码,然后“是”,但这很容易解决。从唯一索引开始,以便数据库在 Text 上强制执行唯一性:

    CREATE UNIQUE INDEX unq_code_text ON Code(Text);
    

    那么,代码应该是这样的:

    DECLARE @codeId INT;
    
    SET @codeId = (SELECT Id FROM Code WHERE [Text] = @code);
    
    IF ( @codeId IS NULL )
    BEGIN TRY
        INSERT INTO Code ([Text]) VALUES (@code)
            SET @codeId = SCOPE_IDENTITY();
    END TRY
    BEGIN CATCH
        -- you can check or just assume that the issue the unique constraint
    END CATCH;
    
    INSERT INTO Session (UserId, codeId)
        VALUES (@userId, @codeId);
    

    TRY/CATCH 块处理竞争条件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-29
      • 2010-10-30
      • 2017-07-27
      • 2012-12-13
      • 2021-11-04
      • 2018-12-08
      • 1970-01-01
      相关资源
      最近更新 更多