2009-06-08 27 views
5

Me gustaría saber su (s) experiencia (s) al reemplazar cursores de SQL Server en código existente, o cómo tomó un problema que un tipo de procedimiento usaría un cursor para resolver, y lo hizo basado en conjunto.¿Cuáles son las diferentes maneras de reemplazar un cursor?

¿Cuál fue el problema que se utilizó para resolver el cursor? ¿Cómo reemplazaste el cursor?

Respuesta

5

intente nunca bucle, trabajar en conjuntos de datos.

puede insertar, actualizar, eliminar varias filas a la vez. aquí en una inserción de ejemplo de varias filas:

INSERT INTO YourTable 
     (col1, col2, col3, col4) 
    SELECT 
     cola, colb+Colz, colc, @X 
     FROM .... 
      LEFT OUTER JOIN ... 
     WHERE... 

Al mirar un lazo, vea lo que hizo dentro de él. Si solo se trata de inserciones/eliminaciones/actualizaciones, vuelva a escribir para usar comandos individuales. Si hay IF, vea si pueden ser enunciados CASE o DONDE las condiciones en inserciones/eliminaciones/actualizaciones. Si es así, elimine el bucle y use los comandos de configuración.

He tomado bucles y los reemplacé con los comandos de configuración y reduje el tiempo de ejecución de minutos a unos pocos segundos. Realicé procedimientos con muchos bucles anidados y llamadas a procedimientos y mantuve los bucles (era imposible usar solo inserciones/eliminaciones/actualizaciones), pero quité el cursor, y he visto menos bloqueo/bloqueo y un aumento de rendimiento masivo también. Aquí hay dos métodos de bucle que son mejores que los bucles del cursor ...

si tiene que bucle, más de un conjunto hacer algo como esto:

--this looks up each row for every iteration 
DECLARE @msg VARCHAR(250) 
DECLARE @hostname sysname 

--first select of currsor free loop 
SELECT @hostname= min(RTRIM(hostname)) 
    FROM master.dbo.sysprocesses (NOLOCK) 
    WHERE hostname <> '' 

WHILE @hostname is not null 
BEGIN 
    set @msg='exec master.dbo.xp_cmdshell "net send ' 
     + RTRIM(@hostname) + ' ' 
     + 'testing "' 
    print @msg 
    --EXEC (@msg) 

    --next select of cursor free loop 
    SELECT @hostname= min(RTRIM(hostname)) 
     FROM master.dbo.sysprocesses (NOLOCK) 
     WHERE hostname <> '' 
     and hostname > @hostname 
END 

si tiene un conjunto razonable de artículos (no 100.000) para recorrer usted puede hacer esto:

--this will capture each Key to loop over 
DECLARE @msg VARCHAR(250) 
DECLARE @From int 
DECLARE @To  int 
CREATE TABLE #Rows 
(
    RowID  int not null primary key identity(1,1) 
    ,hostname varchar(100) 
) 

INSERT INTO #Rows 
SELECT DISTINCT hostname 
    FROM master.dbo.sysprocesses (NOLOCK) 
    WHERE hostname <> '' 
SELECT @From=0,@[email protected]@ROWCOUNT 

WHILE @From<@To 
BEGIN 
    SET @[email protected]+1 

    SELECT @msg='exec master.dbo.xp_cmdshell "net send ' 
     + RTRIM(hostname) + ' ' 
     + 'testing "' 
     FROM #Rows WHERE [email protected] 
    print @msg 
    --EXEC (@msg) 
END 
2

Bueno, a menudo un desarrollador de aplicaciones acostumbrado a la programación de procedimientos, por costumbre, intentará hacer todo de forma procedural, incluso en SQL.

En la mayoría de los casos, un SELECT con los parámetros correctos podría funcionar, o tal vez se trata de una instrucción UPDATE.

El punto es realmente: necesita comenzar a pensar en las operaciones de configuración y decirle a su RDBMS lo que quiere hacer, no cómo hacerlo paso a paso.

Es difícil dar una única respuesta "correcta" a esto ..... casi tendría que mostrarlo con un ejemplo concreto.

Marc

0

escribí un código que calcula los totales acumulados para los datos financieros relacionados con un determinado año. En cada trimestre, tuve que agregar el valor del trimestre actual al total acumulado mientras manejaba NULL de manera apropiada, de modo que el total acumulado del trimestre anterior se transfirió cuando el valor del trimestre actual era NULO.

Originalmente, lo hice usando un cursor y desde un punto de vista funcional cumplía con los requisitos del negocio. Desde un punto de vista técnico, resultó ser un obstáculo para el espectáculo porque a medida que aumentaba la cantidad de datos, el código tardaba exponencialmente más. La solución fue reemplazar el cursor con una subconsulta correlacionada que cumplía con los requisitos funcionales y eliminaba cualquier problema de rendimiento.

Espero que esto ayude,

Bill

4

he reemplazado algunos cursores con bucles tiempo.

DECLARE @SomeTable TABLE 
(
    ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL, 
    SomeNumber int, 
    SomeText varchar 
) 

DECLARE @theCount int 
DECLARE @theMax int 

DECLARE @theNumber int 
DECLARE @theText varchar 

INSERT INTO @SomeTable (SomeNumber, SomeText) 
SELECT Number, Text 
FROM PrimaryTable 

SET @theCount = 1 
SELECT @theMax = COUNT(ID) FROM @SomeTable 

WHILE (@theCount <= @theMax) 
BEGIN 

    SET @theNumber = 0 
    SET @theText = '' 

    SELECT @theNumber = IsNull(Number, 0), @theText = IsNull(Text, 'nothing') 
    FROM @SomeTable 
    WHERE ID = @theCount 

    -- Do something. 
    PRINT 'This is ' + @theText + ' from record ' + CAST(@theNumber AS varchar) + '.' 

    SET @theCount = @theCount + 1 

END 

PRINT 'Done' 
Cuestiones relacionadas