You are on page 1of 6

Optimizacin de Scripts SQL Optimizacion de Scripts en SQL

Eliminando los Cursores


sbado, 21 de enero de 2006

En este artculo se muestra como cambiar el uso de un CURSOR por una sentencia WHILE, la cul nos mejorara el performance en un 75%. Desde mi punto de vista lo ltimo que se debe de hacer en el diseo de una aplicacin es hacer uso de los cursores, ya que consumen muchos recursos y son muy lentos. As que debemos de tomarlos como la ltima alternativa en un desarrollo. Supongamosquetenemos el siguiente cursor DECLARE @item_category_id INT DECLARE @order_id INT DECLARE @purchase_order_id INT DECLARE item_cursor CURSOR FAST_FORWARD FOR SELECT it.item_category_id ,ord.order_id FROM dbo.item_categories it INNER JOIN dbo.ordersord ON ord.item_category_id = it.item_category_id WHERE ord.order_date>= '1-sep-05' and it.isSuspended != 1 OPEN item_cursor FETCH NEXT FROM item_cursor INTO @item_category_id ,@order_id WHILE @@FETCH_STATUS = 0 BEGIN EXEC dbo.usp_generate_purchase_order @item_category_id, @order_id, @purchase_order_id OUTPUT

Optimizacin de Scripts SQL

/* Codigo extra propio de nuestra aplicacin */ FETCH NEXT FROM item_cursor INTO @item-category_id ,@order_id END Ahora este es el codigo sin la necesidad de hacer uso de cursores y que nos da el mismo resultado usando un loopWhile. --Declarando Variables DECLARE @item_category_id INT DECLARE @order_id INT DECLARE @purchase_order_id INT --Declarandotabla DECLARE @item_table TABLE (primary_key INT IDENTITY(1,1) NOT NULL, --THE IDENTITY STATEMENT IS IMPORTANT! item_category_id INT, order_id INT ) --Insertar los registros en nuestratablatal y como se seleccionaban en el Cursor INSERT INTO @item_table SELECT it.item_category_id ,ord.order_id FROM dbo.item_categories it INNER JOIN dbo.ordersord ON ord.item_category_id = it.item_category_id WHERE ord.order_date>= '1-sep-05' and it.isSuspended != 1 DECLARE @item_category_counter INT DECLARE @loop_counter INT

Optimizacin de Scripts SQL


SET @loop_counter = ISNULL((SELECT COUNT(*) FROM @item_table),0) -- Hacer el conteo de registros de nuestratabla SET @item_category_counter = 1 WHILE @loop_counter> 0 AND @item_category_counter<= @loop_counter BEGIN SELECT @item_category_id = item_category_id ,@order_id = order_id FROM @item_table WHERE primary_key = @item_category_counter --Ahorapasar el item-category_id y order_id al store procedure EXEC dbo.usp_generate_purchase_order @item_category_id, @order_id, @purchase_order_id OUTPUT /* Codigo extra de nuestraaplicacin */ SET @item_category_counter = @item_category_counter + 1 END Este cambionosmejora el performance en un 75%, a comparacin de un CURSO. Existen algunos casos que no son muy comunes, en el cul es preferible hacer uso de un cursor, pero en un 99% solo te va a alentar tu aplicacin, as que evita hacer uso de ellos.

Como hacer un count(*) sin afectar el performance


domingo, 19 de febrero de 2006

Si alguna vez trataste de hacer un SELECT count(*) en una tabla muy grande, debes de saber cuanto tiempo puede llevarse acabo esto, generalmente en tablas de millones de registros son minutos. Existe una manera ms eficientes de hacerlo. Por ejemplo, cuando se ejecuta el siguiente comando en una tabla de 10 millones de registros, tarda ms de un minuto en darme el resultado, esto debido a que SQL Server debe de llevar a cabo varios I/O para realizar la cuenta. SELECT COUNT(*) from Una manera ms eficiente y rpida de contar los registros de cualquier tabla es ejecutando el siguiente

