Professional Documents
Culture Documents
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
/* 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
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
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.
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