none
Cursor para acumular un monto y actualizar el valor de una tabla virtual

    Question

  • Hola, tengo el siguiente codigo:

    DECLARE @A		varchar(5)
    DECLARE @B		varchar(5)
    DECLARE @C		varchar(5)
    DECLARE @Mto		float
    DECLARE	@Rp		tinyint
    DECLARE	@Cd		smallint
    DECLARE @Rva		float
    DECLARE @Rwa		float
    DECLARE CurSin CURSOR FOR 
    	SELECT		A, C, Mto 
    	FROM		#tbl
    	ORDER BY	A
    OPEN		CurSin
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    WHILE @@FETCH_STATUS = 0 
    BEGIN
    	CASE WHEN	@A <> @B 
    		THEN
    			SET	@Rp		= 1
    			SET	@Rva		= 0
    			SET	@Rwa		= 999
    		ELSE
    			SET	@Rp		= 0
    	END
    	SET	@B	=	@A
    	SET	@Rva	=	ROUND(@Rva + 500)
    	SET	@Cd	= 0
    	IF	ABS(@Rva) <= 0.05 AND ABS(@Rva) >.05
    		BEGIN	SET	@Cd	= 1		END
    	SET	@Rwa	= @Rva
    	UPDATE	#tbl SET	Rp= @Rp FROM #Sin
    	UPDATE	#tbl SET	Cd= @Cd FROM #Sin
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    END
    CLOSE		CurSin
    DEALLOCATE	CurSin

    El problema es que me marca los siguiente errores:

    Msg 156, Level 15, State 1, Line 19

    Incorrect syntax near the keyword 'CASE'.

    Msg 156, Level 15, State 1, Line 24

    Incorrect syntax near the keyword 'ELSE'.

    Msg 156, Level 15, State 1, Line 27

    Incorrect syntax near the keyword 'SET'.

    Msg 156, Level 15, State 1, Line 39

    Incorrect syntax near the keyword 'CLOSE'.

    Lo que quiero hacer, es que lea registro a registro, ya que quiero llenar las columnas Rp, Cd. pero estas dependen de varios campos.

    A:Llave, B:LlaveAnterior, Rwa:MontoAcumulado por la llave (solo para validar).

    Espero me puedan ayudar.

    Saludos

    Tuesday, October 02, 2012 3:31 PM

Answers

  • Hola Buen Día, que tal : 

    Hasta donde tengo entendido, el uso del CASE WHEN es solo dentro de QUERYs, no como tu lo estás utilizando, en ese caso deberías usar IF ELSE, puedes probar así: 

    ...
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        IF @A <> @B
    	BEGIN
    			SET	@Rp		= 1
    			SET	@Rva	= 0
    			SET	@Rwa	= 999
        END
    	ELSE
    	   	    SET	@Rp		= 0
    
    	SET	@B	=	@A
    	SET	@Rva	=	ROUND(@Rva + 500)
    	SET	@Cd	= 0
    	IF	ABS(@Rva) <= 0.05 AND ABS(@Rva) >.05
    		SET	@Cd	= 1	
    		
    	SET	@Rwa	= @Rva
    	UPDATE	#tbl SET	Rp= @Rp FROM #Sin
    	UPDATE	#tbl SET	Cd= @Cd FROM #Sin
    	
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    END
    CLOSE		CurSin
    DEALLOCATE	CurSin

    En donde haces tus UPDATES, no comprendo porque actualizas una tabla temporal desde otra si por lo que veo el valor que utilizar proviene de una variable y no desde la otra tabla temporal.

    Por otra parte no es muy recomendable utilizar CURSORES y Tablas Temporales por cuestiones de rendimiento, has tratado otras soluciones ????

    SALUDOS!


    Sergio Sánchez Arias

    • Marked as answer by R-2 Tuesday, October 02, 2012 4:11 PM
    Tuesday, October 02, 2012 3:42 PM

