Получить общую сумму за последние 3 транзакции каждого клиента за предыдущий месяц - PullRequest
1 голос
/ 01 февраля 2012

Мне нужно получить общую сумму за последние 3 транзакции каждого клиента за предыдущий месяц.Допустим, сегодня 2012/1/31.

Пожалуйста, предоставьте пошаговый ответ о том, почему вы используете подход, который вы используете.

Например, вот ответ, который я подумал,Это может быть неправильно.

  1. Создать курсор для итерации по группе таблиц CustomerTransaction по CustomerName
  2. Создать внутренний запрос, чтобы получить последние 3 транзакции клиента за предыдущий месяц (используя select top 3), вставьте его во временную таблицу
  3. Выберите результат во временной таблице и получите сумму (Amount) и сгруппируйте ее по CustomerName.

Так что яиметь таблицу CustomerTransaction со следующими столбцами:

ID, CustomerName, Amount, TransactionDate

Вот скрипт, если он вам нужен.Я использую его для проверки результата.

insert into Test.dbo.CustomerTransaction (CustomerName, Amount, TransactionDate)
values ('John', 100.0, '2011-12-31'),
('John', 100.0, '2011-12-30'),
('John', 100.0, '2011-12-29'),
('John', 100.0, '2011-12-28'),
('Boyd', 100.0, '2011-12-30'),
('Boyd', 200.0, '2011-12-29'),
('Boyd', 100.0, '2011-12-28'),
('Boyd', 100.0, '2011-12-27')

Ответы [ 4 ]

2 голосов
/ 01 февраля 2012

Я сам предпочитаю комбинацию Cross Apply & Top для простоты.

ОБНОВЛЕНИЕ - Я исправил расчет диапазона дат.

Также - если вам нужен запрос, чтобы быть детерминированным, либо используйте метод SELECT TOP N WITH TIES, либо добавьте первичный ключ или какой-то еще «уникальный код» в предложение ORDER BY запроса SELECT TOP.

DECLARE @fd AS DATETIME;
DECLARE @ld AS DATETIME;
SET @fd = (dateadd(month, datediff(month, -1, getdate()) - 2, -1) + 1);
SET @ld =  dateadd(month, datediff(month, -1, getdate()) - 1, -1);

WITH Customers AS (
    SELECT CustomerName
    FROM tempdb.dbo.CustomerTransaction
    GROUP BY CustomerName
)
SELECT C.CustomerName,
       SUM(Amount) AS Total
FROM Customers AS C
CROSS APPLY (
    SELECT TOP (3) Amount
    FROM tempdb.dbo.CustomerTransaction AS T
    WHERE TransactionDate BETWEEN @fd AND @ld
      AND T.CustomerName = C.CustomerName
    ORDER BY TransactionDate DESC
) Q
GROUP BY C.CustomerName;

Результаты:

CustomerName Total
------------ ---------------------------------------
Boyd         400.00
John         300.00
1 голос
/ 28 марта 2012

Я думаю, что ответ все еще неправильный, но очень близкий. Это все еще пропускает последний день.
Последний день месяца в 12:00:00 - это конец предыдущего дня. Чтобы включить полный последний день, он должен идти до 11: 59: 59.999 PM

DECLARE @fd AS TIMEDATE;
DECLARE @ld AS TIMEDATE;

SET @fd = (dateadd(month, datediff(month, -1, getdate()) - 2, -1) + 1); 
SET @ld =  (dateadd(ms, -2, dateadd(month, datediff(month, -1, getdate()) - 1, -1) + 1));

PRINT @fd
PRINT @ld
0 голосов
/ 01 февраля 2012
declare @d datetime = '2012-01-31'

declare @CustomerTransaction table(CustomerName varchar(10), Amount money, TransactionDate datetime) 

insert into @CustomerTransaction 
(CustomerName, Amount, TransactionDate) 
values ('John', 100.0, '2011-12-31'), 
('John', 100.0, '2011-12-30'), ('John', 100.0, '2011-12-29'), 
('John', 100.0, '2011-12-28'), ('Boyd', 100.0, '2011-12-30'), 
('Boyd', 200.0, '2011-12-29'), ('Boyd', 100.0, '2011-12-28'), 
('Boyd', 100.0, '2011-12-27') 

;with cte as
(
select CustomerName, Amount, TransactionDate, 
rn = row_number() over (partition by customername order by transactiondate desc) 
from @CustomerTransaction--replace with: from Test.dbo.CustomerTransaction 
where datediff(month, TransactionDate, @d) = 1
)
select CustomerName, Amount, TransactionDate 
from cte where rn < 4
0 голосов
/ 01 февраля 2012

Это было забавно!

http://sqlfiddle.com/#!3/ae6fd/20

with lastMonth as
  (

    select * from CustomerTransaction
    where
    TransactionDate >= DateAdd(m, -1, cast(cast(month(getdate()) as varchar) + '/1/' + cast(year(getdate()) as varchar) as date)) AND

    TransactionDate < cast(cast(month(getdate()) as varchar) + '/1/' + cast(year(getdate()) as varchar) as date)
  ) 


select
  customerName,
  sum(Amount) as total
from
 lastMonth lm
where 
  not exists (

    select 1 from lastMonth lm2 
    WHERE lm2.customerName = lm.customerName AND
      exists (
        select 1 from lastMonth lm3
        WHERE lm3.customerName = lm2.customerName AND
              exists (
                select 1 from lastMonth lm4
                WHERE lm4.customerName = lm3.customerName AND
                lm4.TransactionDate > lm3.TransactionDate
              ) AND
              lm3.TransactionDate > lm2.TransactionDate
      ) AND
      lm2.TransactionDate > lm.TransactionDate

  )
group by
  customerName

Шаг за шагом, почему я сделал это так:

LastMonth как CTE, поэтому я быне нужно фильтровать базовый набор данных многократно, как по производительности, так и по удобочитаемости.

Последние три значения выполняются через цепочку не существует + существует.Они служат для фильтрации данных из lastMonth, так что будет разрешено возвращать только три записи (выполненные через то, что составляет три объединения).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...