sql >> Database >  >> RDS >> Database

Traceervlag 2389 en de nieuwe kardinaliteitsschatter

Een van de SQL Server-traceervlaggen die al een tijdje bestaat, is 2389. Het wordt vaak besproken met 2390, maar ik wil me in dit bericht alleen concentreren op 2389. De traceringsvlag werd geïntroduceerd in SQL Server 2005 SP1, die op 18 april 2006 werd uitgebracht (volgens http://sqlserverbuilds.blogspot.co.uk/), dus het bestaat al meer dan 10 jaar. Traceervlaggen veranderen het gedrag van de engine en 2389 stelt de optimizer in staat om oplopende statistieken te identificeren en als zodanig te bestempelen (vaak "het oplopende sleutelprobleem" genoemd). Wanneer dit gebeurt, worden de statistieken automatisch bijgewerkt tijdens het compileren van de query, wat betekent dat de optimizer informatie heeft over de hoogste waarde in de tabel (vergeleken met wanneer de traceervlag niet wordt gebruikt).

Ik had onlangs een discussie met een klant over het gebruik van deze traceringsvlag en het kwam op vanwege dit type scenario:

  • Je hebt een grote tabel met een INT als primaire sleutel, en deze is geclusterd.
  • U heeft een niet-geclusterde index die leidt naar een DATETIME-kolom.
  • De tabel bevat ongeveer 20 miljoen rijen en er worden elke dag tussen de 5.000 en 100.000 rijen toegevoegd.
  • Statistieken worden elke nacht bijgewerkt als onderdeel van uw onderhoudstaak.
  • Statistieken voor automatisch bijwerken zijn ingeschakeld voor de database, maar zelfs als er 100.000 rijen aan de tabel worden toegevoegd, is dat veel minder dan de 4 miljoen rijen (20%) die nodig zijn om een ​​automatische update uit te voeren.
  • Als gebruikers een query uitvoeren op de tabel met de datum in het predikaat, kunnen de queryprestaties geweldig zijn, of verschrikkelijk.

Die laatste kogel laat het bijna klinken als een parametergevoeligheidsprobleem, maar dat is het niet. In dit geval is het een statistisch probleem. Mijn suggestie aan de klant was om TF 2389 te gebruiken, of de statistieken gedurende de dag vaker bij te werken (bijvoorbeeld via een Agent Job). Toen dacht ik dat ik wat testen zou doen, aangezien de client SQL Server 2014 draaide. Hier werd het interessant.

De installatie

We gaan de bovengenoemde tabel maken om te testen in de RTM-build van SQL Server 2016, binnen de WideWorldImporters-database, en ik ga de compatibiliteitsmodus in eerste instantie op 110 instellen:

USE [master];
GO
RESTORE DATABASE [WideWorldImporters]
FROM  DISK = N'C:\Backups\WideWorldImporters-Full.bak'
WITH  FILE = 1,
MOVE N'WWI_Primary' TO N'C:\Databases\WideWorldImporters\WideWorldImporters.mdf',
MOVE N'WWI_UserData' TO N'C:\Databases\WideWorldImporters\WideWorldImporters_UserData.ndf',
MOVE N'WWI_Log' TO N'C:\Databases\WideWorldImporters\WideWorldImporters.ldf',
MOVE N'WWI_InMemory_Data_1' TO N'C:\Databases\WideWorldImporters\WideWorldImporters_InMemory_Data_1',
NOUNLOAD, REPLACE, STATS = 5;
GO
 
ALTER DATABASE [WideWorldImporters] SET COMPATIBILITY_LEVEL = 110;
GO
 
USE [WideWorldImporters];
GO
 
CREATE TABLE [Sales].[BigOrders](
[OrderID] [int] NOT NULL,
[CustomerID] [int] NOT NULL,
[SalespersonPersonID] [int] NOT NULL,
[PickedByPersonID] [int] NULL,
[ContactPersonID] [int] NOT NULL,
[BackorderOrderID] [int] NULL,
[OrderDate] [date] NOT NULL,
[ExpectedDeliveryDate] [date] NOT NULL,
[CustomerPurchaseOrderNumber] [nvarchar](20) NULL,
[IsUndersupplyBackordered] [bit] NOT NULL,
[Comments] [nvarchar](max) NULL,
[DeliveryInstructions] [nvarchar](max) NULL,
[InternalComments] [nvarchar](max) NULL,
[PickingCompletedWhen] [datetime2](7) NULL,
[LastEditedBy] [int] NOT NULL,
[LastEditedWhen] [datetime2](7) NOT NULL,
CONSTRAINT [PK_Sales_BigOrders] PRIMARY KEY CLUSTERED
(
[OrderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [USERDATA]
) ON [USERDATA] TEXTIMAGE_ON [USERDATA];

Vervolgens gaan we ongeveer 24 miljoen rijen in BigOrders laden en een niet-geclusterde index maken op OrderDate.

SET NOCOUNT ON;
 
DECLARE @Loops SMALLINT = 0, @IDIncrement INT = 75000;
 
WHILE @Loops < 325 -- adjust this to increase or decrease the number of rows added
BEGIN
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + @IDIncrement,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
 
CHECKPOINT;
 
SET @Loops = @Loops + 1;
SET @IDIncrement = @IDIncrement + 75000;
END
 
CREATE NONCLUSTERED INDEX [NCI_BigOrders_OrderDate]
ON [Sales].[BigOrders] ([OrderDate], CustomerID);

Als we het histogram voor de niet-geclusterde index controleren, zien we dat de hoogste datum 2016-05-31 is:

DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);


Statistieken voor de NCI op OrderDate

Als we vragen naar een datum daarna, noteer dan het geschatte aantal rijen:

SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-01';


Plan bij het zoeken naar een datum die verder gaat dan wat er in het histogram staat

Het is 1, omdat de waarde buiten het histogram ligt. En in dit geval is dat oké, want na 31 mei 2016 zijn er geen rijen meer in de tabel. Maar laten we er een paar toevoegen en dezelfde zoekopdracht opnieuw uitvoeren:

INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25000000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-01',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-01';


Plan na het toevoegen van rijen na 31 mei

Het geschatte aantal rijen is nog steeds 1. Maar hier wordt het interessant. Laten we de compatibiliteitsmodus wijzigen in 130, zodat we de nieuwe Cardinality Estimator gebruiken en kijken wat er gebeurt.

USE [master];
GO
 
ALTER DATABASE [WideWorldImporters] SET COMPATIBILITY_LEVEL = 130
GO
 
USE [WideWorldImporters];
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-01';


Plan na het toevoegen van rijen voor 1 juni met de nieuwe CE

Onze planvorm is hetzelfde, maar nu is onze schatting 4.898 rijen. De nieuwe CE behandelt waarden buiten de geschiedenis anders dan de oude CE. Dus... hebben we zelfs traceervlag 2389 nodig?

De Test – Deel I

Voor de eerste test blijven we in compatibiliteitsmodus 110 en doorlopen we wat we zouden zien met 2389. Als u deze traceringsvlag gebruikt, kunt u deze inschakelen als opstartparameter in de SQL Server-service, of u kunt DBCC gebruiken TRACEON om het instantiebreed in te schakelen. Begrijp dat in uw productieomgeving, als u DBCC TRACEON gebruikt om de traceringsvlag in te schakelen, wanneer de instantie opnieuw wordt opgestart, de traceervlag niet van kracht is.

Als de traceervlag is ingeschakeld, moet een statistiek drie (3) keer worden bijgewerkt voordat de optimizer deze als oplopend markeert. We forceren vier updates voor een goede maatregel en voegen meer rijen toe tussen elke update.

USE [master];
GO
 
ALTER DATABASE [WideWorldImporters] SET COMPATIBILITY_LEVEL = 110;
GO
 
DBCC TRACEON (2389, -1);
GO
 
USE [WideWorldImporters];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25100000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-02',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy]
[LastEditedWhen]
)
SELECT
[OrderID] + 25200000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-03',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25300000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-04',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];

