jueves, 16 de septiembre de 2010

SQL. Recorrer y actualizar ciertos registros de una tabla. Cursores.

A veces, mi ignorancia en el lenguaje SQL que hace verme forzado a trabajar en horas intempestivas como hoy (terminar a las 02:00 para reiniciar a las 06:00).

El problema es que tengo una tabla de cientos de miles de registros con un campo expediente que tiene que ser único. Es decir, que no admita duplicados. Y en dicho campo, 290 mil registros tienen un valor que es vacio, es cero o es directamente null.

El quid del asunto es localizar dichos registros y actualizarme el campo expediente con una id construido.

Después de demasiadas horas perdidas, la madrugada y un reparador mini sueño, me trajo la respuesta en forma de Cursores.

Primero declaremos las variables que voy a utilizar en las operaciones.

-- Id del registro
-- Campo expediente a modificar
-- Contador, campo numérico autoincremental que vamos a almacenar en el campo expediente
declare @id as nvarchar(16)
declare @expediente as varchar(30)
declare @contador as int

A continuación declaramos el cursor, para manipular los datos solamente sobre los expedientes que cumplen la condición, en vez de sobre los cientos de miles de toda la tabla.

declare CURSORVC cursor for
  select id, expediente  from Tabla
  Where
  expediente = '0'
  OR expediente = ''
  OR expediente = null
  OR expediente IS NULL
  Order by id

Inicializamos el contador.

SET @contador = 10

Y abrimos el cursor, recuperando la primera fila. Aquí lo único que hay que tener cuidado es que las variables en las que cargamos los datos del cursor sean correspondientes a los campos obtenidos en la select de la declaración del cursor.

open CURSORVC
  fetch next from CURSORVC into @id, @expediente

A continuación iniciamos el bucle

while @@fetch_status = 0
    begin

Actualizamos los datos del campo expediente cuando el id sea el del registro que nos ha traido el cursor

update Tabla set expediente =  CAST(@contador As varchar(10)) + ' -2010'
    where id=@id
    -- Avanzamos otro registro
    fetch next from CURSORVC into @id, @expediente

-- Avanzamos el contador en uno
    set @contador = @contador + 1
   end

Y fínalmente cerramos el cursor y lo eliminamos de memoria

      close CURSORVC
deallocate CURSORVC

Y así, lo que por código tardaba horrores, pero horrores de los malos, ahora he actualizado los casi trescientos mil registros en menos de tres minutos.

P.D. Besitos al Borjus que me recordó que con experiencia un cursor como este se hace en unos 10 minutos, contra las cinco horas que me ha costado el primero mío :)

5 comentarios:

Anónimo dijo...

¡Me acabas de regalar por lo menos una hora! Exactamente lo que buscaba.Muchas gracias.

German dijo...

hola, la verdad necesito lo mismo que uds, pero desde visual 2010 o 2012.

necesito saber si el estado de un cliente es = false o 0 y saber, desde otra tabla, si la fecha que esta inserta en igual o menor a la actual. esto para el vencimiento de cuotas y el estado es para bloquear al cliente si no pago.


seria de mucha ayuda si alguien me puede apoyar.

Gracias!!!

Anónimo dijo...

Y @expediente la ocupaste para?

Anónimo dijo...

Muchas pero muchas gracias... me habia dado vueltas y vueltas para encontrar la manera de como actualizar las lineas de la tabla que su valor cambiaba por la salida de producto... era simple..mi linea decia que tenia 5 unidades..y la misma linea decia que tenia 3 comprometidos para vender.. osea que en la siguiente linea donde apareciera el produto no podia comparar con los antiguos 5 del stock .. si no que con la resta del stock menos lo comprometido (que eran 3).. por lo que mi nuevo stock eran 2 para la linea siguiente. Asi que MUCHAS GRACIAS ...

si a alguien le sirve.. dejo mi codigo.. saludos a todos:

declare @codigo_art as nvarchar(16)
declare @numnota as int
declare @item_notde as int
declare @veces as int
declare @cantidad_pend as numeric(38, 2)
declare @Total_STKyOC as numeric(38, 2)

declare @codigo_art_ANTERIOR as nvarchar(16)
declare @cantidad_pend_ANTERIOR as numeric(38, 2)
declare @Total_STKyOC_ANTERIOR as numeric(38, 2)

declare CURSORNV cursor for
select codigo_art,numnota,item_notde,veces, cantidad_pend, Total_STKyOC from T_Web_CheckNV_2015
Where veces > 1
Order by codigo_art,numnota,item_notde

SET @Total_STKyOC_ANTERIOR = (select top 1 Total_STKyOC from T_Web_CheckNV_2015 where veces > 1)
SET @cantidad_pend_ANTERIOR = 0
SET @codigo_art_ANTERIOR = (select top 1 codigo_art from T_Web_CheckNV_2015 where veces > 1)


open CURSORNV
fetch next from CURSORNV into @codigo_art, @numnota,@item_notde,@veces,@cantidad_pend,@Total_STKyOC

while @@fetch_status = 0

begin

update T_Web_CheckNV_2015 set Total_STKyOC = @Total_STKyOC - @cantidad_pend_ANTERIOR
where codigo_art=@codigo_art and numnota = @numnota and item_notde=@item_notde


SET @codigo_art_ANTERIOR = @codigo_art
SET @cantidad_pend_ANTERIOR = @cantidad_pend_ANTERIOR + @cantidad_pend

fetch next from CURSORNV into @codigo_art, @numnota,@item_notde,@veces,@cantidad_pend,@Total_STKyOC

IF @codigo_art <> @codigo_art_ANTERIOR

BEGIN
SET @cantidad_pend_ANTERIOR = 0

update T_Web_CheckNV_2015 set Total_STKyOC = @Total_STKyOC - @cantidad_pend_ANTERIOR
where codigo_art=@codigo_art and numnota = @numnota and item_notde=@item_notde

SET @codigo_art_ANTERIOR = @codigo_art
SET @cantidad_pend_ANTERIOR = @cantidad_pend_ANTERIOR + @cantidad_pend

fetch next from CURSORNV into @codigo_art, @numnota,@item_notde,@veces,@cantidad_pend,@Total_STKyOC

END


end


close CURSORNV
deallocate CURSORNV

Anónimo dijo...

Estimado, gracias por dejar el codigo fuente. me soluciono un problema super grande!
saludos.