Здравствуйте, Иванов Д.И.!
Рабочая база в формате MS Access, в которой проверены запросы, находится в
База для консультации 187532Небольшое описание таблиц
Таблица t1
acc
restrub
turncred
turndeb
date_n
Таблица t2
acc
summa_fond
date_n
stavka
При создании нового клиента необходимо добавлять во вторую таблицу начальные данные по процентам с датой меньше или равной дате добавления клиента.
Возможно, было бы лучше хранить два значения процентных ставок – отдельно для дебета и кредита. Это потребует незначительной переделки запросов.
Решение создано для MS Access 2000, но переход на MS SQL Server не займет много времени.
Для начала разобьем решение задачи на несколько этапов.
1. Получение суммы транша и процентной ставки на момент даты транша
SELECT *, ( select top 1 t2.stavka from t2 where t2.acc=t1.acc and t2.date_n<=t1.date_n order by t2.date_n desc ) as stavka FROM t1 where date_n>=cdate ( '1.2.2013' ) and date_n<=cdate ( '28.2.2013' )
Результатом будет таблица
acc restrub turncred turndeb date_n stavka
45208%1 0 0 50000 02.02.13 13
45208%1 0 0 70000 07.02.13 13
45208%1 0 0 30000 08.02.13 10
45208%1 0 0 90000 09.02.13 10
45208%1 0 0 40000 12.02.13 12
45208%1 0 0 25000 21.02.13 12
В которую выводятся все записи, входящие в диапазон дат, заданных условием. Процентная ставка берется из второй таблицы для даты, меньше или равной дате записи в первой таблице
2. Определение дней, в котором производились транши с привязкой к процентной ставке и виду операции – дебет или кредит. Также добавляется 1 день к счетчику дней, в которые производился транш за заданный период.
SELECT
acc as ac, date_n as dat, turncred as cred_sum,stavka as cred_stavka,iif ( turncred>0,1,0 ) as cred_day,turndeb as deb_sum,stavka as deb_stavka,iif ( turndeb>0,1,0 ) as deb_day
from
( запрос 1 )
В результате получим таблицу
ac dat cred_sum cred_stavka cred_day deb_sum deb_stavka deb_day
45208%1 02.02.13 0 13 0 50000 13 1
45208%1 07.02.13 0 13 0 70000 13 1
45208%1 08.02.13 0 10 0 30000 10 1
45208%1 09.02.13 0 10 0 90000 10 1
45208%1 12.02.13 0 12 0 40000 12 1
45208%1 21.02.13 0 12 0 25000 12 1
Перемножая сумму транша по дебету или кредиту с процентной ставкой получаем какое-то значение, которое используем в дальнейших расчетах. Также добавляем 1 день в счетчик дней, когда использовался транш.
3. Определяем среднее значение за период. При этом необходимо знать, сколько дней в этом периоде ( для февраля 2013 это 28 дней ) .
SELECT ac, sum ( cred_sum*cred_stavka ) AS cred_proc, sum ( cred_day ) AS cred_period, sum ( deb_sum*deb_stavka ) AS deb_proc, sum ( deb_day ) AS deb_period
FROM
( запрос 2 )
GROUP BY ac
В результате получим таблицу
ac cred_proc cred_period deb_proc deb_period
45208%1 0 0 3540000 6
Осталось рассчитать среднее значение по формуле
cred_proc * cred_period / 100 / 28 и
deb_proc * deb_period / 100 / 28
Проще это организовать пользовательской функцией в MS SQL Server с передачей значений начального и конечного периода с обязательной проверкой, чтобы конечный период был не меньше начального. Или добавить проверку в функцию с выводом пустой таблицы при ошибках.
GO
CREATE FUNCTION dbo.MyFunc
( @BegDat smalldatetime, @EndDat smalldatetime )
RETURNS TABLE
AS
RETURN (
SELECT ac, (cred_proc * cred_period / 100 / datediff ( ‘d’, @BegDat, dateadd ( 'd' , 1, @EndDat ) ) ) as cred_transh, (deb_proc * deb_period / 100 / datediff ( ‘d’, @BegDat, dateadd ( 'd' , 1, @EndDat ) ) ) as deb_transh
From (
SELECT ac, sum ( cred_sum*cred_stavka ) AS cred_proc, sum ( cred_day ) AS cred_period, sum ( deb_sum*deb_stavka ) AS deb_proc, sum ( deb_day ) AS deb_period
FROM
( SELECT
acc as ac, date_n as dat, turncred as cred_sum,stavka as cred_stavka,iif ( turncred>0,1,0 ) as cred_day,turndeb as deb_sum,stavka as deb_stavka,iif ( turndeb>0,1,0 ) as deb_day
from
( SELECT *, ( select top 1 t2.stavka from t2 where t2.acc=t1.acc and t2.date_n<=t1.date_n order by t2.date_n desc ) as stavka FROM t1 where date_n>=@BegDat and date_n<=@EndDat )
)
GROUP BY ac
) )
В MS Access получился результат
ac cred_transh deb_transh
45208%1 0 7585.71428571429
Так как в MS SQL Server 2012 уже существует функция IIF , то переделывать не нужно. Для остальных версий сервера необходимо заменить
Iif (turncred >0,1,0 ) на CASE turncred >0 THEN 0 ELSE 1 END
Iif ( turndeb>0,1,0 ) на CASE turndeb>0 THEN 0 ELSE 1 END
Вызов пользовательской функции осущевляется так
SELECT *
FROM dbo.MyFunc ( convert ( smalldatetime, ‘2/1/2013’,101 ) , convert ( smalldatetime, ‘2/28/2013’,101 ) )
Работоспособность в MS SQL Server не проверял.
Если будут еще вопросы – отвечу в мини-форуме.
С уважением.