Als we de statistieken opnieuw controleren en de traceringsvlag 2388 gebruiken om aanvullende informatie weer te geven, zien we dat de statistiek nu is gemarkeerd als Oplopend:

DBCC TRACEON (2388);
GO
 
DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);


NCI op OrderDate gemarkeerd als ASC

Als we naar een toekomstige datum vragen, wanneer de statistieken volledig up-to-date zijn, zien we dat deze nog steeds 1 rij schat:

SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan na TF 2389 ingeschakeld, maar geen rijen buiten histogram

Nu voegen we rijen toe voor 5 juni en voeren we dezelfde zoekopdracht opnieuw uit:

INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25400000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-05',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan na TF 2389 ingeschakeld, 70K+ rijen toegevoegd buiten histogram

Onze schatting is niet langer 1, maar 22.595. Laten we nu voor de lol de traceringsvlag uitschakelen en kijken wat de schatting is (ik ga de procedurecache wissen, omdat het uitschakelen van de traceringsvlag geen invloed heeft op wat zich momenteel in de cache bevindt).

DBCC TRACEOFF (2389, -1);
GO
 
DBCC FREEPROCCACHE;
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan nadat TF 2389 *uitgeschakeld* is, 70K+ rijen daarna toegevoegd histogram

Deze keer krijg ik weer een schatting van 1 rij. Ook al is de statistiek oplopend gemarkeerd, als traceringsvlag 2389 niet is ingeschakeld, wordt er slechts één rij geschat wanneer u naar een waarde buiten het histogram zoekt.

