如果不存在最佳实践,则插入 SQLServer

我有一个 Competitions的结果表,其中举行 队员姓名和他们的排名在一方面。

另一方面,我需要保持 独特竞争对手名称表:

CREATE TABLE Competitors (cName nvarchar(64) primary key)

现在我在第一个表中有大约200,000个结果,我可以执行 当竞争对手的桌子是空的时候:

INSERT INTO Competitors SELECT DISTINCT Name FROM CompResults

而且这个查询只需要5秒钟就可以插入大约11,000个名字。

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

但是什么是最佳实践时,新的结果添加,与新的和现有的竞争对手? 我不想截断现有的竞争对手表

我需要执行 INSERT 语句只为新的竞争对手和做什么,如果他们存在。

408152 次浏览

您将需要将这些表连接在一起,并获得一个在 Competitors中不存在的独特竞争对手的列表。

这将插入唯一的记录。

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

有时可能需要快速完成此插入,而无需等待选择唯一名称。在这种情况下,可以将惟一名称插入到临时表中,然后使用该临时表插入到实际表中。这样做效果很好,因为所有处理都发生在插入临时表的时候,所以它不会影响实际的表。然后,在完成所有处理之后,将快速插入到实际的表中。我甚至可以包装最后一部分,即插入到实际表中的事务中。

从语义上讲,你是在询问“插入不存在的竞争对手”:

INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
NOT EXISTS (SELECT * FROM Competitors c
WHERE cr.Name = c.cName)

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

正常点。

你有模特比赛的桌子吗?比赛是由竞争者组成的?你需要在一个或多个比赛中有一个明确的参赛者名单... ..。

你应该有以下几张桌子... ..。

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]
)
)

对竞争对手的约束。竞争 ID 和竞争 ID 指向其他表。

有了这种表格结构-你的键都是简单的 INTS-似乎没有一个好的自然键适合模型,所以我认为代理键是一个很好的适合这里。

因此,如果你有这个,然后得到不同的名单的竞争对手在一个特定的竞争,你可以发出这样的查询:

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
)

如果你想知道每个竞争对手的得分:

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]

当你有一个新的竞争对手,然后你只需检查哪些已经存在的竞争对手表。如果他们已经存在,那么你不插入竞争对手为这些竞争对手,并做插入为新的。

然后你插入新的竞争在竞争中,最后你只是使所有的链接在竞争对手。

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

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

新语法 合并还提供了一种紧凑、优雅和高效的方法:

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);

按照 Transact Charlie 的建议将 操作表标准化是一个好主意,并且随着时间的推移将会减少许多麻烦和问题——但是有一些例如支持与外部系统集成的 接口表和支持分析处理的 报告表; 这些类型的表应该是 不一定被标准化——事实上,通常是 对他们来说更加方便和有效

在这种情况下,我认为 Transact Charlie 对您的操作表的建议是一个很好的建议。

但是为了集成(从外部来源加载数据)的目的,我会在“竞争者”表中为“竞争者”名称添加一个索引(不一定是唯一的) ,以支持“竞争者”名称上的高效连接,并且我会将一个接口表放入其中: “竞争结果”。

比赛结果应该包含你的比赛结果中的任何数据。像这样的接口表的目的是使从 Excel 工作表或 CSV 文件,或者其他任何形式的数据中截断和重新加载它变得尽可能快和容易。

该接口表不应被视为规范化操作表集的一部分。然后你就可以像 Richard 建议的那样加入“竞争结果”,将记录插入到那些还不存在的竞争对手中,并更新那些已经存在的记录(例如,如果你实际上有更多关于竞争对手的信息,比如他们的电话号码或电子邮件地址)。

有一件事我要注意-在现实中,竞争对手的名称,它似乎,是 在你的数据中不太可能是唯一的。例如,在20万个竞争者中,你很可能有两个或更多的大卫 · 史密斯。因此,我建议您从竞争对手那里收集更多的信息,比如他们的电话号码或电子邮件地址,或者更可能是独一无二的东西。

您的操作表“竞争者”应该为每个数据项只有一列,这些数据项可以提供组合自然键; 例如,它应该为主电子邮件地址提供一列。但是接口表应该有一个主电子邮件地址的 老了新的值槽,这样旧值就可以用来查找竞争对手的记录,并将其中的一部分更新为新值。

