Cursores en SQL SERVER | SQL Server Performance Forums

SQL Server Performance Forum – Threads Archive

Cursores en SQL SERVER

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
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.
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
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.
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
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.
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

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
Gracias Enrique, estamos aqui para servirte. [<img src=’/community/emoticons/emotion-5.gif’ alt=’;)‘ />]
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
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.
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.

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.
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
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.
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
Podrías mostrar la sp? Luis Martin
Moderator
SQL-Server-Performance.com All postings are provided “AS IS” with no warranties for accuracy.
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 ‘[email protected] = ‘ + @Identif
–PRINT ‘[email protected] = ‘ + @campoX
–PRINT ‘[email protected] = ‘ + @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 ‘[email protected] = ‘ + @Identif
–PRINT ‘[email protected] = ‘ + @campoX
–PRINT ‘[email protected] = ‘ + @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
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.
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.
Así es, la forma en que lo ejecuto es el siguiente: por ejemplo:
execute PegaCta2 "4/30/2004" Saludos! Enrique Madrigal
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.
]]>