sql >> Base de Datos >  >> RDS >> Database

Trace Flag 2389 y el nuevo Estimador de Cardinalidad

Una de las marcas de seguimiento de SQL Server que ha existido durante un tiempo es 2389. A menudo se discute con 2390, pero solo quiero centrarme en 2389 para esta publicación. El indicador de seguimiento se introdujo en SQL Server 2005 SP1, que se lanzó el 18 de abril de 2006 (según http://sqlserverbuilds.blogspot.co.uk/), por lo que existe desde hace más de 10 años. Los indicadores de seguimiento cambian el comportamiento del motor, y 2389 permite que el optimizador identifique las estadísticas que están ascendiendo y las marque como tales (a menudo llamado "el problema de la clave ascendente"). Cuando esto ocurre, las estadísticas se actualizarán automáticamente en el momento de la compilación de la consulta, lo que significa que el optimizador tiene información sobre el valor más alto de la tabla (en comparación con cuando no se usa el indicador de seguimiento).

Recientemente tuve una conversación con un cliente sobre el uso de esta marca de rastreo y surgió debido a este tipo de escenario:

  • Tiene una tabla grande que tiene un INT como clave principal y está agrupada.
  • Tiene un índice no agrupado que conduce a una columna DATETIME.
  • La tabla tiene alrededor de 20 millones de filas, y cada día se agregan entre 5000 y 100 000 filas.
  • Las estadísticas se actualizan todas las noches como parte de su tarea de mantenimiento.
  • Las estadísticas de actualización automática están habilitadas para la base de datos, pero incluso si se agregan 100 000 filas a la tabla, eso es mucho menos que los 4 millones de filas (20 %) que se necesitan para invocar una actualización automática.
  • Cuando los usuarios consultan la tabla utilizando la fecha en el predicado, el rendimiento de la consulta puede ser excelente o terrible.

Esa última viñeta casi hace que suene como un problema de sensibilidad de parámetros, pero no lo es. En este caso, es un problema de estadísticas. Mi sugerencia para el cliente fue usar TF 2389 o actualizar las estadísticas con más frecuencia durante el día (por ejemplo, a través de un trabajo de agente). Entonces pensé en hacer algunas pruebas, ya que el cliente ejecutaba SQL Server 2014. Aquí es donde las cosas se pusieron interesantes.

La configuración

Vamos a crear la tabla antes mencionada para realizar pruebas en la compilación RTM de SQL Server 2016, dentro de la base de datos de WideWorldImporters, y voy a establecer el modo de compatibilidad en 110 inicialmente:

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];

A continuación, cargaremos alrededor de 24 millones de filas en BigOrders y crearemos un índice no agrupado en 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);

Si revisamos el histograma para el índice no agrupado, vemos que la fecha más alta es 2016-05-31:

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


Estadísticas del NCI en OrderDate

Si buscamos una fecha posterior a esa, tenga en cuenta el número estimado de filas:

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


Planifique cuando consulte una fecha más allá de lo que está en el histograma

Es 1, porque el valor está fuera del histograma. Y en este caso, está bien, porque no hay filas en la tabla más allá del 31 de mayo de 2016. Pero agreguemos algunas y luego volvamos a ejecutar la misma consulta:

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 después de agregar filas después del 31 de mayo

El número estimado de filas sigue siendo 1. Pero aquí es donde las cosas se ponen interesantes. Cambiemos el modo de compatibilidad a 130 para usar el nuevo Estimador de cardinalidad y ver qué sucede.

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';


Planifique después de agregar filas para el 1 de junio, usando el nuevo CE

La forma de nuestro plan es la misma, pero ahora nuestra estimación es de 4898 filas. El nuevo CE trata los valores fuera del historial de forma diferente al antiguo CE. Entonces... ¿necesitamos siquiera rastrear el indicador 2389?

La Prueba - Parte I

Para la primera prueba, permaneceremos en el modo de compatibilidad 110 y ejecutaremos lo que veríamos con 2389. Al usar este indicador de seguimiento, puede habilitarlo como un parámetro de inicio en el servicio de SQL Server o puede usar DBCC. TRACEON para habilitarlo en toda la instancia. Comprenda que en su entorno de producción, si utiliza DBCC TRACEON para habilitar el indicador de seguimiento, cuando la instancia se reinicie, el indicador de seguimiento no tendrá efecto.

Con el indicador de rastreo habilitado, una estadística debe actualizarse tres (3) veces antes de que el optimizador la marque como ascendente. Forzaremos cuatro actualizaciones por si acaso y agregaremos más filas entre cada actualización.

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];

Si revisamos las estadísticas nuevamente y usamos el indicador de seguimiento 2388 para mostrar información adicional, vemos que la estadística ahora está marcada como Ascendente:

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


NCI en OrderDate marcado como ASC

Si consultamos una fecha futura, cuando las estadísticas estén completamente actualizadas, vemos que aún estima 1 fila:

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


Plan después de TF 2389 habilitado, pero sin filas más allá del histograma

Ahora agregaremos filas para el 5 de junio y volveremos a ejecutar la misma consulta:

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 después de TF 2389 habilitado, más de 70 000 filas añadidas más allá del histograma

