You are on page 1of 12

Capítulo 1

Técnicas avanzadas de consulta

Objetivo

Al finalizar el capítulo, el alumno:

 Identificar cómo opera SQL Server 2016 en un SELECT.


 Identificar diferentes técnicas de escritura de consultas avanzadas.
 Usar vistas y funciones de metadatos para administrar la base de datos.

Temas

1. Uso avanzado de SELECT


2. Uso de sub-consultas
3. Expresiones de tabla
4. Operadores de conjuntos
5. Funciones de ventana
6. Uso de los metadatos de SQL Server

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 2

1. Uso avanzado de SELECT

1.1 Pasos
Para poder simular una carga de trabajo pesada en SQL Server, se usan sentencias que
contienen una combinación de malas prácticas.

Es recomendable, realizar un nuevo restore de la base de datos AdventureWorks2008R2,


esto se realizó en el ejercicio inicial del laboratorio 1 del capítulo 1.

La carga de trabajo es simulada por los siguientes procedimientos almacenados, estos


deben ser ejecutados en el siguiente orden:

USE AdventureWorks2008R2;
GO
CREATE PROCEDURE dbo.spr_ShoppingCart
@ShoppingCartId VARCHAR(50)
AS
SELECT sci.Quantity,
p.ListPrice,
p.ListPrice * sci.Quantity AS LineTotal,
p.[Name]
FROM Sales.ShoppingCartItem AS sci
JOIN Production.Product AS p
ON sci.ProductID = p.ProductID
WHERE sci.ShoppingCartID = @ShoppingCartId ;
GO

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 3

CREATE PROCEDURE dbo.spr_ProductBySalesOrder @SalesOrderID INT


AS
SELECT ROW_NUMBER() OVER (ORDER BY sod.ModifiedDate) AS LineNumber,
p.[Name],
sod.LineTotal
FROM Sales.SalesOrderHeader AS soh
JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
JOIN Production.Product AS p
ON sod.ProductID = p.ProductID
WHERE soh.SalesOrderID = @SalesOrderID
ORDER BY p.[Name] ASC ;
GO
CREATE PROCEDURE dbo.spr_PersonByFirstName
@FirstName NVARCHAR(50)
AS

SELECT p.BusinessEntityID,
p.Title,
p.LastName,
p.FirstName,
p.PersonType
FROM Person.Person AS p
WHERE p.FirstName = @FirstName ;
GO
CREATE PROCEDURE dbo.spr_ProductTransactionsSinceDate
@LatestDate DATETIME,
@ProductName NVARCHAR(50)
AS
SELECT p.Name,
th.ReferenceOrderID,
th.ReferenceOrderLineID,
th.TransactionType,
th.Quantity
FROM Production.Product AS p
JOIN Production.TransactionHistory AS th
ON p.ProductID = th.ProductID AND
th.TransactionID = (SELECT TOP (1)
th2.TransactionID
FROM Production.TransactionHistory th2
WHERE th2.ProductID = p.ProductID
ORDER BY th2.TransactionID DESC
)
WHERE th.TransactionDate > @LatestDate AND
p.Name LIKE @ProductName ;
GO
CREATE PROCEDURE dbo.spr_PurchaseOrderBySalesPersonName @LastName
NVARCHAR(50)
AS
SELECT poh.PurchaseOrderID,
poh.OrderDate,
pod.LineTotal,
p.[Name] AS ProductName,
e.JobTitle,
per.LastName + ', ' + per.FirstName AS SalesPerson
FROM Purchasing.PurchaseOrderHeader AS poh
JOIN Purchasing.PurchaseOrderDetail AS pod
ON poh.PurchaseOrderID = pod.PurchaseOrderID

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 4

JOIN Production.Product AS p
ON pod.ProductID = p.ProductID
JOIN HumanResources.Employee AS e
ON poh.EmployeeID = e.BusinessEntityID
JOIN Person.Person AS per
ON e.BusinessEntityID = per.BusinessEntityID
WHERE per.LastName LIKE @LastName
ORDER BY per.LastName,
per.FirstName ;
GO

Ahora, se ejecuta cada uno de ellos con los siguientes parámetros:

EXEC dbo.spr_ShoppingCart
'20621' ;
EXEC dbo.spr_ProductBySalesOrder
43867 ;
EXEC dbo.spr_PersonByFirstName
'Gretchen' ;
EXEC dbo.spr_ProductTransactionsSinceDate
@LatestDate = '9/1/2004',
@ProductName = 'Hex Nut%' ;
EXEC dbo.spr_PurchaseOrderBySalesPersonName
@LastName = 'Hill%' ;