We hebben aangetoond dat traceringsvlag 2389 doet wat we verwachten - wat het altijd heeft gedaan - bij het gebruik van de oude Cardinality Estimator. Laten we nu eens kijken wat er gebeurt met de nieuwe.

De Test – Deel II

Om grondig te zijn, ga ik alles resetten. Ik zal de database opnieuw maken, de compatibiliteitsmodus instellen op 130, de gegevens in eerste instantie laden, vervolgens traceringsvlag 2389 inschakelen en drie sets gegevens laden met tussenliggende statistieken.

USE [master];
GO
 
RESTORE DATABASE [WideWorldImporters]
FROM  DISK = N'C:\Backups\WideWorldImporters-Full.bak'
WITH  FILE = 1,
MOVE N'WWI_Primary' TO N'C:\Databases\WideWorldImporters\WideWorldImporters.mdf',
MOVE N'WWI_UserData' TO N'C:\Databases\WideWorldImporters\WideWorldImporters_UserData.ndf',
MOVE N'WWI_Log' TO N'C:\Databases\WideWorldImporters\WideWorldImporters.ldf',
MOVE N'WWI_InMemory_Data_1' TO N'C:\Databases\WideWorldImporters\WideWorldImporters_InMemory_Data_1',
NOUNLOAD, REPLACE, STATS = 5;
GO
 
USE [master];
GO
 
ALTER DATABASE [WideWorldImporters] SET COMPATIBILITY_LEVEL = 130;
GO
 
USE [WideWorldImporters];
GO
 
CREATE TABLE [Sales].[BigOrders](
[OrderID] [int] NOT NULL,
[CustomerID] [int] NOT NULL,
[SalespersonPersonID] [int] NOT NULL,
[PickedByPersonID] [int] NULL,
[ContactPersonID] [int] NOT NULL,
[BackorderOrderID] [int] NULL,
[OrderDate] [date] NOT NULL,
[ExpectedDeliveryDate] [date] NOT NULL,
[CustomerPurchaseOrderNumber] [nvarchar](20) NULL,
[IsUndersupplyBackordered] [bit] NOT NULL,
[Comments] [nvarchar](max) NULL,
[DeliveryInstructions] [nvarchar](max) NULL,
[InternalComments] [nvarchar](max) NULL,
[PickingCompletedWhen] [datetime2](7) NULL,
[LastEditedBy] [int] NOT NULL,
[LastEditedWhen] [datetime2](7) NOT NULL,
CONSTRAINT [PK_Sales_BigOrders] PRIMARY KEY CLUSTERED
(
[OrderID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
ALLOW_PAGE_LOCKS = ON) ON [USERDATA]
) ON [USERDATA] TEXTIMAGE_ON [USERDATA];
GO
 
SET NOCOUNT ON;
 
DECLARE @Loops SMALLINT = 0;
DECLARE @IDIncrement INT = 75000;
 
WHILE @Loops < 325 -- adjust this to increase or decrease the number of rows added
BEGIN
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + @IDIncrement,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
 
CHECKPOINT;
 
SET @Loops = @Loops + 1;
SET @IDIncrement = @IDIncrement + 75000;
END
 
CREATE NONCLUSTERED INDEX [NCI_BigOrders_OrderDate]
ON [Sales].[BigOrders] ([OrderDate], CustomerID);
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25000000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-01',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
DBCC TRACEON (2389, -1);
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25100000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-02',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25200000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-03',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];
GO
 
INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25300000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-04',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
UPDATE STATISTICS [Sales].[BigOrders] [NCI_BigOrders_OrderDate];

Ok, dus onze gegevens zijn volledig geladen. Als we de statistieken opnieuw controleren en de traceringsvlag 2388 gebruiken om aanvullende informatie weer te geven, zien we dat de statistiek opnieuw wordt gemarkeerd als Oplopend:

DBCC TRACEON (2388);
GO
 
DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);


NCI OrderDate-statistieken gemarkeerd als ASC met TF 2389 en compatibiliteitsmodus 130

Oké, dus laten we opnieuw vragen voor 5 juni:

SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan met nieuwe CE, geen rijen verder dan wat er in het histogram staat

Onze schatting is 4.922. Niet helemaal wat het was in onze eerste test, maar zeker niet 1. Nu zullen we enkele rijen toevoegen voor 5 juni en opnieuw een zoekopdracht uitvoeren:

