Более эффективные запросы для получения последней записи за предыдущие месяцы, если она существует, или первой записи этого месяца, если нет - PullRequest
1 голос
/ 13 июля 2020

Я хотел бы спросить предложения о более эффективных способах эффективного выполнения запросов. Допустим, у меня есть таблица с именами payment_info и customer_master.

payment_info table:

created_date (отметка времени) | customer_id | category | payment_amount | carryover_amount

customer_master таблица:

customer_id | carryover_amount

Из этих таблиц мне нужно запросить данные за определенный месяц и год (интересующий месяц) следующим образом:

(1). Если существует запись до интересующего месяца, найдите carryover_amount последней записи из payment_info таблицы

(2). Если (1) не выполняется, найдите первую запись интересующего месяца и вычислите carryover_amount на основе категории из таблицы payment_info.

(3). Если (1) и (2) не удовлетворены, найдите carryover_amount из таблицы customer_master

Скажем: интересующий месяц 202007 (июль 2020 г.).

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

SELECT (CASE
        WHEN EXISTS (SELECT * FROM payment_info WHERE customer_id = a.customer_id AND to_char(created_date, 'YYYYMM') < '202007')
          THEN ( SELECT carryover_amount FROM payment_info WHERE customer_id = a.customer_id ORDER BY created_date DESC )
        WHEN EXISTS (SELECT * FROM payment_info WHERE customer_id = a.customer_id AND to_char(created_date, 'YYYYMM') = '202007')
          THEN ( SELECT (CASE
                         WHEN category = 1 THEN carryover_amount - payment_amount
                         ELSE carryover_amount + payment_amount
                         END)
                   FROM payment_info WHERE customer_id = a.customer_id AND to_char(created_date, 'YYYYMM') = '202007'
          )
        ELSE (SELECT carryover_amount FROM customer_master WHERE customer_id = a.customer_id)
        END) AS carryover
  FROM payment_info a

Этот запрос хорошо работает для небольшого количества данных в таблице payment_info. Однако, когда объем данных достаточно велик, этот запрос выполняется бесконечно. Я знаю, что указанный выше запрос неэффективен, но я не мог найти лучшего способа решить указанную выше проблему. Именно об этом я и хочу спросить в этом посте. Есть ли способ сделать более быстрый запрос относительно вышеуказанной проблемы. Любое предложение приветствуется.

Спасибо.

Примечание: я использую Postgres, и мне не разрешено изменять структуру таблицы .

Ответы [ 2 ]

2 голосов
/ 13 июля 2020

Я бы начал с попытки бокового соединения:

select cm.*,
       coalesce(pi.carryover_amount, cm.carryover_amount)
from customer_master cm left join lateral
     (select pi.*
      from payment_info pi
      where pi.customer_id = cm.customer_id and
            pi.created_date < to_date('202007' || '01', 'YYYYMMDD') + interval '1 month'
      order by pi.created_date < to_date('202007' || '01', 'YYYYMMDD') desc,
               (case when pi.created_date < to_date('202007' || '01', 'YYYYMMDD') then pi.created_date end) desc,
               pi.created_date asc
      limit 1
     ) pi
     on true;
1 голос
/ 13 июля 2020

Когда мы создаем запрос специально для большого набора данных, использование оператора select в операторе select - плохая практика. Пожалуйста, постарайтесь избежать этого.

Я предполагаю, что дата создания также имеет время. Вы можете изменить это в соответствии с вашими требованиями. Ниже показан скелет того, как я бы это сделал. Надеюсь, это то, что вы ищете:

With tmp AS(Select carrover_amount, customer_id from 
(Select carrover_amount, customer_id from payment_info 
where to_char(created_date, 'YYYYMM') < '202007' 
order by created_date desc) where row_num = 1)
    
,tmp2 AS(Select CASE WHEN category = 1 THEN carryover_amount - payment_amount
                ELSE carryover_amount + payment_amount END carrover_amount2, customer_id from (Select customer_id, carrover_amount, min(created_date) OVER() as first_dt, payment_amount, created_date from payment_info where to_char(created_date, 'YYYYMM') = '202007' 
order by created_date) where first_dt = created_date)
    
    
Select CASE WHEN t1.customer_id IS NOT NULL THEN t1.carrover_amount
            WHEN t2.customer_id IS NOT NULL THEN t2.carrover_amount2
            ELSE b.carryover_amount END carryover
from payment_info a LEFT OUTER JOIN tmp t1 ON a.customer_id = t1.customer_id
                    LEFT OUTER JOIN tmp2 t2 ON a.customer_id = t2.customer_id
                    LEFT OUTER JOIN customer_tbl b ON a.customer_id = b.customer_id;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...