Рекомендации, необходимые для заполнения исторической таблицы postgresql несколькими строками на дату и время

#sql #postgresql #date #insert

Вопрос:

У меня есть SQL-запрос, который генерирует несколько строк за каждый час с информацией о рынке (по одной строке на рынок), например, накопленное количество уникальных клиентов на тот момент из таблицы, которая выглядит следующим образом, за которой следует запрос:

organization_id транзакция_id транзакция_date_time Торговая площадка
6789 80031 2021-06-07 15:33 3
6789 80032 2021-06-07 15:34 3
6789 80033 2021-06-07 15:44 3
6789 80034 2021-06-07 15:45 3
1234 80035 2021-06-17 14:07 1
1234 80036 2021-06-17 14:07 1
4321 80037 2021-07-05 11:51 2
4321 80038 2021-07-05 11:51 2
1234 80039 2021-07-13 15:41 1
1234 80040 2021-07-14 09:41 1
1234 80041 2021-07-14 09:55 1
     select
        date_trunc('hour', current_timestamp) as "date_time"
        ,ct.country_code as market
        ,count(distinct ct.organization_id) acc_alltime_organizations
        ,count(distinct case when ct.transaction_date_time >= current_date - interval '30' day then ct.organization_id else null end) acc_1m_organizations
    from customer_transactions ct
    group by
            "date_time"
            ,market
 

Выше приведен пример таблицы, которую я создаю (на самом деле накоплений больше, но это не должно кардинально изменить настройку) для метки current_timestamp. Мне бы хотелось, чтобы эти строки относились не только к текущей отметке времени, но и к каждому часу, например, с тех пор 2016-01-01 10:00:00 и до сих пор. Это означает, что, например, я хочу знать, что за 2018-09-09 00:00:00 месяц, предшествующий этой дате, было 10000 организаций, которые когда-либо использовали продукт, и 1000 уникальных организаций, использовавших продукт.

Столбец со всеми часами, для которых мне нужны эти строки, может быть сгенерирован следующим образом:

 select count(*)
from generate_series(
    (select date_trunc('hour', min(ct.transaction_date_time))
                from customer_transactions ct 
    ),
    current_date,
    interval '1 hour'
) as t("date_time")
)
 

Желаемый результат выглядит примерно так, как показано в следующей таблице:

дата и время Торговая площадка acc_alltime_организации acc_1m_организации
2016-01-01 10:00 1 10 5
2016-01-01 10:00 2 9 4
2016-01-01 10:00 3 8 3
2016-01-01 10:00 4 7 2
2016-01-01 11:00 1 10 5
2016-01-01 11:00 2 9 4
2016-01-01 11:00 3 8 3
2016-01-01 11:00 4 7 2
2016-01-01 12:00 1 11 6
2016-01-01 12:00 2 10 5
2016-01-01 12:00 3 9 4
2016-01-01 12:00 4 8 3

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

Если есть другой метод, кроме зацикливания, который предпочтительнее, пожалуйста, также дайте мне знать :). Помощь в этом была бы очень признательна!

Комментарии:

1. . . . Пожалуйста, предоставьте примерные данные и желаемые результаты. Ваш первый запрос синтаксически неверен (я думаю , что в нем просто отсутствует a group by , но неясно, чего еще может не хватать).

2. Вы правы, я удалил слишком много из исходного запроса, я обязательно исправлю его. Я также добавлю немного примеров данных, столько, сколько смогу :).

Ответ №1:

Если это ваш первоначальный запрос:

 select date_trunc('hour', current_timestamp) as date_time,
       ct.country_code as market,
       count(distinct ct.organization_id) as acc_alltime_organizations,
       count(distinct case when ct.transaction_date_time >= current_date - interval '30' day then ct.organization_id end) as acc_1m_organizations
from customer_transactions ct
group by ct.country_code;
 

Затем вы можете изменить его в течение нескольких часов, используя:

 select h.date_time,
       t.country_code as market,
       count(distinct ct.organization_id) as acc_alltime_organizations,
       count(distinct case when ct.transaction_date_time >= h.date_time - interval '30' day then ct.organization_id end) as acc_1m_organizations
from (select generate_series(min(ct.transaction_date_time), current_date, interval '1 hour') as date_time
      from customer_transactions ct
     ) h left join
     customer_transactions ct
     on ct.transaction_date_time <= h.date_time
group by h.date_time, ct.country_code
 

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

Комментарии:

1. Спасибо вам за ваш ответ. Я добавил еще немного информации о образцах набора данных, который у меня есть, и желаемом результате. Я попытался запустить ваш сценарий, и, к сожалению, его запуск занимает очень много времени, даже если я запускал его только в течение последних 3 часов (более 500 секунд для этого и подсчета) на меньшей выборке набора данных для разработки. Если бы вы могли дать несколько советов по более эффективному решению, это было бы здорово. Я не возражаю против логики, пока я получаю результат (несколько) эффективно. Спасибо!