因此,比赛结果应该有一些“旧”和“新”字段-旧电子邮件,新电子邮件,旧电话,新电话,等等。这样,您就可以在“竞争者”中从“竞争者名称”、“电子邮件”和“电话”中组成一个复合键。

然后,当你有一些竞争结果,你可以截断和重新载入您的比赛结果表从您的 Excel 表或任何你有,并运行一个单一的,有效的插入,以插入所有新的竞争对手到竞争对手表,单一的,有效的更新,以更新所有的信息,现有的竞争对手从比赛结果。您还可以执行一次插入操作,将新行插入“竞争对手”表中。这些事情可以在 ProcessCOMPtionResults 存储过程中完成,该存储过程可以在加载了 COMPITtionResults 表之后执行。

这是对我在现实世界中看到的 Oracle 应用程序、 SAP、 PeopleSoft 以及其他企业软件套件的一系列做法的一种基本描述。

最后我想说的是我以前在 SO 上做过的一个注释: 如果你创建了一个外键,确保在你可以将一行的竞争对手添加到竞争对手 确保外键设置为级联更新和删除之前,竞争对手表中存在一个竞争对手。这样,如果你需要删除一个竞争对手,你可以这样做,所有与该竞争对手相关的行将自动删除。否则,在默认情况下,外键将要求您删除所有相关的行,然后才允许您删除竞争对手。

(有些人认为没有级联的外键是一个很好的安全预防措施,但我的经验是,他们只是一个怪异的痛苦的屁股,往往不仅仅是一个疏忽的结果,他们创造了一堆使工作的 DBA。处理人们意外删除的东西是为什么你有“你确定”对话框和各种类型的常规备份和冗余数据源。比起意外地删除一个竞争对手然后说“哦,不!我不是故意的!现在我没有他们的比赛结果了!啊!”后者当然很常见,因此,您确实需要为此做好准备,但是前者更常见,因此为前者做好准备的最简单和最好的方法是使外键级联更新和删除。)

上面关于正常化的讨论的答案是伟大的!但是,如果您发现自己处于像我这样的位置,不允许触及数据库模式或结构,那该怎么办呢?例如,DBA 是“上帝”,所有建议的修订都进入/dev/null?

在这方面,我觉得这个 已经回答了这个堆栈溢出张贴太对于上面提供代码示例的所有用户来说。

我正在重新发布来自 插入不存在的值的代码,这对我帮助最大,因为我不能改变任何底层的数据库表:

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

上面的代码使用的字段与您使用的字段不同,但是您可以通过各种技术获得大致的要点。

注意,根据 Stack Overflow 上的原始答案,这段代码是 从这里复制的

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

  • 如果您能够规范化并生成索引/键——很好!
  • 如果没有,你有像我这样的代码破解手段,希望 以上帮助。

祝你好运!

好吧,这是7年前的问题,但我认为最好的解决方案是完全放弃新的表,只是这样做作为一个自定义视图。这样就不用复制数据,不用担心数据的唯一性,也不会触及实际的数据库结构。就像这样:

CREATE VIEW vw_competitions
AS
SELECT
Id int
CompetitionName nvarchar(75)
CompetitionType nvarchar(50)
OtherField1 int
OtherField2 nvarchar(64)  --add the fields you want viewed from the Competition table
FROM Competitions
GO

可以在这里添加其他项,如其他表的联接、 WHERE 子句等。这很可能是解决这个问题的最优雅的方案,因为您现在可以只查询视图:

SELECT *
FROM vw_competitions

... 并向视图查询添加任何 WHERE、 IN 或 EXISTS 子句。

此外,如果要插入多个列并想检查它们是否存在,请使用下面的代码

Insert Into [Competitors] (cName, cCity, cState)
Select cName, cCity, cState from
(
select new.* from
(
select distinct cName, cCity, cState
from [Competitors] s, [City] c, [State] s
) new
left join
(
select distinct cName, cCity, cState
from [Competitors] s
) existing
on new.cName = existing.cName and new.City = existing.City and new.State = existing.State
where existing.Name is null  or existing.City is null or existing.State is null
)