INSERT [Sales].[BigOrders]
( [OrderID],
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
[OrderDate],
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
)
SELECT
[OrderID] + 25400000,
[CustomerID],
[SalespersonPersonID],
[PickedByPersonID],
[ContactPersonID],
[BackorderOrderID],
'2016-06-05',
[ExpectedDeliveryDate],
[CustomerPurchaseOrderNumber],
[IsUndersupplyBackordered],
[Comments],
[DeliveryInstructions],
[InternalComments],
[PickingCompletedWhen],
[LastEditedBy],
[LastEditedWhen]
FROM [Sales].[Orders];
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan met nieuwe CE, met 70K+ rijen voorbij wat er in het histogram staat

De schatting is hetzelfde. Dus wat als we traceringsvlag 2389 uitzetten?

DBCC TRACEOFF (2389, -1);
GO
 
DBCC FREEPROCCACHE;
GO
 
SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-05';


Plan met nieuwe CE maar TF 2389 is NIET ingeschakeld, met 70K+ rijen voorbij wat er in het histogram staat

De schatting veranderde iets, tot 4.930, maar het veranderde. Dit vertelt me ​​dat traceringsvlag 2389 enig effect heeft op de schatting, maar hoeveel is onbekend.

De Test – Deel III

Ik heb nog een laatste test uitgevoerd, waarbij ik de database heb hersteld, de compatibiliteitsmodus op 130 heb gezet, alle gegevens opnieuw heb geladen, statistieken meerdere keren heb bijgewerkt, maar traceringsvlag 2389 NIET heb ingeschakeld. De code is hetzelfde als deel II, behalve voor het gebruik DBCC TRACEON om 2389 in te schakelen.  Toen ik op 5 juni vroeg, zowel voor als na het toevoegen van de gegevens, was het geschatte aantal rijen 4.920.

Wat betekent het?

Om samen te vatten, bij gebruik van compatibiliteitsmodus 110 of lager, werkt traceringsvlag 2389 zoals altijd. Maar bij gebruik van compatibiliteitsmodus 120 of hoger, en dus de nieuwe CE, zijn de schattingen niet hetzelfde in vergelijking met de oude CE, en in dit specifieke geval zijn ze niet zo verschillend, of de traceervlag nu wordt gebruikt of niet.

Dus wat moet je doen? Testen, zoals altijd. Ik heb niets gevonden in MSDN dat aangeeft dat traceringsvlag 2389 niet wordt ondersteund met compatibiliteitsmodus 120 en hoger, en ik heb ook niets gevonden dat een gedragsverandering documenteert. Ik vind het wel heel interessant dat de schattingen anders zijn (in dit geval veel lager) met de nieuwe CE. Dat kan mogelijk een probleem zijn, maar er zijn meerdere factoren in het spel als het gaat om schattingen, en dit was een heel eenvoudige vraag (één tabel, één predikaat). In dit geval is de schatting ver weg (4920 rijen versus de 22.595 rijen voor de datum van 5 juni).

Als ik de query opnieuw uitvoer voor een datum met hetzelfde aantal rijen als is binnen het histogram krijg ik een soortgelijk plan, maar het loopt parallel:

SELECT CustomerID, OrderID, SalespersonPersonID
FROM [Sales].[BigOrders]
WHERE [OrderDate] = '2016-06-02';


Plan een zoekopdracht die een datum in het histogram gebruikt (nieuw CE, geen TF)

De schatting is ook nauwkeuriger (68.318). Het plan verandert in dit geval niet significant, maar de kosten zijn duidelijk hoger. Op een gegeven moment, afhankelijk van het aantal rijen dat zou worden geretourneerd, kan dit leiden tot een tabelscan.

De beste richtlijn op dit moment als je 2014 of hoger en compatibiliteitsmodus 120 of hoger gebruikt, en je hebt leidende kolommen in statistieken die oplopend zijn, is om te testen. Als u merkt dat de nieuwe kardinaliteitschatter niet zo'n goede schatting geeft als de oude CE, raad ik u aan een Connect-item in te dienen, zodat het productteam hiervan op de hoogte is. Er zijn altijd eenmalige en unieke gevallen, maar als veel klanten (lees:JIJ) consequent hetzelfde gedrag vinden – en dat is niet ideaal – dan is het belangrijk om het ontwikkelteam hiervan op de hoogte te stellen.

Dit is een ander belangrijk punt om te overwegen bij het upgraden naar 2014 of 2016 – en een herinnering om niet negeer je testen (en trouwens, Query Store zou hier met 2016 buitengewoon handig zijn). Kom tot het vrienden.


  1. Uw database upgraden naar PostgreSQL versie 10 - wat u moet weten

  2. PGError:FOUT:toestemming geweigerd voor relatie (bij gebruik van Heroku)

  3. Hoe een back-up te maken en te herstellen (exporteren en importeren) MySQL-databases Zelfstudie

  4. Wat is beter in MYSQL count(*) of count(1)?