如果不存在SQL Server插入最佳实践

Did*_*evy 148 sql-server insert-update

我有一个Competitions结果表,一方面保存团队成员的名字和他们的排名.

另一方面,我需要维护一个独特的竞争对手名称表:

CREATE TABLE Competitors (cName nvarchar(64) primary key)
Run Code Online (Sandbox Code Playgroud)

现在我在第一个表中有大约200,000个结果,当竞争对手表空时,我可以执行此操作:

INSERT INTO Competitors SELECT DISTINCT Name FROM CompResults
Run Code Online (Sandbox Code Playgroud)

查询只需要大约5秒钟就可以插入大约11,000个名称.

到目前为止,这不是一个关键应用程序,因此我可以考虑每月截断一次竞争对手表,当我收到大约10,000行的新竞争结果时.

但是,当新的和现有的竞争对手增加新的结果时,最佳做法是什么?我不想截断现有的竞争对手表

我只需要为新的竞争对手执行INSERT语句,如果它们存在则不执行任何操作.

gbn*_*gbn 204

在语义上你要求"插入竞争对手尚不存在的地方":

INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
   NOT EXISTS (SELECT * FROM Competitors c
              WHERE cr.Name = c.cName)
Run Code Online (Sandbox Code Playgroud)

  • @Didier Levy:效率?为什么截断,重新创建何时只能使用差异进行更新.那就是:BEGIN TRAN DELETE CompResults INSERT CompResults .. COMMIT TRAN =更多工作. (3认同)
  • 嗯,这是我在SO上提出问题之前所要做的.但我的思想的核心是:这对于每周一次从头开始重建名表有多好?(记住这只需要几秒钟) (2认同)

pco*_*fre 53

另一个选择是将您的结果表与您现有的竞争对手表连接起来,并通过过滤与联接中不匹配的不同记录来查找新的竞争对手:

INSERT Competitors (cName)
SELECT  DISTINCT cr.Name
FROM    CompResults cr left join
        Competitors c on cr.Name = c.cName
where   c.cName is null
Run Code Online (Sandbox Code Playgroud)

新语法MERGE还提供了一种紧凑,优雅和高效的方法:

MERGE INTO Competitors AS Target
USING (SELECT DISTINCT Name FROM CompResults) AS Source ON Target.Name = Source.Name
WHEN NOT MATCHED THEN
    INSERT (Name) VALUES (Source.Name);
Run Code Online (Sandbox Code Playgroud)

  • MERGE声明仍然存在很多问题.只是谷歌"SQL合并问题" - 许多博主已经详细讨论了这一点. (4认同)

Tra*_*lie 32

不知道为什么其他人还没有说过这个;

NORMALIZE.

你有一张比赛模型表吗?比赛是由竞争对手组成的?您需要在一个或多个比赛中列出不同的参赛者名单......

你应该有以下表格.....

CREATE TABLE Competitor (
    [CompetitorID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitorName] NVARCHAR(255)
    )

CREATE TABLE Competition (
    [CompetitionID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitionName] NVARCHAR(255)
    )

CREATE TABLE CompetitionCompetitors (
    [CompetitionID] INT
    , [CompetitorID] INT
    , [Score] INT

    , PRIMARY KEY (
        [CompetitionID]
        , [CompetitorID]
        )
    )
Run Code Online (Sandbox Code Playgroud)

对CompetitionCompetitors.CompetitionID和CompetitorID的约束指向其他表.

有了这种表结构 - 你的键都是简单的INTS - 似乎没有一个适合模型的好的自然键,所以我认为SURROGATE KEY非常适合这里.

因此,如果你有这个,那么在特定的竞争中获得不同的竞争者列表,你可以发出这样的查询:

DECLARE @CompetitionName VARCHAR(50) SET @CompetitionName = 'London Marathon'

    SELECT
        p.[CompetitorName] AS [CompetitorName]
    FROM
        Competitor AS p
    WHERE
        EXISTS (
            SELECT 1
            FROM
                CompetitionCompetitor AS cc
                JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]
            WHERE
                cc.[CompetitorID] = p.[CompetitorID]
                AND cc.[CompetitionName] = @CompetitionNAme
        )
