sql—更高效的查询,可以获取前几个月的最后一条记录(如果存在),或者获取本月的第一条记录(如果不存在)

xmq68pz9  于 2021-07-26  发布在  Java
关注(0)|答案(2)|浏览(304)

我想问关于更好的方法做查询效率的建议。假设我有一个名为payment\u info和customer\u master的表。 payment_info 表格: created_date (时间戳) customer_id | category | payment_amount |
carryover_amount customer_master 表格: customer_id | carryover_amount 从这些表中,我需要根据特定的月份和年份(感兴趣的月份)查询数据,如下所示:
(1). 如果在感兴趣的月份之前存在记录,则查找 carryover_amount 最后一条记录 payment_info table
(2). 如果(1)不满足,则查找感兴趣月份的第一条记录,并计算 carryover_amount 基于 payment_info table。
(3). 如果(1)和(2)不满意,则查找 carryover_amountcustomer_master table
假设:感兴趣的月份是 202007 (2020年7月)。
我现在可以想到的select查询如下:

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\u info表中的少量数据。但是,当数据足够大时,此查询将永远使用。我知道上面的查询效率不高,但是我找不到更好的方法来解决上面的问题。这就是我在这篇文章里想问的。有没有办法做一个更快的查询关于上述问题的任何建议是赞赏的。
谢谢。
注意:我使用的是postgres,不允许更改表结构。

b5lpy0ml

b5lpy0ml1#

我会先尝试横向连接:

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;
e0uiprwp

e0uiprwp2#

当我们专门为大数据集构建查询时,在select语句中使用select语句是不好的做法。请尽量避免。
我假设创建日期也有时间。你可以根据你的要求改变。下面是我将如何做到这一点的 backbone 。我希望这就是你想要的:

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;

相关问题