Bu MERGE ifadesi neden oturumun ölmesine neden oluyor?


23

MERGEVeritabanına karşı verilen aşağıdaki ifadeye sahibim :

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" = s."Region"
OUTPUT $action, inserted.*, deleted.*;

Ancak bu, oturumun aşağıdaki hatayla sonlandırılmasına neden olur:

Mesaj 0, Seviye 11, Durum 0, Satır 67 Geçerli komutta ciddi bir hata oluştu. Varsa, sonuçlar atılmalıdır.

Mesaj 0, Seviye 20, Durum 0, Satır 67 Geçerli komutta ciddi bir hata oluştu. Varsa, sonuçlar atılmalıdır.

Hatayı üreten kısa bir test betiği hazırladım:

USE master;
GO
IF DB_ID('TEST') IS NOT NULL
DROP DATABASE "TEST";
GO
CREATE DATABASE "TEST";
GO
USE "TEST";
GO

SET NOCOUNT ON;

IF SCHEMA_ID('MySchema') IS NULL
EXECUTE('CREATE SCHEMA "MySchema"');
GO

IF OBJECT_ID('MySchema.Region', 'U') IS NULL
CREATE TABLE "MySchema"."Region" (
"Id" TINYINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_Region" PRIMARY KEY,
"Name" VARCHAR(8) NOT NULL CONSTRAINT "UK_MySchema_Region" UNIQUE
);
GO

INSERT [MySchema].[Region] ([Name]) 
VALUES (N'A'), (N'B'), (N'C'), (N'D'), (N'E'), ( N'F'), (N'G');

IF OBJECT_ID('MySchema.Location', 'U') IS NULL
CREATE TABLE "MySchema"."Location" (
"Id" SMALLINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_Location" PRIMARY KEY,
"Region" TINYINT NOT NULL CONSTRAINT "FK_MySchema_Location_Region" FOREIGN KEY REFERENCES "MySchema"."Region" ("Id"),
"Name" VARCHAR(128) NOT NULL,
CONSTRAINT "UK_MySchema_Location" UNIQUE ("Region", "Name") 
);
GO

IF OBJECT_ID('MySchema.Point', 'U') IS NULL
CREATE TABLE "MySchema"."Point" (
"ObjectId" BIGINT NOT NULL CONSTRAINT "PK_MySchema_Point" PRIMARY KEY,
"Name" VARCHAR(64) NOT NULL,
"LocationId" SMALLINT NULL CONSTRAINT "FK_MySchema_Point_Location" FOREIGN KEY REFERENCES "MySchema"."Location"("Id"),
"Region" TINYINT NOT NULL CONSTRAINT "FK_MySchema_Point_Region" FOREIGN KEY REFERENCES "MySchema"."Region" ("Id"),
CONSTRAINT "UK_MySchema_Point" UNIQUE ("Name", "Region", "LocationId")
);
GO

-- CONTAINS HISTORIC Point DATA
IF OBJECT_ID('MySchema.PointHistory', 'U') IS NULL
CREATE TABLE "MySchema"."PointHistory" (
"Id" BIGINT IDENTITY NOT NULL CONSTRAINT "PK_MySchema_PointHistory" PRIMARY KEY,
"ObjectId" BIGINT NOT NULL,
"Name" VARCHAR(64) NOT NULL,
"LocationId" SMALLINT NULL,
"Region" TINYINT NOT NULL
);
GO

CREATE TYPE "MySchema"."PointTable" AS TABLE (
"ObjectId"      BIGINT          NOT NULL PRIMARY KEY,
"PointName"     VARCHAR(64)     NOT NULL,
"Location"      VARCHAR(16)     NULL,
"Region"        VARCHAR(8)      NOT NULL,
UNIQUE ("PointName", "Region", "Location")
);
GO

DECLARE @p1 "MySchema"."PointTable";

insert into @p1 values(10001769996,N'ABCDEFGH',N'N/A',N'E')

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" = s."Region"
OUTPUT $action, inserted.*, deleted.*;

OUTPUTMaddeyi kaldırırsam hata oluşmaz. Ayrıca, deletedreferansı kaldırırsam hata oluşmaz. Bu nedenle, şu OUTPUTmaddeye ilişkin MSDN belgelerine baktım :

DELETED, INSERT deyiminde OUTPUT deyimi ile kullanılamaz.

Bana mantıklı geliyor, ancak asıl MERGEamacı, önceden bilmeyebilir olmanız.

Ayrıca, aşağıdaki komut, gerçekleştirilen işlemden bağımsız olarak mükemmel çalışır:

USE tempdb;
GO
CREATE TABLE dbo.Target(EmployeeID int, EmployeeName varchar(10), 
     CONSTRAINT Target_PK PRIMARY KEY(EmployeeID));
CREATE TABLE dbo.Source(EmployeeID int, EmployeeName varchar(10), 
     CONSTRAINT Source_PK PRIMARY KEY(EmployeeID));
GO
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(100, 'Mary');
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(101, 'Sara');
INSERT dbo.Target(EmployeeID, EmployeeName) VALUES(102, 'Stefano');

