Теперь должно быть ясно, что вам определенно нужен столбец, в котором указан последовательный порядок транзакций, потому что в противном случае вы не сможете решить, будет ли дебет размещен до или после кредита, когда они оба имеют одинаковый datestart
. Предполагая, что у вас есть такой столбец (в моем запросе я назвал его ID
), решение может быть следующим, без рекурсии и без самосоединения. Эту проблему можно решить с помощью некоторых оконных функций , доступных с SQL Server 2008.
Мое решение обрабатывает данные в несколько этапов, которые я реализовал в виде последовательности из 2 CTE и окончательного запроса PIVOT
:
DECLARE @StartDate DATE = '20170810';
DECLARE @EndDate DATE = dateadd(dd, 4, @StartDate);
DECLARE @DateRange nvarchar(24);
SET @DateRange =
CONVERT(nvarchar(10), @StartDate, 120) + ' to '
+ CONVERT(nvarchar(10), @EndDate, 120);
WITH
blocks (acct, CD, amount, blockno, r_blockno) AS (
SELECT acct, Credit_or_debit, amount
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID ASC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID ASC)
, ROW_NUMBER() OVER (PARTITION BY acct ORDER BY ID DESC)
- ROW_NUMBER() OVER (PARTITION BY acct, Credit_or_debit ORDER BY ID DESC)
FROM Transactions
WHERE datestart BETWEEN @StartDate AND @EndDate
AND Credit_or_debit IN ('C','D') -- not needed, if always true
),
blockpairs (acct, CD, amount, pairno) AS (
SELECT acct, CD, amount
, DENSE_RANK() OVER (PARTITION BY acct, CD ORDER BY blockno)
FROM blocks
WHERE (blockno > 0 OR CD = 'C') -- remove leading debits
AND (r_blockno > 0 OR CD = 'D') -- remove trailing credits
)
SELECT acct, @DateRange AS DateRange
, amt.C AS credit_amount, amt.D AS debit_amount
FROM blockpairs PIVOT (SUM(amount) FOR CD IN (C, D)) amt
ORDER BY acct, pairno;
И вот как это работает:
блоки
Здесь соответствующие данные извлекаются из таблицы, то есть применяется фильтр диапазона дат, а другой фильтр в столбце Credit_or_debit
гарантирует, что в результате содержатся только значения C и D (если это в зависимости от случая в вашей таблице эта часть предложения WHERE
может быть опущена). Важной частью этого CTE является разность двух чисел (blockno
). Кредиты и дебеты нумеруются отдельно, а их соответствующие номера вычитаются из общего номера строки. В последовательном блоке дебетов или кредитов эти числа будут одинаковыми для каждой записи, и они будут разными (более высокими) в более поздних блоках того же типа. Основное использование, если эта нумерация предназначена для идентификации самого первого блока (номер 0
), чтобы иметь возможность исключить его из
дальнейшая обработка на следующем шаге, если это дебетовый блок. Чтобы иметь возможность также идентифицировать самый последний блок (и отфильтровать его на следующем шаге, если это кредитный блок), аналогичная нумерация блоков выполняется в обратном порядке (r_blockno
). Результат (который я заказал только для визуализации с вашими примерами данных) будет выглядеть так:
![Result of the blocks CTE](https://i.stack.imgur.com/OEtVf.jpg)
blockpairs
В этом CTE, как описано выше, самый первый блок отфильтровывается, если это дебетовый блок, и самый последний блок отфильтровывается, если это кредитный блок. При этом количество оставшихся блоков должно быть четным, а логический порядок блоков должен быть последовательностью пар кредитных и дебетовых блоков, каждая из которых начинается с кредитного блока и сопровождается соответствующим дебетовым блоком. Каждая пара кредитных / дебетовых блоков в итоге будет иметь одну строку. Чтобы правильно связать кредитные и дебетовые блоки в запросе, я присваиваю им одинаковые номера, используя отдельные нумерации для каждого типа ( n -й кредитный блок и n -ый дебетовый блок связаны, давая им тот же номер n ). Для этой нумерации я использую функцию DENSE_RANK
, чтобы все записи в блоке получали одинаковое число (pairno
) и делали нумерацию без пробелов. Для нумерации блоков одного типа я повторно использую описанное выше поле blockno
для упорядочения. Результат в вашем примере (снова отсортирован для визуализации):
![Result of the blockpairs CTE](https://i.stack.imgur.com/3SYpt.jpg)
Финальный запрос PIVOT
Наконец, credit_amount
и debit_amount
агрегируются по соответствующим блокам, группирующимся по acct
и pairno
, а затем отображаются рядом друг с другом, используя запрос PIVOT
.
![Final result](https://i.stack.imgur.com/BzWEN.jpg)
Хотя столбец pairno
не виден, он используется для сортировки полученных записей.