Nuestra estimación ya no es 1, es 22.595. Ahora, solo por diversión, deshabilitemos el indicador de rastreo y veamos cuál es la estimación (voy a borrar el caché de procedimientos, ya que deshabilitar el indicador de rastreo no afectará lo que está actualmente en el caché).

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


Plan después de que TF 2389 esté *deshabilitado*, más de 70 000 filas añadidas histograma

Esta vez obtengo una estimación de 1 fila nuevamente. Aunque la estadística se marca como ascendente, si el indicador de seguimiento 2389 no está habilitado, solo estima 1 fila cuando consulta un valor fuera del histograma.

Hemos demostrado que la marca de rastreo 2389 hace lo que esperamos, lo que siempre ha hecho, cuando usamos el antiguo Estimador de cardinalidad. Ahora veamos qué pasa con el nuevo.

La Prueba - Parte II

Para ser exhaustivo, voy a restablecer todo. Volveré a crear la base de datos, estableceré el modo de compatibilidad en 130, cargaré los datos inicialmente, luego activaré el indicador de seguimiento 2389 y cargaré tres conjuntos de datos con actualizaciones de estadísticas en el medio.

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, entonces nuestros datos están completamente cargados. Si revisamos las estadísticas nuevamente y usamos el indicador de rastreo 2388 para mostrar información adicional, vemos que la estadística nuevamente se marca como Ascendente:

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


Estadística OrderDate de NCI marcada como ASC con TF 2389 y modo de compatibilidad 130

Ok, volvamos a consultar para el 5 de junio:

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


Plan con nuevo CE, sin filas más allá de lo que está en el histograma

Nuestra estimación es 4.922. No es exactamente lo que era en nuestra primera prueba, pero definitivamente no 1. Ahora agregaremos algunas filas para el 5 de junio y volveremos a consultar:

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';


Planifique con el nuevo CE, con más de 70 000 filas más allá de lo que está en el histograma

La estimación es la misma. Entonces, ¿qué sucede si desactivamos el indicador de rastreo 2389?

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


Plan con nuevo CE pero TF 2389 NO está habilitado, con 70K+ filas más allá de lo que está en el histograma

La estimación cambió ligeramente, a 4.930, pero cambió. Esto me dice que el indicador de rastreo 2389 tiene algún efecto en la estimación, pero se desconoce cuánto.

La Prueba - Parte III

Ejecuté una prueba final, donde restauré la base de datos, configuré el modo de compatibilidad en 130, cargué todos los datos nuevamente, actualicé las estadísticas varias veces, pero NO habilité el indicador de rastreo 2389. El código es el mismo que la Parte II, excepto por usar DBCC TRACEON para habilitar 2389. Cuando consulté el 5 de junio, tanto antes como después de agregar los datos, el número estimado de filas era 4920.

¿Qué significa?

Para resumir, cuando se usa el modo de compatibilidad 110 o inferior, el indicador de seguimiento 2389 funciona como siempre. Pero cuando se usa el modo de compatibilidad 120 o superior y, por lo tanto, el nuevo CE, las estimaciones no son son iguales en comparación con el antiguo CE y, en este caso específico, no son tan diferentes ya sea que se use el indicador de rastreo o no.

Entonces, ¿qué debería hacer? Prueba, como siempre. No he encontrado nada documentado en MSDN que indique que el indicador de rastreo 2389 no es compatible con el modo de compatibilidad 120 y superior, ni he encontrado nada que documente un cambio en el comportamiento. Sí me parece muy interesante que las estimaciones sean diferentes (en este caso mucho más bajas) con el nuevo CE. Eso podría ser potencialmente un problema, pero hay múltiples factores en juego cuando se trata de estimaciones, y esta fue una consulta muy simple (una tabla, un predicado). En este caso, la estimación está muy lejos (4920 filas frente a las 22 595 filas para la fecha del 5 de junio).

Si vuelvo a ejecutar la consulta para una fecha que tiene el mismo número de filas que es dentro del histograma, obtengo un plan similar, pero se ejecuta en paralelo:

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


Planifique una consulta que use una fecha dentro del histograma (nuevo CE, sin TF)

La estimación también es más precisa (68.318). El plan no cambia significativamente en este caso, pero el costo es obviamente mayor. En algún momento, dependiendo de la cantidad de filas que se devuelvan, esto podría derivar en un escaneo de tabla.

La mejor guía en este momento si está ejecutando 2014 o superior y el modo de compatibilidad 120 o superior, y tiene columnas principales en estadísticas que están ascendiendo, es probar. Si encuentra que el nuevo Cardinality Estimator no proporciona una estimación tan buena como el antiguo CE, le recomendaría que presente un elemento de conexión para que el equipo del producto esté al tanto. Siempre hay casos excepcionales y únicos, pero si muchos clientes (léase:USTED) encuentran constantemente el mismo comportamiento, y no es ideal, entonces es importante informar al equipo de desarrollo al respecto.

Este es otro elemento importante a tener en cuenta al actualizar a 2014 o 2016, y un recordatorio para no descuide sus pruebas (y, por cierto, Query Store sería extremadamente útil aquí con 2016). Háganlo amigos.