Если у вас есть SQL Server 2014, вы можете использовать LAG
или LEAD
функция заглянуть в другие строки, что упрощает:
Select PersonId, Sum(InfrequentPayment) InfrequentPayments
from
(
select PersonId
, case
when dateadd(day,@period,paymentdate) < coalesce(lead(PaymentDate) over (partition by personid order by PaymentDate),getutcdate())
then 1
else 0
end InfrequentPayment
from @Payment
) x
Group by PersonId
Демо: http://sqlfiddle.com/#!6/9eecb7d/491
Объяснение:
Внешний SQL является довольно тривиальным; мы берем результаты внутреннего SQL, группы PersonId, и подсчитываем/суммируем количество раз, когда они выплатили платеж, оцененный как нечастый.
Внутренний SQL также прост; мы выбираем каждую запись, принимая к сведению человека и оцениваем ли этот платеж (точнее, задержку после этого платежа) нечасто.
В заявлении дела указано, что представляет собой нечастую оплату. Здесь мы говорим, что если платеж с датой записи плюс 90 дней остается раньше следующего платежа (или текущей даты, если это последний платеж, так что нет следующего платежа), то это редко (1); иначе это не (0).
coalesce
просто предназначен для обработки последней записи для человека; то есть, если нет следующего платежа, используется текущая дата (таким образом, удержание последнего, кто был последним, за 90 дней до сегодняшнего дня).
Теперь для «умного» бита: lead(PaymentDate) over (partition by personid order by PaymentDate)
. LEAD
- это новая функция SQL, которая позволяет вам смотреть запись после текущей (LAG
- чтобы увидеть предыдущую запись).
Если вы знакомы с row_number()
или rank()
, вы уже можете понять, что здесь происходит. Чтобы определить запись после текущей, мы не смотрим на текущий запрос; скорее мы укажем предложение order by
только для этой функции; это то, что находится в скобках после ключевого слова over
. Мы также хотим сравнить только даты платежа каждого человека с другими платежами, сделанными ими; а не любым клиентом. Для этого мы используем предложение partition by
.
Я надеюсь, что это имеет смысл/соответствует вашему требованию. Скажите, если что-то неясно, и я попытаюсь улучшить свое объяснение.
EDIT
Для более старых версий SQL, тот же эффект может быть достигнут за счет использования или ROW_NUMBER
и LEFT OUTER JOIN
; то есть
;with cte (PersonId, PaymentDate, SequenceNo) as
(
select PersonId
, PaymentDate
, ROW_NUMBER() over (partition by PersonId order by PaymentDate)
from @Payment
)
select a.PersonId
, sum(case when dateadd(day,@period,a.paymentdate) < coalesce(b.paymentdate,getutcdate()) then 1 else 0 end) InfrequentPayments
from cte a
left outer join cte b
on b.PersonId = a.PersonId
and b.SequenceNo = a.SequenceNo + 1
Group by a.PersonId
Другой метод, который должен работать на большинстве баз данных (хотя и менее эффективный)
select PersonId
, sum(InfrequentPayment) InfrequentPayments
from
(
select PersonId
, case when dateadd(day,@period,paymentdate) < coalesce((
select min(PaymentDate)
from @Payment b
where b.personid = a.personid
and b.paymentdate > a.paymentdate
),getutcdate()) then 1 else 0 end InfrequentPayment
from @Payment a
) x
Group by PersonId
структура таблицы? –
Поскольку ваш вопрос не имеет специфики (см. Комментарии выше), я могу предоставить общий подход без SQL. Вам необходимо упорядочить платежи последовательно для каждого клиента или самостоятельно присоединить каждую строку к хронологическому «следующему». Затем для каждой из этих двух строк используйте функции даты, специфичные для СУБД, для определения количества дней между датами, сохраняя только те, у которых разница> = 90 дней. Затем group byIDID и подсчитайте номер. – Turophile
Просто ищите общий подход. Структура таблицы: идентификатор, личный идентификатор, CreateDate – gene