Despues de un buen rato de depuracion, por fin tengo lo que necesito, comparto el código, muy seguramente se puede optimizar, a mi no me gusta usar cursores asi que los implemento en ciclos while.
Código SQL
[-]
create procedure sp_c_pago_reemplazo @Total numeric(19,2),@Fecha datetime
as
set nocount ON
declare @Pago table (n int identity(1,1),pag_id int,pag_total numeric(19,2))
declare @Resultado table (n int,pag_id int,pag_total numeric(19,2))
-- Recupero los Pagos del Mes
insert @pago
select
pag_id,pag_total
from pago
where pag_total<=@Total
and pag_status='A'
and pag_id not in (select pag_id from cfdi_pago)
and year(pag_fecha)=year(@Fecha)
and month(pag_fecha)=month(@Fecha)
order by pag_total desc,pag_id desc
declare
@pag_id int,
@pag_total_idx numeric(19,2),
@pag_total numeric(19,2),
@n int,
@l int,
@n_ int,
@l_ int,
@c int,
@r int,
@Suma numeric(19,2)
select @n=MIN(n),@l=MAX(n),@Suma=0 from @Pago
while (@n<=@l)
begin
-- Limpio tabla de resultados
delete from @Resultado
set @Suma=0
-- Tomo el primer pago
select @pag_total_idx=pag_total,@pag_id=pag_id from @pago where n=@n
if @pag_total_idx<=@Total
insert @Resultado select @n,@pag_id,@pag_total_idx
-- Verifico si se encontro el total
if @Total=@pag_total_idx
Goto Final
else
begin
select
@c=COUNT(n),
@r=0
from @Pago
where pag_total<=@Total-@pag_total_idx
print 'Repeticiones '+cast(@c as varchar)
while @r<@c
begin -- Repito la busqueda para todas las combinaciones
delete from @Resultado where n>@n
set @Suma=0
select
@n_=MIN(n)+@r,
@l_=@l
from @Pago
where pag_total<=@Total-@pag_total_idx
while @n_<=@l_
begin
select @pag_total=pag_total,@pag_id=pag_id from @pago where n=@n_
--print 'Nivel '+cast(@pag_total as varchar)
if (@pag_total_idx+@Suma+@pag_total)<=(@Total)
begin
insert @Resultado select @n_,@pag_id,@pag_total
set @Suma=@Suma+@pag_total
end
if (@Suma+@pag_total_idx)=@Total -- Verifico si se encontro el total
Goto Final
set @n_=(select MIN(n) from @Pago where n>@n_ and pag_total<@pag_total)
end
set @r=@r+1
end
end
set @n=(select MIN(n) from @pago where n>@n and pag_total<@pag_total_idx)
end
Final:
select pag_id,pag_total from @Resultado
De manera breve explico lo que hace.
Recupero y guardo en memoria los pagos que no se hayan facturado todavia y que el monto sea menor ó igual al importe deseado, guardo en memoria para poder trabajar con esos datos sin tener que acceder a las tablas fisicas debido a que el sistema esta siendo usado y tiene un alto indice de concurrencia. Guardo los valores minimo y maximo de Ids de los pagos para recorrerlos en un ciclo while primario el cual se encargara de recorrer uno a uno los pagos de manera descendente en el monto.
Si encuentro un pago igual a el monto que estoy buscando, finalizo el ciclo y muestro los resultados.
Si no encuentro un pago igual al monto, entonces tengo uno con un monto menor ya que asi esta establecida la consulta y procedo a realizar una sub-busqueda en la tabla de pagos que cargue en memoria pero iniciando desde los pagos que potencialmente sumados den como resultado la cantidad deseada, es decir no recorro todos los registros, para eso agrego la sentencia
where pag_total<=@Total-@pag_total_idx.
En el peor de los casos tengo que repetir esta sub-busqueda tantas veces como registros en la sub-busqueda se encuentren, sumando todos los registros encontrados entre si hasta hayar la combinacion correcta.
Bueno soy malo para explicar, pero me funciona de maravilla.
de una tabla de pagos de el mes de enero 2012, tengo 1766 registros, despues de ejecutar el sp con los parametros 35000,'01/01/2012' obtengo los siguientes resultados
Código:
pag_id pag_total
198232 18000.00
198191 2000.00
197846 1956.50
197296 1937.00
196831 1823.50
196806 1812.00
197823 1718.50
198264 1653.50
197260 1557.00
197546 1500.00
198277 1031.00
197692 11.00
la suma es correcta, estoy buscando los pagos que me sumen 35,000.00 pesos para elaborar una factura por esta cantidad (por ejemplo) ya que el mes pasado falto elaborarle una factura a un cliente por este monto, como no hay un pago en este mes por esa cantidad, busco los pagos que tengo libres para poder generar esa factura.
Gracias por sus consejos!