All replies

  • Hola Buen Día, que tal : 

    Hasta donde tengo entendido, el uso del CASE WHEN es solo dentro de QUERYs, no como tu lo estás utilizando, en ese caso deberías usar IF ELSE, puedes probar así: 

    ...
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    WHILE @@FETCH_STATUS = 0 
    BEGIN
        IF @A <> @B
    	BEGIN
    			SET	@Rp		= 1
    			SET	@Rva	= 0
    			SET	@Rwa	= 999
        END
    	ELSE
    	   	    SET	@Rp		= 0
    
    	SET	@B	=	@A
    	SET	@Rva	=	ROUND(@Rva + 500)
    	SET	@Cd	= 0
    	IF	ABS(@Rva) <= 0.05 AND ABS(@Rva) >.05
    		SET	@Cd	= 1	
    		
    	SET	@Rwa	= @Rva
    	UPDATE	#tbl SET	Rp= @Rp FROM #Sin
    	UPDATE	#tbl SET	Cd= @Cd FROM #Sin
    	
    FETCH NEXT FROM CurSin INTO @A, @C, @Mto 
    END
    CLOSE		CurSin
    DEALLOCATE	CurSin

    En donde haces tus UPDATES, no comprendo porque actualizas una tabla temporal desde otra si por lo que veo el valor que utilizar proviene de una variable y no desde la otra tabla temporal.

    Por otra parte no es muy recomendable utilizar CURSORES y Tablas Temporales por cuestiones de rendimiento, has tratado otras soluciones ????

    SALUDOS!


    Sergio Sánchez Arias

    • Marked as answer by R-2 Tuesday, October 02, 2012 4:11 PM
    Tuesday, October 02, 2012 3:42 PM
  • Gracias por la respuesta.

    Perdon, las tablas son la misma #tbl = #Sin

    y no va a ser una tabla temporal, solo que como estoy en pruebas por eso la tengo temporal.

    Hay alguna manera que no sea con cursores?

    Estos los uso por que necesito que vaya registro a registro y que se vaya en orden, por que solo uno de los valores de A debe tener 1 y todos los demas 0 en RP.

    Tuesday, October 02, 2012 4:08 PM
  • Posiblemente amigo, todo depende de verificar la forma que obtienes los datos  que almacenas en tu tabla de pruebas ( o temporal ) para revisar si es posible evitar los cursores.

    SALUDOS


    Sergio Sánchez Arias

    Tuesday, October 02, 2012 4:16 PM
  • 1. Aparte del problema del Case/If que Sergio menciono,
    tambien los Update en el fin del Cursor no tienen sentido.
    Supongo que deben ser de la siguiente forma:

    UPDATE	#tbl SET Rp= @Rp Where ...=...;

    Me parece que todo el Cursor puede ser cambiado en un Update como:

    Update T
    Set    Rp=...,
           Cd=...
    FROM #tbl T
    Inner Join #Sin S
         On T...=S...;
    pero sin los esquemas de las tablas es imposible sugerir exactamente como.


    El castellano no es mi lengua materna. Discúlpenme por los errores gramaticales, y, si pueden, corríjanme en los comentarios, o por correo electrónico. ¡Muchas gracias! Blog: http://about.me/GeriReshef

    • Proposed as answer by HunchbackMVP Tuesday, October 02, 2012 4:53 PM
    Tuesday, October 02, 2012 4:20 PM
  • Actualmente, ya corrio, pero lamentablemente se ciclo, y solo me arrojo ceros.

    Este es un pequeño ejemplo de como  debe quedar

    A B Mto Rp Cd
    E1 EST 50 1 0
    E1 MENOS 1000 0 0
    E1 H 350 0 1
    E1 MENOS 200 0 1
    E2 EST 120 1 0
    E2 H 0 0 1
    E2 Pag 85 0 0
    E2 Rec -50 0 1
    E2 MENOS 200 0 1
    E2 MAS 0.5 0 0

    por cada registro columna A debe haber un 1.

    Tuesday, October 02, 2012 4:40 PM
  • como lo comentaba anteriormente:

    Perdon, las tablas son la misma #tbl = #Sin

    Y uso los cursores por que necesito que vaya registro a registro y que se vaya en orden, por que solo uno de los valores de A debe tener 1 y todos los demas 0 en RP.

    Tuesday, October 02, 2012 4:42 PM
  • Hola,

    Una solución iterativa, desempeñándola con  un cursor, conllevan una pérdida de rendimiento brutal, y no aconsejo que os pongáis a diseñar una solución con cursores. Es una forma sencilla y rápida de destrozar el potencial de un servidor SQL Server. Olvidémonos de usar cursores, ya que tenemos que pensar en conjuntos. Cuando nos paremos a pensar en una solución a nuestras consultas, no hay que dejar de pensar que SQL Server es un motor de base de datos relacional que trabaja con conjuntos de datos, que son los que queremos explotar haciendo consultas T-SQL, y una forma mala de hacerlo es utilizando cursores.

    por ejemplo, me parce mejor, la solucion que te aporta Geri


     Norman M. Pardell 

    ||Microsoft Certified IT Professional|| Database Administrator. Database Developer. SQL Server 2008

    Wednesday, October 03, 2012 9:28 AM
  • yo se que los cursores son muy lentos, estoy tratando de hacer eest de otra manera, pero yo necesito que lo lea registro a registro ya que el monto se acumulara, esto va a recaer en una condicion que le da el valor a la variable cd

    DECLARE @A varchar(5) DECLARE @B varchar(5) DECLARE @C varchar(5) DECLARE @Mto float DECLARE @Rp tinyint DECLARE @Cd smallint DECLARE @Rva float DECLARE @Rwa float DECLARE CurSin CURSOR FOR SELECT A, C, Mto FROM #tbl ORDER BY A OPEN CurSin FETCH NEXT FROM CurSin INTO @A, @C, @Mto WHILE @@FETCH_STATUS = 0 BEGIN IF @A <> @B BEGIN SET @Rp = 1 SET @Rva = 0 SET @Rwa = 999 END

    ELSE

    BEGIN SET @Rp = 0

    END SET @B = @A SET @Rva = ROUND(@Rva + Mto) SET @Cd = 0 IF ABS(@Rva) <= 0.05 AND ABS(@Rwa) >.05 BEGIN SET @Cd = 1 END SET @Rwa = @Rva UPDATE #tbl SET Rp= @Rp

    UPDATE #tbl SET Cd= @Cd

    FETCH NEXT FROM CurSin INTO @A, @C, @Mto END CLOSE CurSin DEALLOCATE CurSin

    lo que quieroes que le asigne en una columna (RP) un 1, en caso de que sea el primer registro de ese valor de  A. Despues dependiendo del monto asignara Cd.

    el problema es que cd, siempre es diferente y depende de los valores de los registros anteriores que coinciden entre A.

    SAludos


    • Edited by R-2 Wednesday, October 03, 2012 2:07 PM
    Wednesday, October 03, 2012 2:07 PM