我有一个表,数据名为 energydata
它只有三列
(webmeterID, DateTime, kWh)
我在表中有一组新的更新数据temp_energydata
。
在DateTime
和webmeterID
保持不变。但是kWh
值需要从temp_energydata
表中更新。
我该如何以正确的方式编写T-SQL?
Answers:
假设您需要一个实际的SQL ServerMERGE
语句:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh);
如果您还想删除目标中不在源中的记录:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
因为这已经变得越来越流行了,所以我觉得我应该扩大一些注意事项以引起注意。
首先,有几个博客报告了旧版本SQL Server中该MERGE
语句的并发问题。我不知道此问题是否在以后的版本中得到解决。无论哪种方式,都可以通过指定HOLDLOCK
或SERIALIZABLE
锁定提示来解决:
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
[...]
您还可以使用更严格的事务隔离级别来完成同一件事。
还有其他一些已知问题与MERGE
。(请注意,由于Microsoft对Connect进行了核对,并且未将旧系统中的问题链接到新系统中的问题,因此这些较旧的问题很难找到。谢谢Microsoft!)据我所知,大多数都不常见问题或可以使用与上述相同的锁定提示来解决,但我尚未对其进行测试。
实际上,即使我自己对MERGE
语句从未遇到任何问题,但我现在总是使用WITH (HOLDLOCK)
提示,并且我更喜欢仅在最简单的情况下使用该语句。
NOT MATCHED BY SOURCE
在这种情况下,可能需要谨慎使用该子句。如果temp_energydata
仅包含中的一部分成员的更新energydata
,则您的第二个MERGE将删除在临时集中找不到的所有成员的数据。
我经常用“培根钻头”很好的答案,因为我不记得语法。
但是我通常会添加一个CTE,以使DELETE部分更有用,因为您经常希望仅将合并应用于目标表的一部分。
WITH target as (
SELECT * FROM dbo.energydate WHERE DateTime > GETDATE()
)
MERGE INTO target WITH (HOLDLOCK)
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.DateTime = source.DateTime
WHEN MATCHED THEN
UPDATE SET target.kWh = source.kWh
WHEN NOT MATCHED BY TARGET THEN
INSERT (webmeterID, DateTime, kWh)
VALUES (source.webmeterID, source.DateTime, source.kWh)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
如果您只需要energydata
基于中的数据更新记录temp_energydata
,并假设其中temp_enerydata
不包含任何新记录,请尝试以下操作:
UPDATE e SET e.kWh = t.kWh
FROM energydata e INNER JOIN
temp_energydata t ON e.webmeterID = t.webmeterID AND
e.DateTime = t.DateTime
这是工作的sqlfiddle
但是,如果temp_energydata
包含新记录,并且您energydata
最好将其插入一个语句中,那么您绝对应该使用培根比特斯给出的答案。
MERGE
操作,而是如何将数据从一个表合并到另一个表中(UPDATE
如此处所示)。
UPDATE ed
SET ed.kWh = ted.kWh
FROM energydata ed
INNER JOIN temp_energydata ted ON ted.webmeterID = ed.webmeterID
energydata
对于以外的日期temp_energydata
,这很可能会覆盖其中的电表读数,这可能会带来意想不到的意外结果。
正确的方法是:
UPDATE test1
INNER JOIN test2 ON (test1.id = test2.id)
SET test1.data = test2.data
temp_energydata
。当然,您可以添加一个INSERT INTO ... SELECT * FROM ... old LEFT JOIN new WHERE old.foo IS NULL
(在UPDATE之前或之后),但是它是两个语句,并且如果有足够的数据,则执行时间可能会足够长,从而导致出现问题,除非您锁定该表并且这样做可能会激怒用户(这里没有足够的空间来容纳所有方案)。说了这么多,我自己先进行UPDATE,然后进行INSERT(反之亦然),但它不能回答OP的问题。
temp_energydata
不在energydata
?