SQL Оператор слияния с индексированным представлением - PullRequest
0 голосов
/ 29 мая 2020

Я использую SQL Server 2016 и пытаюсь создать оператор MERGE, который будет вставлять новую запись, если она не существует, или получать идентификатор записи, если она существует.

Это работает правильно, но имеет непреднамеренный побочный эффект, вызывающий обновление индексированного представления, даже если данные не обновляются, когда я запускаю это несколько раз.

Это ожидаемое поведение или я просто делаю что-то не так?

Интуитивно я предполагаю, что ожидал, что, поскольку слияние находит совпадение, затем обновляет только локальную переменную, никаких изменений в индексированной вид вообще будет отображаться в плане выполнения.

Execution Plan

CREATE TABLE dbo.Customer
(
    CustomerId INT IDENTITY(100,1) PRIMARY KEY NOT NULL,
    CustomerName VARCHAR(100) NOT NULL
)
GO

CREATE VIEW dbo.Customer_v
WITH SCHEMABINDING
AS
    SELECT CustomerId, CustomerName 
    FROM dbo.Customer
GO

CREATE UNIQUE CLUSTERED INDEX [CL_IX_Customer_v]
ON dbo.Customer_v(CustomerId ASC);
GO

DECLARE @CustomerId AS INT
DECLARE @CustomerName AS VARCHAR(100) = 'Mike Smith'
DECLARE @WasCustomerCreated AS BIT = 0
DECLARE @SummaryOfChanges TABLE(Change VARCHAR(20));

MERGE INTO dbo.Customer AS Target
USING (VALUES (@CustomerName)) AS Source (CustomerName) ON (Target.CustomerName = Source.CustomerName)

WHEN MATCHED THEN 
    UPDATE SET @CustomerId = Target.CustomerId

WHEN NOT MATCHED THEN 
    INSERT (CustomerName) VALUES (@CustomerName)

OUTPUT $action INTO @SummaryOfChanges;

IF EXISTS(SELECT * FROM @SummaryOfChanges WHERE Change = 'Insert')
BEGIN
    SET @WasCustomerCreated = 1
    SET @CustomerId = SCOPE_IDENTITY()
END

SELECT @CustomerId AS CustomerId, @WasCustomerCreated AS WasCustomerCreated

1 Ответ

0 голосов
/ 29 мая 2020

Вы можете добавить «AND @CustomerId <> Target.CustomerId» в оператор слияния:

MERGE INTO dbo.Customer AS Target
USING (VALUES(@CustomerName)) AS Source (CustomerName)
ON (Target.CustomerName = Source.CustomerName)
WHEN MATCHED AND @CustomerId <> Target.CustomerId THEN UPDATE SET @CustomerId = Target.CustomerId
WHEN NOT MATCHED THEN INSERT (CustomerName) VALUES (@CustomerName)
OUTPUT $action INTO @SummaryOfChanges;
...