Optimizacin de Scripts SQL


query: SELECT rows FROM sysindexes WHERE id = OBJECT_ID(' ') AND indid< 2 Al ejecutar este query toma menos de un segundo en darme el mismo resultado que el anterior, y practiamente no hace I/O. Esto se debe a que esta tabla guarda el nmero de registros de todas las tablas. As que en lugar de estar contando los registros solo es necesario ver la tabla sysindexes. Se debe de tener cuidado cuando se quiere un numeroexcto de registros, ya que esta tabla no se actualiza en linea. Tambin es necesario que se tenga habilitada la opcin de "Auto createstatistics" de la base de datos.

Ejemplo de optimizacin de joins


lunes, 20 de febrero de 2006

El optimizador de consultas de SQL Server es muy listo para resolver queries, pero algunas veces necesita un poco de ayuda. Usemos la base de datos Northwind para mostrar un ejemplo. Supongamos que queremos una lista de las ordenes y su detalle de todos los id's que sean mayores o iguales a 11,000. Podramos filtrar la tabla de ordenes o la tabla de detalles, lo cual sera lo mismo para extraer los registros correctos, pero si aplicamos el criterio para las dos tablas la bsqueda ser mucho ms eficiente ya que necesitara de mucho menos I/O's. Este sera el query que comnmente escribiramos para extraer la informacin SELECT * FROM Orders AS O JOIN [Order Details] AS OD ON OD.OrderID= O.OrderID WHERE O.OrderID>= 11000 Ahora si habilitamos las estadsticas de IO veramos lo siguiente: Table 'OrderDetails'. Scan count 78, logical reads 158, physical reads 0, read ahead reads 0. Table 'Orders'. Scan count 1, logical reads 3, physical reads 0, read ahead reads 0.

Optimizacin de Scripts SQL

Y el plan de ejecucin: | | | Nested Loops(Inner Join, OUTER REFERENCES:([O].[OrderID])) Clustered Index Seek(OBJECT:([Northwind].[dbo].[Orders].[PK_Orders] AS Clustered Index Seek(OBJECT:([Northwind].[dbo].[Order

[O]), SEEK:([O].[OrderID] >= 11000) ORDERED FORWARD) Details].[PK_Order_Details] AS [OD]), SEEK:([OD].[OrderID]=[O].[OrderID]) ORDERED FORWARD) Ahorahagamos lo mismoperoanexando el filtroparaambastablas. SELECT * FROM Orders AS O JOIN [Order Details] AS OD ON OD.OrderID = O.OrderID WHERE O.OrderID>= 11000 AND OD.OrderID>= 11000 Revisando las estadsticas de IO, podemos ver como disminuye el nmero de IO's considerablemente. Table 'Order Details'. Scan count 1, logical reads 3, physical reads 0, read ahead reads 0. Table 'Orders'. Scan count 1, logical reads 3, physical reads 0, read ahead reads 0. Ahoraveamos el plan de ejecucin: | Merge Join(Inner Join, MERGE:([O].[OrderID])=([OD].[OrderID]), RESIDUAL:([O].[OrderID]=[OD].[OrderID])) | Clustered Index Seek(OBJECT:([Northwind].[dbo].[Orders].[PK_Orders] AS [O]), SEEK:([O].[OrderID] >= 11000) ORDERED FORWARD) | Clustered Index Seek(OBJECT:([Northwind].[dbo].[Order Details].[PK_Order_Details] AS [OD]), SEEK:([OD].[OrderID] >= 11000) ORDERED FORWARD) Notencomo el primera query utiliza un nested loops join mientrasque el segundoutilizauna merge join

Optimizacin de Scripts SQL


el culesmseficiente. Conculsin. Siempre que escribamos un join en donde la columna filtrada exista en ms de una tabla, debemos anexarla en nuestra clusula WHERE. De esa manera podemos asegurar que el optimizador de consultas de SQL Server va a escoger el plan de ejecucin ms adecuado.