我正在尝试使用MERGE语句从表中插入或删除行,但我只想对这些行的一个子集进行操作。的文档MERGE有一个措辞非常强烈的警告:
仅指定目标表中用于匹配目的的列很重要。也就是说,指定目标表中与源表的相应列进行比较的列。不要试图通过在 ON 子句中过滤掉目标表中的行来提高查询性能,例如通过指定 AND NOT target_table.column_x = value。这样做可能会返回意外和不正确的结果。
但这正是我必须做的事情才能完成我的MERGE工作。
我拥有的数据是一个标准的项目到类别的多对多连接表(例如,哪些项目包含在哪些类别中),如下所示:
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Run Code Online (Sandbox Code Playgroud)
我需要做的是用新的项目列表有效地替换特定类别中的所有行。我最初的尝试是这样的:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId ) …Run Code Online (Sandbox Code Playgroud) 我有一个表测试,其中包含主键和自动递增的列 id 和名称。当且仅当没有记录时,我想插入一条新记录。例如
输入是 id=30122 和 name =john
如果有 ID 为 30122 的记录,那么我将 name 列更新为 john,如果没有记录,那么我将插入一条新记录。
我可以使用 2 个查询,例如
select * from test where id=30122
Run Code Online (Sandbox Code Playgroud)
如果它有一些记录,那么我可以使用 update test set name='john' where id=3012
或者如果它没有记录,那么我可以使用
insert into test(name) values('john')
Run Code Online (Sandbox Code Playgroud)
但我想使用单个查询?
有人可以告诉它是否可能吗?
我再次发现 SQL Server 和 MERGE 语句存在问题,需要进行一些确认。
我可以在 Azure 数据库上不断重现我的问题(但不能在本地 SQL Server 2017/2019 上重现)。
请执行以下步骤(一步一步,而不是一次命令执行)!
1)架构脚本:
CREATE TABLE [dbo].[ImpactValueHistory]
(
[Rn] BIGINT NOT NULL,
[ImpactId] UNIQUEIDENTIFIER NOT NULL,
[ImpactValueTypeId] INT NOT NULL,
[Date] DATE NOT NULL,
[Value] DECIMAL(38, 10) NOT NULL,
[ValidFrom] DATETIME2 NOT NULL CONSTRAINT [DF_ImpactValueHistory_ValidFrom] DEFAULT CONVERT(DATETIME2, '0001-01-01'),
[ValidTo] DATETIME2 NOT NULL CONSTRAINT [DF_ImpactValueHistory_ValidTo] DEFAULT CONVERT(DATETIME2, '9999-12-31 23:59:59.9999999'),
[ImpactPeriodId] INT NOT NULL,
[NormalizedValue] DECIMAL(38, 10) NOT NULL,
)
GO
CREATE CLUSTERED COLUMNSTORE INDEX [COLIX_ImpactValueHistory]
ON [dbo].[ImpactValueHistory];
GO
CREATE NONCLUSTERED …Run Code Online (Sandbox Code Playgroud) 我有以下过程(SQL Server 2008 R2):
create procedure usp_SaveCompanyUserData
@companyId bigint,
@userId bigint,
@dataTable tt_CoUserdata readonly
as
begin
set nocount, xact_abort on;
merge CompanyUser with (holdlock) as r
using (
select
@companyId as CompanyId,
@userId as UserId,
MyKey,
MyValue
from @dataTable) as newData
on r.CompanyId = newData.CompanyId
and r.UserId = newData.UserId
and r.MyKey = newData.MyKey
when not matched then
insert (CompanyId, UserId, MyKey, MyValue) values
(@companyId, @userId, newData.MyKey, newData.MyValue);
end;
Run Code Online (Sandbox Code Playgroud)
CompanyId、UserId、MyKey 构成目标表的组合键。CompanyId 是父表的外键。此外,还有一个非聚集索引CompanyId asc, UserId asc。
它是从许多不同的线程调用的,我一直在调用相同语句的不同进程之间出现死锁。我的理解是“with (holdlock)”对于防止插入/更新竞争条件错误是必要的。
我假设两个不同的线程在验证约束时以不同的顺序锁定行(或页面),因此是死锁。
这是一个正确的假设吗? …
我有以下MERGE针对数据库发出的语句:
MERGE "MySchema"."Point" AS t
USING (
SELECT "ObjectId", "PointName", z."Id" AS "LocationId", i."Id" AS "Region"
FROM @p1 AS d
JOIN "MySchema"."Region" AS i ON i."Name" = d."Region"
LEFT JOIN "MySchema"."Location" AS z ON z."Name" = d."Location" AND z."Region" = i."Id"
) AS s
ON s."ObjectId" = t."ObjectId"
WHEN NOT MATCHED BY TARGET
THEN INSERT ("ObjectId", "Name", "LocationId", "Region") VALUES (s."ObjectId", s."PointName", s."LocationId", s."Region")
WHEN MATCHED
THEN UPDATE
SET "Name" = s."PointName"
, "LocationId" = s."LocationId"
, "Region" …Run Code Online (Sandbox Code Playgroud) 在我们的一个数据库中,我们有一个由多个线程密集并发访问的表。线程确实通过MERGE. 还有一些线程偶尔会删除行,因此表数据非常不稳定。执行 upsert 的线程有时会陷入死锁。该问题看起来与此问题中描述的问题相似。不过,不同之处在于,在我们的例子中,每个线程都只更新或插入一行。
简化设置如下。该表是堆,上面有两个唯一的非聚集索引
CREATE TABLE [Cache]
(
[UID] uniqueidentifier NOT NULL CONSTRAINT DF_Cache_UID DEFAULT (newid()),
[ItemKey] varchar(200) NOT NULL,
[FileName] nvarchar(255) NOT NULL,
[Expires] datetime2(2) NOT NULL,
CONSTRAINT [PK_Cache] PRIMARY KEY NONCLUSTERED ([UID])
)
GO
CREATE UNIQUE INDEX IX_Cache ON [Cache] ([ItemKey]);
GO
Run Code Online (Sandbox Code Playgroud)
典型的查询是
DECLARE
@itemKey varchar(200) = 'Item_0F3C43A6A6A14255B2EA977EA730EDF2',
@fileName nvarchar(255) = 'File_0F3C43A6A6A14255B2EA977EA730EDF2.dat';
MERGE INTO [Cache] WITH (HOLDLOCK) T
USING (
VALUES (@itemKey, @fileName, dateadd(minute, 10, sysdatetime()))
) S(ItemKey, FileName, Expires)
ON …Run Code Online (Sandbox Code Playgroud) 在下面的语法中,When Matched如果更新表中的值与员工表中的值不同,是否可以只更新?类似于我下面的 DDL - 但当然这会引发错误并且不起作用。
我应该在语法中更新什么以仅更新不同的行?我想要的更新不是迭代匹配的每一行,而是emp44 & emp55因为地址不同而只更新。
出现的错误是
消息 156,级别 15,状态 1,第 24 行
关键字“WHERE”附近的语法不正确。
句法
DECLARE @Emp Table (empid varchar(10), empaddress varchar(100))
Insert Into @Emp Values
('emp11', '111 No Blue'),
('emp22', '222 No Blue'),
('emp33', '333 No Blue'),
('emp44', '444 No Blue'),
('emp55', '555 No Blue');
Declare @EmpUpdates TABLE (empid varchar(10), empaddress varchar(100))
Insert Into @EmpUpdates Values
('emp11', '111 No Blue'),
('emp22', '222 No Blue'),
('emp33', '333 No Blue'),
('emp44', '999 No Blue'),
('emp55', …Run Code Online (Sandbox Code Playgroud) 我有一个执行MERGE语句的存储过程。
在执行合并时,它似乎默认锁定整个表。
我在一个事务中调用这个存储过程,我也在做一些其他的事情,我希望它只会锁定受影响的行。
我尝试了提示MERGE INTO myTable WITH (READPAST),它似乎锁定较少。但是ms doc中有一个警告说它可以插入重复的键,甚至绕过主键。
这是我的表架构:
CREATE TABLE StudentDetails
(
StudentID INTEGER PRIMARY KEY,
StudentName VARCHAR(15)
)
GO
INSERT INTO StudentDetails
VALUES(1,'WANG')
INSERT INTO StudentDetails
VALUES(2,'JOHNSON')
GO
CREATE TABLE StudentTotalMarks
(
Id INT IDENTITY PRIMARY KEY,
StudentID INTEGER REFERENCES StudentDetails,
StudentMarks INTEGER
)
GO
INSERT INTO StudentTotalMarks
VALUES(1,230)
INSERT INTO StudentTotalMarks
VALUES(2,255)
GO
Run Code Online (Sandbox Code Playgroud)
这是我的存储过程:
CREATE PROCEDURE MergeTest
@StudentId int,
@Mark int
AS
WITH Params
AS
(
SELECT @StudentId as StudentId,
@Mark …Run Code Online (Sandbox Code Playgroud) 我们经常会遇到“如果不存在,则插入”的情况。Dan Guzman 的博客对如何使此进程线程安全进行了出色的调查。
我有一个基本表,它只是将字符串从SEQUENCE. 在存储过程中,我需要获取该值的整数键(如果它存在),或者INSERT它然后获取结果值。该dbo.NameLookup.ItemName列有一个唯一性约束,因此数据完整性没有风险,但我不想遇到异常。
这不是一个IDENTITY所以我无法获得,SCOPE_IDENTITY并且NULL在某些情况下价值可能是。
在我的情况下,我只需要处理INSERT桌面上的安全问题,所以我试图决定是否使用MERGE这样的方法更好:
SET NOCOUNT, XACT_ABORT ON;
DECLARE @vValueId INT
DECLARE @inserted AS TABLE (Id INT NOT NULL)
MERGE
dbo.NameLookup WITH (HOLDLOCK) AS f
USING
(SELECT @vName AS val WHERE @vName IS NOT NULL AND LEN(@vName) > 0) AS new_item
ON f.ItemName= new_item.val
WHEN MATCHED THEN
UPDATE SET @vValueId = f.Id
WHEN NOT MATCHED BY TARGET THEN
INSERT …Run Code Online (Sandbox Code Playgroud) 我有一个带有触发器的视图instead of,我尝试将其与 EF Core 一起使用,EF Core 尝试以merge语句的形式将批量插入一起。这是我的表格和视图:
create table tbl (id uniqueidentifier not null primary key, data nvarchar(max) null)
go
create view vwTbl as select * from tbl
go
create trigger vwTblInsert on vwTbl instead of insert as
insert into tbl (id, data)
select id, data
from inserted
go
Run Code Online (Sandbox Code Playgroud)
以下是 EF 生成的近似 SQL:
declare @inserted1 table ([id] uniqueidentifier);
merge vwTbl using (
values (newid(), 'xxx')
) as i (id, data) on 1=0
when not matched then …Run Code Online (Sandbox Code Playgroud)