GO
INSERT dbo.Source(EmployeeID, EmployeeName) Values(103, 'Bob');
INSERT dbo.Source(EmployeeID, EmployeeName) Values(104, 'Steve');
GO
-- MERGE statement with the join conditions specified correctly.
USE tempdb;
GO
BEGIN TRAN;
MERGE Target AS T
USING Source AS S
ON (T.EmployeeID = S.EmployeeID) 
WHEN NOT MATCHED BY TARGET AND S.EmployeeName LIKE 'S%' 
    THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
WHEN MATCHED 
    THEN UPDATE SET T.EmployeeName = S.EmployeeName
WHEN NOT MATCHED BY SOURCE AND T.EmployeeName LIKE 'S%'
    THEN DELETE 
OUTPUT $action, inserted.*, deleted.*;
ROLLBACK TRAN;
GO 

Ayrıca, OUTPUThata yapanla aynı şekilde kullanan başka sorularım da var ve bunlar gayet iyi çalışıyor - aralarındaki tek fark, içinde yer alan tablolardır MERGE.

Bu bizim için üretimde büyük sorunlara neden oluyor. Bu hatayı hem VM'de hem de Fizikselde SQL2014 ve SQL2016'da 128GB RAM, 12 x 2.2GHZ Çekirdek, Windows Server 2012 R2 ile yeniden ürettim.

Sorgudan oluşturulan tahmini yürütme planı burada bulunabilir:

Tahmini Yürütme Planı


1
Sorgu tahmini bir plan oluşturabilir mi? (Ayrıca, bu pek çok insanı şok etmeyecek, ama yine de eski üst düzey metodolojiyi öneriyorum - bunun için MERGEyok HOLDLOCK, birincisi, bu yüzden yarış koşullarına karşı bağışıklık kazanmıyor ve hala dikkate alınması gereken başka hatalar da var. çözdükten sonra - veya rapor ettikten sonra - bu soruna neden olan ne olursa olsun.)
Aaron Bertrand

1
Bir erişim ihlali ile bir yığın dökümü verir. Burada yığını çözerken görebildiğim kadarıyla i.stack.imgur.com/f9aWa.png Bu sizin için büyük sorunlara neden oluyorsa, bunu Microsoft PSS ile yükseltmelisiniz. Özellikle deleted.ObjectId, soruna neden olduğu anlaşılıyor . OUTPUT $action, inserted.*, deleted.Name, deleted.LocationId, deleted.Regioniyi çalışıyor.
Martin Smith,

1
Martin ile aynı fikirde. Bu arada, MySchema.PointTabletürü kullanmayarak ve sadece çıplak bir VALUES()cümle ya da #temp tablosu ya da tablo değişkenini kullanarak sorunu önleyip kaçamayacağınıza bakın USING. Katkıda bulunan faktörlerin izole edilmesine yardımcı olabilir.
Aaron Bertrand

Yardımlarınız için teşekkürler, geçici bir tablo kullanmayı denedim ve aynı hata oluştu. Ürün desteği ile yükselteceğim - bu süre zarfında prod çalışmasını devam ettirmek için birleştirme kullanmama sorgusunu yeniden yazdım.
Mr.Brownstone

Yanıtlar:


20

Bu bir hatadır.

Bu ilişkilidir MERGEaçık Cadılar Bayramı Koruma kaçınmak ve katılmak ortadan kaldırmak için kullanılan e özgü luk doldurma optimizasyonlar ve güncellemeyle planı ile bu etkileşim özellikleri nasıl.

Makalemde, Cadılar Bayramı Problemi - Bölüm 3'teki optimizasyonlarla ilgili ayrıntılar var .

Özet, ardından aynı tabloda bir Birleştirme'nin ardından Ekle'dir :

Parçayı planla

Geçici Çözümler

Bu optimizasyonu yenmenin ve böylelikle böceği kaçınmanın birkaç yolu vardır.

  1. Açık Cadılar Bayramı Korumasını zorlamak için belgesiz bir izleme bayrağı kullanın:

    OPTION (QUERYTRACEON 8692);
  2. Maddeyi ONşuna değiştir:

    ON s."ObjectId" = t."ObjectId" + 0
  3. PointTableBirincil anahtarı aşağıdakilerle değiştirmek için tablo tipini değiştirin:

    ObjectID bigint NULL UNIQUE CLUSTERED CHECK (ObjectId IS NOT NULL)

    CHECKKısıtlama parçası bir birincil anahtarın ilk boş reddetme özelliği korumak için dahil, isteğe bağlıdır.

'Basit' güncelleme sorgusu işleme (yabancı anahtar denetimleri, benzersiz dizin bakımı ve çıktı sütunları), başlamak için yeterince karmaşıktır. Kullanarak buna MERGEbirkaç katman ekler. Bunu yukarıda belirtilen spesifik optimizasyonla birleştirin ve bunun gibi son durum hatalarıyla karşılaşmanın harika bir yolunu bulun.

Bildirilen uzun hata satırına bir tane daha eklendi MERGE.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.