Luego, se ejecutan los siguientes pasos para la optimización:

1.2.1 Capturar la carga de trabajo

Para capturar esta información, utilizar el Profiler y los siguientes eventos:

Categoría: Execution
Event: rpc Completed
sql_batch_completed

Filtro: Solo la BD AdventureWorks2008R2

Iniciar el trace y volver a ejecutar los procedimientos almacenados:

EXEC dbo.spr_ShoppingCart
'20621' ;
EXEC dbo.spr_ProductBySalesOrder
43867 ;
EXEC dbo.spr_PersonByFirstName
'Gretchen' ;
EXEC dbo.spr_ProductTransactionsSinceDate
@LatestDate = '9/1/2004',
@ProductName = 'Hex Nut%' ;
EXEC dbo.spr_PurchaseOrderBySalesPersonName
@LastName = 'Hill%' ;

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 5

Guardar la información obtenida por el profiler en el siguiente directorio:

c:\traces\WorkLoadTrace.trc

Si la carpeta Traces no existe en dicha unidad, crearla.

1.2.2 Analizar la carga de trabajo


Una vez que la carga de trabajo ha sido registrada en un archivo, debe ser
analizado, navegando a través de la data, para ello, se podrá utilizar la siguiente
función:

SELECT * FROM ::fn_trace_gettable('c:\traces\WorkLoadTrace.trc', 1)

Realizar los ordenamientos respectivos para conocer los queries con mayor
duración, para ello, ordenar la sentencia anterior:

SELECT * FROM ::fn_trace_gettable('c:\traces\WorkLoadTrace.trc', 1) as ee


ORDER BY ee.Duration DESC ;

Sin embargo, puede ser más adecuado en un entorno de producción donde se


ejecutan los mismos procedimientos en repetidas ocasiones, obtener promedios,
por ejemplo:

SELECT ee.BatchText,
SUM(Duration) AS SumDuration,AVG(Duration) AS AvgDuration,
COUNT(Duration) AS CountDuration
::fn_trace_gettable('c:\traces\WorkLoadTrace.trc', 1) as ee
GROUP BY ee.BatchText ;

1.2.3 Identificar los queries más costosos y que demoran más en ejecutarse.

Para esto, ordenar los queries de la carga de trabajo por CPU, Reads, Writes y
Duration, para identificar los queries más costosos.

1.2.4 Determinar el uso de recursos en general

Esto proporciona una figura, de la cantidad de recursos de hardware consumidos


por los peores queries, para ello, se debe registrar esta información en una tabla,
para comparar los resultados, una vez realizado el afinamiento.

Para esto, ubicar un query y considerar la siguiente información:

SELECT * FROM ::fn_trace_gettable('c:\traces\WorkLoadTrace.trc', 1) as ee

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 6

Ubicar las columnas:

LogicalReads: indica el número de lecturas lógicas realizadas por un query, si


una página no es encontrada en memoria, entonces una lectura lógica para la
página necesitará una lectura física del disco para llenar la página en la memoria.

Writes: número de páginas modificadas por el query

CPU: cuánto tiempo el CPU fue utilizado por un query (en ms)

Duration: el tiempo en ms que le tomó al SQL Server procesar el query

1.2.5 Compilar información detallada de uso de recursos

Se puede disgregar el uso de recursos del punto anterior para poder ubicar al
cuello de botella, para lo cual se debe obtener información más detallada para
determinar qué acceso a tabla es más problemática.

Se puede obtener el número de lecturas realizadas individualmente, en cada tabla


usando STATISTICS IO, para ello:

Ejecutar lo siguiente:

DBCC FREEPROCCACHE() ;
DBCC DROPCLEANBUFFERS ;
GO
SET STATISTICS TIME ON ;
GO
SET STATISTICS IO ON ;
GO
EXEC dbo.spr_PurchaseOrderBySalesPersonName
@LastName = 'Hill%' ;
GO
SET STATISTICS TIME OFF ;
GO
SET STATISTICS IO OFF ;
GO

Ahora, analizar la salida, registrando la siguiente información:

A nivel de Statistics IO

Tabla Logical Reads


Purchasing.PurchaseOrderDetail
Purchasing.PurchaseOrderHeader
Person.Employee
Person.Person
Production.Product

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 7

A nivel de Statistics Time

Evento Duración CPU


Compile
Execution
Completion

1.2.6 Analizar la efectividad de las estadísticas

