Uno dei flag di traccia di SQL Server in circolazione da un po' di tempo è 2389. Viene spesso discusso con 2390, ma voglio solo concentrarmi su 2389 per questo post. Il flag di traccia è stato introdotto in SQL Server 2005 SP1, che è stato rilasciato il 18 aprile 2006 (secondo http://sqlserverbuilds.blogspot.co.uk/), quindi è in circolazione da oltre 10 anni. I flag di traccia modificano il comportamento del motore e 2389 consente all'ottimizzatore di identificare le statistiche in aumento e di contrassegnarle come tali (spesso chiamato "il problema della chiave ascendente"). Quando ciò si verifica, le statistiche verranno aggiornate automaticamente al momento della compilazione della query, il che significa che l'ottimizzatore ha informazioni sul valore più alto nella tabella (rispetto a quando non viene utilizzato il flag di traccia).
Di recente ho avuto una discussione con un cliente sull'utilizzo di questo flag di traccia ed è emerso a causa di questo tipo di scenario:
- Hai una tabella di grandi dimensioni che ha un INT come chiave primaria ed è raggruppata.
- Hai un indice non cluster che conduce su una colonna DATETIME.
- La tabella contiene circa 20 milioni di righe e ogni giorno vengono aggiunte da 5.000 a 100.000 righe.
- Le statistiche vengono aggiornate ogni notte come parte della tua attività di manutenzione.
- Le statistiche di aggiornamento automatico sono abilitate per il database, ma anche se vengono aggiunte 100.000 righe alla tabella, è molto meno dei 4 milioni di righe (20%) necessari per richiamare un aggiornamento automatico.
- Quando gli utenti eseguono query sulla tabella utilizzando la data nel predicato, le prestazioni delle query possono essere ottime o pessime.
L'ultimo proiettile sembra quasi un problema di sensibilità ai parametri, ma non lo è. In questo caso, è un problema di statistiche. Il mio suggerimento al cliente era di utilizzare TF 2389 o aggiornare le statistiche più frequentemente durante il giorno (ad esempio tramite un lavoro dell'agente). Poi ho pensato di fare dei test, dal momento che il client eseguiva SQL Server 2014. È qui che le cose si sono fatte interessanti.
L'impostazione
Creeremo la tabella di cui sopra per i test nella build RTM di SQL Server 2016, all'interno del database WideWorldImporters, e inizialmente imposterò la modalità di compatibilità su 110:
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];
Successivamente caricheremo circa 24 milioni di righe in BigOrders e creeremo un indice non cluster su 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);
Se controlliamo l'istogramma per l'indice non cluster, vediamo che la data più alta è 31-05-2016:
DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);
Statistiche per NCI su OrderDate
Se interroghiamo una data successiva a quella, prendi nota del numero stimato di righe:
SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-01';
Pianifica quando esegui query per una data oltre a quella presente nell'istogramma
È 1, perché il valore è al di fuori dell'istogramma. E in questo caso, va bene, perché non ci sono righe nella tabella oltre il 31 maggio 2016. Ma aggiungiamone alcune e poi rieseguiamo la stessa query:
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';
Pianifica dopo aver aggiunto righe dopo il 31 maggio
Il numero stimato di righe è ancora 1. Ma è qui che le cose si fanno interessanti. Cambiamo la modalità di compatibilità a 130 in modo da utilizzare il nuovo stimatore di cardinalità e vedere cosa succede.
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';
Pianifica dopo aver aggiunto le righe per il 1 giugno, utilizzando il nuovo CE
La forma del nostro piano è la stessa, ma ora la nostra stima è di 4.898 righe. Il nuovo CE tratta i valori al di fuori della storia in modo diverso rispetto al vecchio CE. Quindi... abbiamo anche bisogno del flag di traccia 2389?
Il test – Parte I
Per il primo test, rimarremo in modalità di compatibilità 110 ed eseguiremo ciò che vedremmo con 2389. Quando si utilizza questo flag di traccia, è possibile abilitarlo come parametro di avvio nel servizio SQL Server oppure è possibile utilizzare DBCC TRACEON per abilitarlo a livello di istanza. Tieni presente che nel tuo ambiente di produzione, se utilizzi DBCC TRACEON per abilitare il flag di traccia, al riavvio dell'istanza il flag di traccia non sarà attivo.
Con il flag di traccia abilitato, una statistica deve essere aggiornata tre (3) volte prima che l'ottimizzatore la marchi come crescente. Forzeremo quattro aggiornamenti per buona misura e aggiungeremo più righe tra ogni aggiornamento.
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];
Se controlliamo nuovamente le statistiche e utilizziamo il flag di traccia 2388 per visualizzare informazioni aggiuntive, vediamo che la statistica è ora contrassegnata come Crescente:
DBCC TRACEON (2388); GO DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);
NCI su OrderDate contrassegnato come ASC
Se eseguiamo una query per una data futura, quando le statistiche sono completamente aggiornate, vediamo che stima ancora 1 riga:
SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-05';
Piano dopo TF 2389 abilitato, ma nessuna riga oltre l'istogramma
Ora aggiungeremo righe per il 5 giugno ed eseguiremo di nuovo la stessa query:
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';
Pianifica dopo TF 2389 abilitato, oltre 70.000 righe aggiunte oltre l'istogramma
La nostra stima non è più 1, è 22.595. Ora, solo per divertimento, disabilitiamo il flag di traccia e vediamo qual è la stima (svuoterò la cache delle procedure, poiché la disabilitazione del flag di traccia non influirà su ciò che è attualmente nella cache).
DBCC TRACEOFF (2389, -1); GO DBCC FREEPROCCACHE; GO SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-05';
Piano dopo che TF 2389 è *disabilitato*, oltre 70.000 righe aggiunte istogramma
Questa volta ottengo di nuovo una stima di 1 riga. Anche se la statistica è contrassegnata come crescente, se il flag di traccia 2389 non è abilitato, stima solo 1 riga quando esegui una query per un valore al di fuori dell'istogramma.
Abbiamo dimostrato che il flag di traccia 2389 fa ciò che ci aspettiamo, ciò che ha sempre fatto, quando si utilizza il vecchio stimatore di cardinalità. Ora vediamo cosa succede con quello nuovo.
Il test – Parte II
Per essere approfondito, ripristinerò tutto. Creerò di nuovo il database, imposterò la modalità di compatibilità su 130, caricherò inizialmente i dati, quindi accenderò il flag di traccia 2389 e caricherò tre set di dati con gli aggiornamenti delle statistiche nel mezzo.
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, quindi i nostri dati sono completamente caricati. Se controlliamo nuovamente le statistiche e utilizziamo il flag di traccia 2388 per visualizzare informazioni aggiuntive, vediamo che la statistica è nuovamente contrassegnata come Crescente:
DBCC TRACEON (2388); GO DBCC SHOW_STATISTICS ('Sales.BigOrders',[NCI_BigOrders_OrderDate]);
Statistica NCI OrderDate contrassegnata come ASC con TF 2389 e modalità di compatibilità 130
Ok, quindi interroghiamo di nuovo per il 5 giugno:
SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-05';
Pianifica con il nuovo CE, senza righe oltre a ciò che è nell'istogramma
La nostra stima è 4.922. Non esattamente quello che era nel nostro primo test, ma sicuramente non 1. Ora aggiungeremo alcune righe per il 5 giugno e ripetiamo la query:
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';
Pianifica con il nuovo CE, con oltre 70.000 righe oltre l'istogramma
La stima è la stessa. Quindi ora, cosa succede se disattiviamo il flag di traccia 2389?
DBCC TRACEOFF (2389, -1); GO DBCC FREEPROCCACHE; GO SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-05';
Piano con il nuovo CE ma TF 2389 NON è abilitato, con 70K+ righe oltre ciò che è nell'istogramma
La stima è leggermente cambiata, a 4.930, ma è cambiata. Questo mi dice che il flag di traccia 2389 ha qualche effetto sulla stima, ma quanto è sconosciuto.
Il test – Parte III
Ho eseguito un test finale, in cui ho ripristinato il database, impostato la modalità di compatibilità su 130, caricato di nuovo tutti i dati, aggiornato le statistiche più volte, ma NON ho abilitato il flag di traccia 2389. Il codice è lo stesso della Parte II, ad eccezione dell'utilizzo DBCC TRACEON per abilitare 2389. Quando ho eseguito query per il 5 giugno, sia prima che dopo l'aggiunta dei dati, il numero stimato di righe era 4.920.
Cosa significa?
Per riassumere, quando si utilizza la modalità di compatibilità 110 o inferiore, il flag di traccia 2389 funziona come sempre. Ma quando si utilizza la modalità di compatibilità 120 o superiore, e quindi il nuovo CE, le stime non gli stessi rispetto al vecchio CE e, in questo caso specifico, non sono poi così diversi indipendentemente dal fatto che si utilizzi o meno il flag di traccia.
Allora cosa dovresti fare? Prova, come sempre. Non ho trovato nulla di documentato in MSDN che affermi che il flag di traccia 2389 non è supportato con la modalità di compatibilità 120 e successive, né ho trovato nulla che documenti un cambiamento nel comportamento. Trovo molto interessante che le stime siano diverse (in questo caso molto più basse) con il nuovo CE. Questo potrebbe potenzialmente essere un problema, ma ci sono più fattori in gioco quando si tratta di stime, e questa era una query molto semplice (una tabella, un predicato). In questo caso, la stima è molto lontana (4920 righe contro le 22.595 per la data del 5 giugno).
Se eseguo nuovamente la query per una data che ha lo stesso numero di righe che è all'interno dell'istogramma, ottengo un piano simile, ma viene eseguito in parallelo:
SELECT CustomerID, OrderID, SalespersonPersonID FROM [Sales].[BigOrders] WHERE [OrderDate] = '2016-06-02';
Pianifica una query che utilizzi una data all'interno dell'istogramma (nuovo CE, no TF)
Più precisa è anche la stima (68.318). Il piano in questo caso non cambia significativamente, ma il costo è ovviamente più alto. Ad un certo punto, a seconda del numero di righe che verrebbero restituite, ciò potrebbe causare una scansione della tabella.
La migliore guida in questo momento se stai utilizzando la versione 2014 o successiva e la modalità di compatibilità 120 o successiva e hai colonne iniziali nelle statistiche in aumento, è eseguire il test. Se ritieni che il nuovo Strumento per la stima della cardinalità non fornisca una stima valida come il vecchio CE, ti consiglio di presentare un articolo Connect in modo che il team del prodotto ne sia a conoscenza. Ci sono sempre casi unici e una tantum, ma se molti clienti (leggi:TU) riscontrano costantemente lo stesso comportamento - e non è l'ideale - allora è importante informarne il team di sviluppo.
Questo è un altro elemento importante da considerare durante l'aggiornamento al 2014 o al 2016 e un promemoria per non trascura i tuoi test (e, a proposito, Query Store sarebbe estremamente utile qui con il 2016). Andateci amici.