Run Code Online (Sandbox Code Playgroud)

如果你想要每个比赛的得分,竞争对手是:

SELECT
    p.[CompetitorName]
    , c.[CompetitionName]
    , cc.[Score]
FROM
    Competitor AS p
    JOIN CompetitionCompetitor AS cc ON cc.[CompetitorID] = p.[CompetitorID]
    JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]
Run Code Online (Sandbox Code Playgroud)

当您与新竞争对手进行新竞争时,您只需检查竞争对手表中已存在的竞争对手.如果它们已经存在,那么您不会为竞争对手插入竞争对手并插入新的竞争对手.

然后你插入新的竞赛比赛,最后你只需在CompetitionCompetitors中建立所有链接.

  • 假设OP此时有轻率重组他的所有表以获得一个缓存结果。重写你的数据库和应用程序,而不是在某个定义的范围内解决问题,每次事情都不容易落实到位时,都是灾难的秘诀。 (4认同)
  • 也许在像我这样的 OP 情况下,您并不总是有权修改数据库..并且重写/规范化旧数据库并不总是在预算或分配的时间内。 (2认同)

ric*_*ard 10

您需要将表格加在一起,并获得一个尚未存在的独特竞争对手列表Competitors.

这将插入唯一记录.

INSERT Competitors (cName) 
SELECT DISTINCT Name
FROM CompResults cr LEFT JOIN Competitors c ON cr.Name = c.cName
WHERE c.Name IS NULL
Run Code Online (Sandbox Code Playgroud)

可能有一段时间需要快速完成此插入而无法等待选择唯一名称.在这种情况下,您可以将唯一名称插入临时表,然后使用该临时表插入到您的真实表中.这很有效,因为所有处理都在您插入临时表时发生,因此它不会影响您的真实表.然后,当您完成所有处理后,您可以快速插入真实表格.我甚至可以在事务中将最后一部分插入真实表中.


小智 5

上面关于标准化的答案非常棒!但是,如果您发现自己处于像我一样的位置,不允许您接触数据库模式或结构,该怎么办?例如,DBA 是“神”,所有建议的修订都会转到 /dev/null?

在这方面,我觉得这个 Stack Overflow 帖子也已经回答了上面所有提供代码示例的用户的问题。

我重新发布了INSERT VALUES WHERE NOT EXISTS中的代码,这对我帮助最大,因为我无法更改任何底层数据库表:

INSERT INTO #table1 (Id, guidd, TimeAdded, ExtraData)
SELECT Id, guidd, TimeAdded, ExtraData
FROM #table2
WHERE NOT EXISTS (Select Id, guidd From #table1 WHERE #table1.id = #table2.id)
-----------------------------------
MERGE #table1 as [Target]
USING  (select Id, guidd, TimeAdded, ExtraData from #table2) as [Source]
(id, guidd, TimeAdded, ExtraData)
    on [Target].id =[Source].id
WHEN NOT MATCHED THEN
    INSERT (id, guidd, TimeAdded, ExtraData)
    VALUES ([Source].id, [Source].guidd, [Source].TimeAdded, [Source].ExtraData);
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT id, guidd, TimeAdded, ExtraData from #table2
EXCEPT
SELECT id, guidd, TimeAdded, ExtraData from #table1
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT #table2.id, #table2.guidd, #table2.TimeAdded, #table2.ExtraData
FROM #table2
LEFT JOIN #table1 on #table1.id = #table2.id
WHERE #table1.id is null
Run Code Online (Sandbox Code Playgroud)

上面的代码使用的字段与您所拥有的字段不同,但您可以通过各种技术获得一般要点。

请注意,根据 Stack Overflow 上的原始答案,此代码是从此处复制的

无论如何,我的观点是“最佳实践”通常归结为你能做什么和不能做什么以及理论。

  • 如果您能够规范化并生成索引/键——那就太棒了!
  • 如果没有,并且您像我一样求助于代码黑客,希望上述内容有所帮助。

祝你好运!