Las estadísticas son una de las principales piezas clave para que el Query Optimizer
pueda generar un plan de ejecución más adecuado.

Se puede revisar la información estadística de una tabla y sus índices con DBCC
SHOW_STATISTICS, por ejemplo, para la tabla Purchasing.PurchaseOrderHeader

DBCC SHOW_STATISTICS('Purchasing.PurchaseOrderHeader',
'PK_PurchaseOrderHeader_PurchaseOrderID') ;

Repetir los mismos pasos para cada una de las tablas involucradas.

1.2.7 Analizar la necesidad de defragmentación

Para poder determinar la fragmentación de las tablas utilizar la siguiente vista:

sys.dm_db_index_physical_stats

Ejecutar el siguiente query para la tabla Purchasing.PurchaseOrderHeader

SELECT s.avg_fragmentation_in_percent,
s.fragment_count,
s.page_count,
s.avg_page_space_used_in_percent,
s.record_count,
s.avg_record_size_in_bytes,
s.index_id
FROM sys.dm_db_index_physical_stats(DB_ID('AdventureWorks2008R2'),
OBJECT_ID(N'Purchasing.PurchaseOrderHeader'),
NULL, NULL, 'Sampled') AS s
WHERE s.record_count > 0
ORDER BY s.index_id ;

Ejecutar el query anterior para las otras tablas; observar que una reindexación puede
ayudar, para lo cual, se utiliza un script para realizar la reindexación de manera
automatizada:

DECLARE @DBName NVARCHAR(255),


@TableName NVARCHAR(255),
@SchemaName NVARCHAR(255),
@IndexName NVARCHAR(255),
@PctFrag DECIMAL,
@Defrag NVARCHAR(MAX)
IF EXISTS ( SELECT *
FROM sys.objects
WHERE OBJECT_ID = OBJECT_ID(N'#Frag') )

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 8

DROP TABLE #Frag


CREATE TABLE #Frag (
DBName NVARCHAR(255),
TableName NVARCHAR(255),
SchemaName NVARCHAR(255),
IndexName NVARCHAR(255),
AvgFragment DECIMAL
)
EXEC sys.sp_MSforeachdb
'INSERT INTO #Frag ( DBName,
TableName, SchemaName,
IndexName,
AvgFragment )
SELECT ''?'' AS DBName,
t.Name AS TableName,
sc.Name AS SchemaName,
i.name ASIndexName,
s.avg_fragmentation_in_percent
FROM ?.sys.dm_db_index_physical_stats(DB_ID(''?''),
NULL, NULL, NULL,
''Sampled'') AS s JOIN ?.sys.indexes i
ON s.Object_Id = i.Object_id
AND s.Index_id = i.Index_id
JOIN ?.sys.tables t
ON i.Object_id = t.Object_Id
JOIN ?.sys.schemas sc
ON t.schema_id = sc.SCHEMA_ID
WHERE s.avg_fragmentation_in_percent > 20
AND t.TYPE = ''U''
AND s.page_count > 8
ORDER BY TableName,IndexName';
DECLARE cList CURSOR
FOR
SELECT *
FROM #Frag;
OPEN cList;
FETCH NEXT FROM cList
INTO @DBName, @TableName, @SchemaName, @IndexName, @PctFrag;
WHILE FETCH_STATUS = 0
BEGIN
IF @PctFrag BETWEEN 20.0 AND 40.0
BEGIN
SET @Defrag = N'ALTER INDEX ' + @IndexName + ' ON ' + @DBName +
'.' + @SchemaName + '.' + @TableName + ' REORGANIZE';
EXEC sp_executesql
@Defrag;
PRINT 'Reorganize index: ' + @DBName + '.' + @SchemaName + '.' +
@TableName + '.' + @IndexName;
END
ELSE
IF @PctFrag > 40.0
BEGIN
SET @Defrag = N'ALTER INDEX ' + @IndexName + ' ON ' +
@DBName + '.' + @SchemaName + '.' + @TableName +
' REBUILD';
EXEC sp_executesql
@Defrag;
PRINT 'Rebuild index: ' + @DBName + '.' + @SchemaName +
'.' + @TableName + '.' + @IndexName;
END

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 9

FETCH NEXT FROM cList


INTO @DBName, @TableName, @SchemaName, @IndexName, @PctFrag;
END
CLOSE cList;
DEALLOCATE cList;
DROP TABLE #Frag;

Después de realizada la defragmentacion, volver a ejecutar el query usando la vista:

sys.dm_db_index_ physicalstats

Utilizar la vista para todas las tablas.

