Я хочу создать SQL-запрос, который может суммировать суммы взносов по месяцам. Обычно это не было бы слишком сложно, так как вы просто суммируете стоимость рассрочки и месяц group by
. Однако проблема не так проста, и в оставшейся части поста я проиллюстрирую, почему, и буду просить людей о любой помощи, которую могут предложить.
Прежде всего, важно отметить столбец installments
. Если installments
равен 1, это означает, что полная стоимость оплачивается во время покупки. Если installments
больше 1, это означает, что общая стоимость выплачивается как в текущем, так и в следующих месяцах. Например, если мы видим transaction_id
9 и 10, это транзакция на 100 долларов с двумя частями, что означает, что 50 долларов будут выплачены в феврале, а 50 долларов будут выплачены в марте.
Учтите, что мы хотим видеть ежемесячные счета за credit_card_id = 11111111
. Если мы посмотрим на столбец installments
, мы увидим, что правильный вывод должен быть следующим:
Опять же, для ясности, 75,3 в марте происходит потому, что в январе у нас была транзакция с тремя частями, что означает, что с клиента будет взиматься плата в размере 75,3 в январе, феврале и марте. Проблема в том, что я не знаю, как создать категорию на март из предоставленных данных.
Во-первых, я воссоздал таблицу в SQL и легко смог получить все транзакции для карты по месяцам с помощью следующего запроса SQLite
select strftime('%m', transaction_date) as Month, total_value, installment_value, installments
from transactions
WHERE credit_card_id = '11111111';
который выводит таблицу, которая выглядит так:
Однако было неочевидно, как разделить 3 периода рассрочки на 01, 02 и 03, поэтому я создал новую таблицу со столбцом txn
, который предназначен для присвоения идентификатора уникальным транзакциям, которые можно рассматривать как 1 группа.
CREATE TABLE transactions (
transaction_id int primary key,
credit_card_id int,
transaction_date timestamp,
merchant_name varchar(256),
total_value decimal(19,4),
installment_value decimal(19,4),
installments int,
txn int
);
insert into transactions values(1,11111111,'2018-01-10T00:00:00','Colorful Soaps', 19.99, 19.99, 1, 1);
insert into transactions values(2,22222222,'2018-01-11T00:01:00','Cantina da Mamma',43.5,43.5,1,2);
insert into transactions values(3,33333333,'2018-01-12T01:02:00','Boulevard Hotel',129,129,1,3);
insert into transactions values(4,11111111,'2018-01-15T11:11:11','Micas Bar',225.9,75.3,3,4);
insert into transactions values(5,11111111,'2018-01-15T11:11:11','Micas Bar',225.9,75.3,3,4);
insert into transactions values(6,11111111,'2018-01-15T11:11:11','Micas Bar',225.9,75.3,3,4);
insert into transactions values(7,22222222,'2018-01-18T22:10:01','IPear Store',9999.99,9999.99,1,5);
insert into transactions values(8,11111111,'2018-02-20T21:08:32','Forrest Paintball',1337,1337,1,6);
insert into transactions values(9,44444444,'2018-02-22T00:05:30','Unicorn Costumes',100,50,2,7);
insert into transactions values(10,44444444,'2018-02-22T00:05:30','Unicorn Costumes',100,50,2,7);
Мои вопросы
txn
, чтобы получить эту информацию?Спасибо за помощь.
SQLlite имеет ROW_NUMBER()
(проверено здесь SQLlite).
SELECT
installment_month
,credit_card_id
,SUM(installment_value)
FROM (
SELECT
CASE WHEN strftime('%m',transaction_date) + ROW_NUMBER () OVER(PARTITION BY credit_card_id, transaction_date ORDER BY transaction_date) - 1 > 12
THEN strftime('%Y',transaction_date)*100+strftime('%m',transaction_date) + ROW_NUMBER () OVER(PARTITION BY credit_card_id, transaction_date ORDER BY transaction_date) - 1 + 88
ELSE strftime('%Y',transaction_date)*100+strftime('%m',transaction_date) + ROW_NUMBER () OVER(PARTITION BY credit_card_id, transaction_date ORDER BY transaction_date) - 1
END as installment_month
,*
from transactions
) AS a
GROUP by installment_month, credit_card_id
Проблема возникает, когда рассрочка превышает два года. Вам придется поработать с этой частью. Я бы написал этот кусок кода (случай, когда ...) внутри функции, это сделало бы весь запрос более понятным.
Измените данные в таблице, выполнив взнос больше 1 декабря. Принятый ответ не удастся. Сделайте то же самое с датой рассрочки более 13 декабря, мой ответ не будет успешным. @Caius Jard ответит, что он не подведет, пока ваш календарь достаточно велик. Ответ просто должен заключаться в том, что сейчас генерируется слишком много строк для каждого credit_card_id, и мы не получим ожидаемого результата.
@ g.humpkins (редактирование отправлено) вы можете выполнить требования, используя select distinct
внутри предложения from
, используя вывод inner join
. Вы получите ожидаемый результат. Но, может быть, есть способ получше, давайте подождем и посмотрим ...
Предполагая, что вы используете версию SQLite 3.25+, рассмотрите возможность использования CTE и оконная функция, который создает текущий счетчик по тем же credit_card_id и transaction_date и использует это значение для добавления необходимых месяцев к дате транзакции. Оттуда агрегируйте в соответствии с новой расчетной датой install_date.
WITH cte AS
(SELECT *,
DATE(transaction_date,
'+' || (ROW_NUMBER()
OVER(PARTITION BY transaction_date, credit_card_id
ORDER BY transaction_date) - 1)
|| ' month'
) AS install_date
FROM transactions)
SELECT credit_card_id,
STRFTIME('%Y', install_date) AS install_year,
STRFTIME('%m', install_date) AS install_month,
SUM(installment_value) AS sum_installment_value
FROM cte
GROUP BY credit_card_id,
STRFTIME('%Y', install_date),
STRFTIME('%m', install_date)
ORDER BY credit_card_id,
STRFTIME('%Y', install_date),
STRFTIME('%m', install_date);
Демо Rextesterс использованием PostgreSQL, поскольку AFAIK без онлайн-скрипта (SQLFiddle, SQLiteonline, DBFiddle и т. д.) поддерживает SQLite с оконными функциями
Будьте осторожны, когда рассрочка + transaction_date превышает год, он не сработает. Я попытался предупредить об этом в своем ответе (в случае необходимости).
Вот решение, которое не требует нумерации строк / будет работать в гораздо более старом SQLite (в любой версии, которая поддерживает date () в основном). Он просто полагается на соединение с календарной таблицей (которую вы можете создать с помощью различных методов, но в связанном примере я просто сгенерировал N строк, создав таблицу и выполнив прямую вставку данных, которые мне нужны для запроса), имеет одну строку для 1-го числа каждого месяца. Он использует декартово условие соединения, которое вызывает, например, 3 строки для каждого платежа, состоящего из 3-х частей:
select
t.credit_card_id,
date(cal.d, '-1 month') as month_of_installment,
sum(t.installment_value)
from
cal inner join transactions t on
t.transaction_date between date(cal.d, '-'||installments||' months') and cal.d
group by
t.credit_card_id,
date(cal.d, '-1 month')
Вы можете увидеть настройку на https://www.db-fiddle.com/f/ogj2hK3cMwqp46MY6uVwX8/0
Между прочим, в вашем вопросе или в данных вашего примера что-то не так.
Ежемесячные платежи для кредитной карты ID 11111111 составляют:
2018-01 245.89
2018-02 1562.9
2018-03 225.9
Данные вашего примера содержат 3 одновременных платежа на счет Micas Bar. Мы знаем, что они разные, потому что у них другой идентификатор транзакции, несмотря на идентичные другие данные. Таким образом, январь составляет 75,3 + 75,3 + 75,3 + 19,99, а не только 19,99 + 75,3, как утверждается в вопросе.
Чтобы узнать больше о том, как работает запрос, запустите несгруппированную форму:
select
t.credit_card_id,
date(cal.d, '-1 month') as month_of_installment,
t.*
from
cal inner join transactions t on
t.transaction_date between date(cal.d, '-'||installments||' months') and cal.d
order by
t.credit_card_id,
date(cal.d, '-1 month')
Большинство администраторов баз данных, которых я встречал, выступают за использование таблицы чисел / дат в базе данных для генерации таких запросов - это быстрый способ генерации последовательностей строк, который дает вам строку, к которой можно присоединиться в тех случаях, когда есть, например, нет транзакций в течение месяца (вы можете оставить присоединиться к таблице транзакций в таблице календаря и получить одну строку, сумма которой равна 0, для месяцев без транзакций). Создание группы строк месяцев / дней на следующие 100 лет - тривиальная одноразовая операция.
если размер взноса больше 1, значение installment_value должно распространяться на каждый месяц. Ответ отредактирован в соответствии с требованиями.
SNR, не могли бы вы объяснить, почему / как происходит сбой, если рассрочка превышает два года?