SQL Server Performance

Cursores en SQL SERVER

Discussion in 'Preguntas sobre SQL Server en Español.' started by emadrigal, May 14, 2004.

  1. emadrigal New Member

    Hola, soy nuevo en cursores para sql, por lo que necesito de su valios ayuda. Tengo una tabla que almacena el contenido de un archivo de texto plano en un solo campo. Al principio de cada bloque de datos viene el número de cuenta al que pertenece el detalle de los datos siguientes en la tabla, despuès vienen 2 caracteres en de control en registros seguidos que indican que ya acabó ese bloque de datos. Necesito identificar a qué cuenta pertenece cada bloque de datos, por lo que pienso crear una tabla que me pegue la cuenta que corresponda a cada bloque, por lo que he pensado en hacerlo mediante un corsor que vaya barriendo la tabla. Alguien me podría ayudar con esto? La estructura dla tabla es más o menos así:

    ] --->Estos dos caracteres en la tabla indican que inicia un nuevo
    ] --->bloque de datos
    12345678901 |---> Cuenta a la que pertenece este bloque de datos
    ----------- |
    ----------- |--> detalle de datos
    ----------- |
    ----------- |
    ] ---> Fin de bloque
    ] ---> de datos

    La tabla que pretendo armar es:
    NumCta Detalle
    12345678901 -----------
    12345678901 -----------
    12345678901 -----------
    12345678901 -----------
    12345678902 -----------
    12345678902 -----------
    12345678902 -----------
    12345678902 -----------
    .
    .
    .

    Agradezco de antemano su valiosa ayuda.

    Enrique Madrigal
  2. Luis Martin Moderator

    Enrique:

    Dado que soy el único que responde en español, y dado que no soy desarrollador, consultaré a los especialista en desarrollo en este Forum y te contestaré.

    Tengo alguna duda: es una tabla por única vez o es una tabla que se carga con otro proceso en forma rutinaria?.
    Si fuera una sola vez, quizás se podría pensar en exportarla a formato tabla, mediante excel y luego importarla a formato tabla.

    Saludos,



    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  3. Raulie New Member

    Déjeme ver si entiendo, Usted tiene dos tablas, una con el número de cuenta y otro tabla con los detalles del texto también tiene en la segundo tabla el número de cuenta. Si usted necesita solamente los números de cuenta intentan y utilizan esto SELECT DISTINCT Numero de cuenta... Por favor dame mas detalles en lo que queres aser.

    Saludos
  4. Luis Martin Moderator

    Cuántas filas tiene la tabla?
    Qué longitud tiene cada campo (o el único)?

    Se puede con el cursor si es que no son muchas filas y la longitud no es mucha.
    Se puede también con la solución setbase o con combinación de ambas.
    Sería necesario separar de acuerdo a los caracteres "[" y "|", para saber cómo realizar la separación (split) fíjate:

    http://www.sql-server-performance.com/forum/topic.asp?TOPIC_ID=1092



    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  5. emadrigal New Member

    Luis Martin y Lazy DBA:
    Esto es un proceso que se realizarà rutinariamente, por lo que lo tengo que automatizar.
    Es una tabla con al rededor de 80 mil registros por lo que no se si funciones bien con cursores o si no es así, qué es lo que debo utilizar?.
    Lo siguiente es un ejemplo de la tabla, y es un solo campo.
    El dato que me sirve para identificar el número de cuenta está en el 3er. renglón después de los caracteres "No." y es por ejemplo:
    ]
    ]
    1 10000000011Cust.: CLIENTE001 Basic No.000001
    en este caso el número de cuenta es: ^^^^^^-------> "000001"

    Todo lo que está debajo es el detalle de esa cuenta, hasta encontrar en esa misma posición un número de cuenta distinto.

    Por lo que tengo que identificar en qué momento cambia ese número para a partir de esto armar una nueva tabla que contenga como campos No.Cta y Col001.


    ----------------------------------------Col001--------------------------------------------
    ]
    ]
    1 10000000011Cust.: CLIENTE001 Basic No.000001 Address 1 Cust.: 000001
    Address 2 Cust.: 000001 Address 3 C ust.: 000001 Address 4 Cust.: 00000C.R. 01/0011 Zip/Postal
    131001 000001 30
    3 1 000001 SINCE JUN 1995MEXICO, DF HOUSTON 829 APR 01 TO APR 30 2004 1,358,989.46 .00
    .00 .00 .00 1,358,989.46 965,710.65 .00 3 3 784.00-
    7 366,557.68 .00 .00 13,381,907.18 .00 .00 .00 .00
    12,389,475.40 .00 .00 1,358,989.46 .00 .00 % %
    8
    ]
    ]
    1 10000000011Cust.: CLIENTE002 Basic No.000002 Address 1 Cust.: 000002
    Address 2 Cust.: 000002Address 3 C ust.: 000001 Address 4 Cust.: 00000C.R. 01/0011 Zip/Postal
    131001 000001 30
    3 1 000001 SINCE JUN 1995MEXICO, DF HOUSTON 829 APR 01 TO APR 30 2004 1,358,989.46 .00
    .00 .00 .00 1,358,989.46 965,710.65 .00 3 3 784.00-
    7 366,557.68 .00 .00 13,381,907.18 .00 .00 .00 .00
    12,389,475.40 .00 .00 1,358,989.46 .00 .00 % %
    8
    ]
    ]

    ej. de tabla que pretendo armar.
    NoCta Col001
    ------- -----------------------
    000001 Detalle................
    000001 Detalle................
    000001 Detalle................
    000001 Detalle................
    000001 Detalle................
    000002 Detalle................
    000002 Detalle................
    000002 Detalle................
    000002 Detalle................
    000002 Detalle................
    000003 Detalle................
    .
    .
    . etc.

    Gracias por la ayuda que me puedan brindar.
    Enrique

  6. Luis Martin Moderator

    Te serviría la función SUBSTRING ( expression , start , length ) ?

    Para tu tabla sería : substring (campo,43,6) para obtener el número de cuenta, y luego la misma función para tomar desde el fin de la cuenta hasta el final del registro.




    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  7. Raulie New Member

    Aqui esta un cursor modificalo y aver si te ayuda. Tambien juega con esta funcion para grabar la start precicion en caso que no es 43 SELECT PATINDEX('%Basic No.%', Col001)+9 FROM TABLA. Los Cursors usan mucho resources y el performance de cursors es malo, vamos a ver si los otros membros de foro pueden ayudar.

    DECLARE @POSITION INT
    DECLARE MY_CUR CURSOR FAST_FORWARD FOR
    SELECT SUBSTRING (Col001,43,6) AS NO_CUENTA FROM TABLA
    OPEN MY_CUR FETCH NEXT FROM MY_CUR INTO @POSITION
    SELECT SUBSTRING (Col001,43,6) AS NO_CUENTA FROM TABLA
    FETCH NEXT FROM MY_CUR INTO @POSITION
    PRINT @POSITION
    CLOSE MY_CUR
    DEALLOCATE MY_CUR
    GO


  8. emadrigal New Member

    Hola, les agradezco mucho la ayuda que me han proporcionado, me ha sido de gran utilidad.
    He creado este stored procedure de un cursor para hacer el update que necesitaba, lo publico por si a alguien le es de utilidad.
    Saludos y gracias nuevamente por su ayuda.
    Enrique.

    CREATE PROCEDURE PegaCta

    AS

    DECLARE @POSITION INT
    DECLARE @campoY VARCHAR (15)
    DECLARE @Col001 VARCHAR (255)
    DECLARE @Col002 VARCHAR (50)
    DECLARE @campoX VARCHAR (15)
    DECLARE @Identif VARCHAR (15)

    DECLARE MY_CUR CURSOR local FOR
    SELECT substring(Col001,32,15) AS Identif, Col001, Col002 FROM CHQHO

    OPEN MY_CUR FETCH MY_CUR INTO @Identif, @Col001, @Col002
    SELECT @POSITION = 1
    While (@@fetch_status =0)
    begin
    select @campoX = @Identif
    FETCH MY_CUR INTO @Identif, @Col001, @Col002

    --Print 'FETCHcampoX = ' + @campoX

    --Print 'Identif = ' + @Identif

    If substring(@Identif,1,9) = 'Basic No.'
    BEGIN
    Select @campoY = @Identif

    Update CHQHO Set Col002 = substring(@CampoY,10,15) where current of MY_CUR

    END
    ELSE
    If @Identif <> @campoX or @Identif = Null or @Identif = ''
    BEGIN
    Update CHQHO Set Col002 = substring(@CampoY,10,15) where current of MY_CUR
    -- update CHQHO Set Periodo ='04/30/2004'
    -- update CHQHO Set Control = 1000
    END

    SELECT @POSITION = @POSITION + 1


    PRINT @POSITION
    --Print 'Identif = ' + @Identif
    end
    CLOSE MY_CUR
    DEALLOCATE MY_CUR



    GO


    Enrique Madrigal
  9. Raulie New Member

    Gracias Enrique, estamos aqui para servirte. [<img src='/community/emoticons/emotion-5.gif' alt=';)' />]
  10. emadrigal New Member

    Hola estimados miembros del grupo. Tengo 2 problemas con el manejo de cursores.
    El 1o.
    Necesito hacer la actualización de los últimos 3 registros de una tabla, para lo cual he creado el siguiente cursor:

    DECLARE MY_CUR2 CURSOR SCROLL FOR
    Select Col000, Col001, Col002 from CHQHO
    ORDER BY Col000
    OPEN MY_CUR2
    FETCH LAST FROM MY_CUR2
    FETCH RELATIVE -2 FROM MY_CUR2
    WHILE @@FETCH_STATUS = 0
    BEGIN
    Update CHQHO Set Col002 = @campoY where current of MY_CUR2
    FETCH NEXT FROM MY_CUR2
    END
    CLOSE MY_CUR2
    DEALLOCATE MY_CUR2

    Sin embargo al ejecutarlo me regresa el siguinte error:
    Server: Msg 16929, Level 16, State 1, Procedure PegaCta3, Line 49
    The cursor is READ ONLY.
    The statement has been terminated.

    ¿Cual es la forma correcta de hacer actualizaciones en una tabla mediante un cursor?

    El 2o.
    El cursor mencionado arriba está anidado dentro de otro cursor, y ambos se encuentran contenidos en un stored procedure. Cuando el stored procedure se ejecuta, éste termina en el cursor anidado, y ya no se continúa ejecutando el resto del programa. ¿A qué se debe ésto, cómo puedo hacer para que el programa continue?

    Gracias por la ayuda que puedan proporcionarme con estos casos.
    Saludos.


    Enrique Madrigal
  11. Luis Martin Moderator

    No estoy segura que necesites un cursor para esto.

    Lo siguiente debería funcionar (se asume que col1000 es tu clave)


    update c1 set col002 = @campoy from chqho c1 join (select top 3 col000 from chqho order by col000 desc) as c2 on c1.col000 = c2.col000

    El subquery devuelve el valor de la clave para las últimos 3 registros, la parte principal del query los actualiza.
    El cursor es sucio y no debe ser usado si no es absolutamente necesario.




    The subquery returns the key value of the last 3 records, the main part of the query updates them
    The cursor is messy and I dont even want to pick it apart if its not necessary.

    Nota: La respuesta proviene de: ChrisFretwell

    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  12. emadrigal New Member

    Luis Martín:
    Muchas gracias por tu ayuda. Tienes razón, el query que me sugeriste funciona bien.
    Ya había realizado un cursor y se tardaba demasiado, con el query, se acorta significativamente el tiempo de ejecución.
    Ya también, he descubierto porque la ejecución en los cursores anidados se interrumpía al terminar el cursor que estaba contenido dentro del otro. La razón es que ambos se encontraban ejecutándose mediante el ciclo de un "While (@@fetch_status = 0)", de tal forma que al terminar el ciclo del fetch anidado la función de cursor "@@fetch_status", ya trae un valor distinto de Cero, ya que es el valor que toma al terminar el ciclo del cursor anidado. Es por eso que al evaluar el "While" del otro cursor el valor del "@@fetch..." no cumple la condición para entrar de nuevo al ciclo. Lo solucioné colocando al final del "while" un "fetch last from..." para hacer que el valor del "@@fetch..." fuera Cero y como consecuencia, válido para el cursor.

    Quiero aprovechar para realizar otra pregunta. ¿Porqué mientras estoy trabajando en una ventana del "SQL Query Analyzer" ejecutando un stored procedure, cuando lo ejecuto la primera vez la ejecución se realiza correctamente, y al tratar de ejecutarlo nuevamente solo me regresa el mensaje: "The command(s) completed successfully."?

    Gracias nuevamente y saludos.

    Enrique Madrigal.
  13. Luis Martin Moderator

    Cada vez que ejecutas la stored procedure por primera vez muestra algún output y la segunda no muestra nada?
    Podría ser que la sp no tenga nada que mostrar la segunda vez?


    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  14. emadrigal New Member

    Luis:

    El SP se debería de mostrar output cada vez que se ejecuta, es por eso que no se porqué motivo solo la primera vez muestra la salida.

    Enrique.



    Enrique Madrigal
  15. Luis Martin Moderator

    La verdad no se me ocurre nada. (supongo que solamente te pasa con una sola sp, sino te recomendaría que reinstales el último Service Pack).

    Por favor confírmame si es con una sola sp, así consulto con otros miembros del Forum.
    Dime también qué SQL y service pack tienes.

    Gracias,


    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  16. emadrigal New Member

    Luis:
    Hasta el momento, solo me ha sucedido con ésta sp, con respecto a la versión es SQL Server 2000 Service Pack4.
    Saludos!

    Enrique Madrigal
  17. Luis Martin Moderator

    Podrías mostrar la sp?



    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  18. emadrigal New Member

    Aquí está:
    SET QUOTED_IDENTIFIER ON
    GO
    SET ANSI_NULLS ON
    GO




    CREATE PROCEDURE PegaCta2
    @FechArch datetime
    AS

    DECLARE @POSITION INT
    DECLARE @CONT INT
    DECLARE @campoY VARCHAR (15)
    DECLARE @Col001 VARCHAR (255)
    DECLARE @Col002 VARCHAR (50)
    DECLARE @campoX VARCHAR (15)
    DECLARE @Identif VARCHAR (15)
    DECLARE @Id_CUR2 INT
    DECLARE @Col001_CUR2 VARCHAR (255)
    DECLARE @Col002_CUR2 VARCHAR (50)

    DECLARE MY_CUR CURSOR for
    SELECT substring(Col001,12,6) AS Identif, Col001, Col002 FROM CHQHOpaso
    Order by Col000
    --FOR Update of Col002

    OPEN MY_CUR --FETCH NEXT from MY_CUR INTO @Identif, @Col001, @Col002
    SELECT @POSITION = 1
    SELECT @CONT = 1
    While (@@fetch_status =0)
    BEGIN
    select @campoX = @Identif
    FETCH MY_CUR INTO @Identif, @Col001, @Col002

    --Print 'FETCHcampoX = ' + @campoX

    --Print 'FETCHIdentif = ' + @Identif

    If substring(@Col001,19,5) = 'SINCE' and (@@fetch_status =0)
    BEGIN
    Select @campoY = @Identif

    --PRINT 'IF@Identif = ' + @Identif
    --PRINT 'IF@campoX = ' + @campoX
    --PRINT 'IF@campoY = ' + @campoY
    -- PROCEDIMIENTO PARA REGRESAR 3 POSICIONES EN LA TABLA CHQHO MEDIANTE UN CURSOR Y
    -- ACTUALIZAR Col002 CON EL NUM. DE CTA. MEDIANTE OTRO CURSOR

    update c1 set col002 = @campoy from chqho c1 join
    (select top 4 col000 from chqho order by col000 desc) as c2 on c1.col000 = c2.col000
    where substring(Col001,1,1)<> '9'
    -- FIN DEL PROCEDIMIENTO

    -- Update CHQHO Set Col002 = substring(@CampoY,10,15) where current of MY_CUR
    INSERT INTO CHQHO (Col001, Col002, Periodo)
    VALUES (@Col001, @CampoY, @FechArch)
    -- update CHQHO Set Periodo ='04/30/2004'
    -- update CHQHO Set Control = 1000

    END
    ELSE
    If @Identif <> @campoX /*or @Identif = Null or @Identif = ''*/ and (@@fetch_status =0)
    BEGIN
    --PRINT 'ELSE@Identif = ' + @Identif
    --PRINT 'ELSE@campoX = ' + @campoX
    --PRINT 'ELSE@campoY = ' + @campoY

    --Update CHQHO Set Col002 = substring(@CampoY,10,15) where current of MY_CUR
    INSERT INTO CHQHO (Col001, Col002, Periodo)
    VALUES (@Col001, @CampoY, @FechArch)
    -- update CHQHO Set Periodo ='04/30/2004'
    -- update CHQHO Set Control = 1000
    END

    SELECT @POSITION = @POSITION + 1

    --PRINT 'POSICIÓN = ' + str(@POSITION)
    --PRINT 'CONT = ' + str(@CONT)
    --Print 'Identif = ' + @Identif
    END
    CLOSE MY_CUR
    DEALLOCATE MY_CUR












    GO
    SET QUOTED_IDENTIFIER OFF
    GO
    SET ANSI_NULLS ON
    GO

    Gracias y saludos.

    Enrique Madrigal
  19. Luis Martin Moderator

    Una prueba, mientras consulto con otros miembros del forum, sería ejecutar la sp las dos veces, mientras ejecutas el Profiler.
    Con el Profiler puedes ver cuantas lecturas, escrituras, etc se realizaron las dos veces.



    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  20. Luis Martin Moderator

    Una pregunta, la primera vez que ejecutas el script, en realidad estas creando la store procedure y el output debería ser, "The command(s) completed successfully.".

    A partir de alli, cada vez que ejecutes la sp deberías llamarla :
    pegacta2, con los parámetros que les quieres pasar.

    La estás ejecutando de esa forma?



    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

  21. emadrigal New Member

    Así es, la forma en que lo ejecuto es el siguiente:

    por ejemplo:
    execute PegaCta2 "4/30/2004"

    Saludos!

    Enrique Madrigal
  22. Luis Martin Moderator

    OK. Qué muestra el plan de ejecución?. Puede haber algún cambio de datos luego de la ejecución?


    Luis Martin
    Moderator
    SQL-Server-Performance.com

    All postings are provided “AS IS” with no warranties for accuracy.

Share This Page