Агрегации в SQLite - PullRequest
       9

Агрегации в SQLite

0 голосов
/ 26 октября 2018

Я хочу построить SQL-запрос, который может суммировать суммы взносов по месяцам. Обычно это не было бы слишком сложно, поскольку вы просто суммировали бы суммы взносов и group by month. Однако проблема не так проста, и в оставшейся части поста я проиллюстрирую причину и получу любую помощь, которую люди могут предложить.

Сначала важно отметить столбец installments. Где installments равно 1, это означает, что общая стоимость выплачивается во время покупки. Если installments больше 1, это означает, что общая стоимость выплачивается в текущем и последующих месяцах. Например, если мы видим transaction_id 9 и 10, это транзакция стоимостью 100 долларов США, состоящая из двух частей, что означает, что 50 долларов будут выплачены в феврале, а 50 долларов - в марте.

Учтите, что мы хотим видеть ежемесячные счета за credit_card_id = 11111111. Если мы посмотрим на столбец installments, то увидим, что правильный вывод должен быть следующим:

  • Январь: 19,99 + 75,3
  • Февраль: 1337 + 75,3
  • март: 75,3

Опять же, для ясности, 75,3 в марте происходит потому, что в январе у нас была транзакция с 3 частями, то есть с клиента будет взиматься 75,3 в январе, феврале и марте. Проблема в том, что я не знаю, как создать категорию для марта на основе данных.

Transactions

Во-первых, я заново создал таблицу в SQL и легко смог получить все транзакции для карты по месяцам с помощью следующего запроса SQLite

select strftime('%m', transaction_date) as Month, total_value, installment_value, installments 
from transactions 
WHERE credit_card_id = '11111111';

который выводит таблицу, которая выглядит следующим образом output

Однако было неочевидно, как разделить 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);

Мои вопросы

  1. Можно ли получить выходные данные формата, который я определил выше, в SQLite, и если да, то как?
  2. Должен ли я иметь столбец txn, чтобы получить эту информацию?

Спасибо за вашу помощь.

Ответы [ 3 ]

0 голосов
/ 26 октября 2018

Предполагая, что вы запускаете версию SQLite 3.25+, рассмотрите возможность использования CTE и оконной функции , которая создает количество выполнений с одинаковыми credit_card_id и action_dat e и использует это значение добавить необходимые месяцы к дате транзакции. Оттуда, агрегировать в соответствии с новой рассчитанной датой, 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 с оконными функциями

0 голосов
/ 27 октября 2018

Вот решение, которое не требует нумерации строк / будет работать в гораздо более старой версии 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

Кстати, что-то напутало в вашем вопросе или данных вашего примера.

Ежемесячные платежи за номер кредитной карты 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 лет - тривиальная разовая операция

0 голосов
/ 26 октября 2018

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

Проблема возникает, когда платежи превышают два года. Вам придется работать над этой частью. Я написал бы этот кусок кода (случай, когда ...) внутри функции, это сделало бы весь запрос более ясным.

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