1.2.8 Analizar el plan de ejecución de los queries.

Para cada query identificado, se debe analizar su plan de ejecución asociado, dar clic
en el botón “Show Actual Execution Plan” y ejecutar el procedimiento almacenado.

EXEC dbo.spr_PurchaseOrderBySalesPersonName
@LastName = 'Hill%' ;

1.2.9 Identificar los operadores más costosos en el plan de ejecución.

Una vez analizado el plan de ejecución del query, el próximo paso es identificar los
operadores más costosos, por ejemplo en el paso anterior, se encuentran los siguientes
operadores más costosos:

Operador 1.- El key lookup sobre la tabla Purchasing.PurchaseOrderHeader es


aproximadamente 40%.

Operador 2.- El hash match entre Purchasing.PurchaseOrderHeader y


Purchasing.PurchaseOrderDetail es aproximadamente 20%.

1.2.10 Analizar los efectos de los cambios en la carga de trabajo de la base de datos.

Una vez aplicados los ajustes a los operadores más costosos, se tiene que evaluar si los
cambios han sido beneficiosos para la carga de trabajo, para esto volver a capturar la
información con el Profiler y utilizar los siguientes eventos:

Categoría: Execution
Event: rpc Completed
sql_batch_completed

Filtro: Solo la BD AdventureWorks2008R2

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 10

Iniciar el trace y volver a ejecutar los procedimientos almacenados:

EXEC dbo.spr_ShoppingCart
'20621' ;
EXEC dbo.spr_ProductBySalesOrder
43867 ;
EXEC dbo.spr_PersonByFirstName
'Gretchen' ;
EXEC dbo.spr_ProductTransactionsSinceDate
@LatestDate = '9/1/2004',
@ProductName = 'Hex Nut%' ;
EXEC dbo.spr_PurchaseOrderBySalesPersonName
@LastName = 'Hill%' ;

Guardamos la información obtenida por el profiler en el siguiente directorio:

c:\traces\WorkLoadTrace2.trc

Y realizamos las comparaciones entre WorkLoadTrace.trc y WorkLoadTrace2.trc,


utilizando la funcion:

SELECT * FROM ::fn_trace_gettable('c:\traces\WorkLoadTrace.trc', 1)


SELECT * FROM ::fn_trace_gettable('c:\traces\WorkLoadTrace2.trc', 1)

1.2.11 Iterar a través de estos pasos.

Finalmente, si el resultado no es el adecuado, regresar al paso inicial y repetir este


proceso, hasta encontrar el performance adecuado al servidor de base de datos SQL
Server.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 11

2. Uso de metadatos para monitorear la performace

A continuación se presenta un checklist para mantener y monitorear la performance de un


servidor SQL Server, como una referencia rápida para desarrolladores y administradores de base
de datos.

Se categorizan en:

2.1 Diseño de base de datos

• Balancear la normalización del modelo de datos.


• Aprovechar los constraints de integridad de datos.
• Aprovechar los constraints de integridad referencial.
• Adoptar las mejores prácticas del diseño de índices.
• Evitar el uso de prefijo sp_ para los nombres de los procedimientos almacenados
• Minimizar el uso de triggers.

2.2 Diseño de queries

• Utilizar el comando SET NOCOUNT ON al inicio.


• Definir, explícitamente, los dueños de los objetos.
• Evitar operaciones aritméticas o el uso de funciones sobre las columnas que forman
parte de la cláusula WHERE.
• Evitar el uso de hints.
• Evitar usar las vistas en joins.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016


Técnicas avanzadas de consulta 12

• Asegurarse que no se presenten conversiones implícitas en los tipos de datos.


• Emplear las mejores prácticas analizadas para reusar los planes de ejecución.
• Emplear las mejores prácticas para las transacciones.
• Eliminar en lo posible o reducir, el uso de cursores.

2.3 Realizar la configuración de los siguientes

• Affinity mask.
• Opciones de configuración de memoria.
• Max Degree of Parallelism.
• Emplear resource governor.
• Emplear fill factor.
• Compresion de base de datos.

2.4 Administración de Base de Datos

• Mantener las estadísticas al día.


• Mantener la mínima cantidad de índices fragmentados.
• Realizar una depuración periódica del errorlog.
• Evitar el uso de AutoShrink de la base de datos.
• Evitar el uso excesivo del trace.

2.5 Respaldos de Base de datos

• Programar respaldos diferenciales y de log frecuentes.


• Utilizar respaldos con compresión.

Cibertec Perú S.A.C - Transact-SQL - SQL Server 2016

You might also like