在两个不同的横向连接中具有相同的输出

时间:2018-10-02 06:52:37

标签: sql postgresql aggregate-functions lateral-join

我正在使用PostgreSQL,以获取特定日期之间每个月的前10张和后10张发票。我在横向连接中有意外的输出。首先,限制不起作用,并且每个array_agg聚合都返回数百行,而不是限制为10行。其次,即使一个聚合被排序ASC且其他DESC

我如何只检索每个月组的前10张发票和最后10张发票?

SELECT first.invoice_month,
       array_agg(first.id) first_ten,
       array_agg(last.id) last_ten
FROM public.invoice i
   JOIN LATERAL (
      SELECT id, to_char(invoice_date, 'Mon-yy') AS invoice_month
      FROM  public.invoice
      WHERE id = i.id
      ORDER BY invoice_date, id ASC
      LIMIT 10
   ) first ON i.id = first.id
   JOIN LATERAL (
      SELECT id, to_char(invoice_date, 'Mon-yy') AS invoice_month
      FROM public.invoice
      WHERE id = i.id
      ORDER BY invoice_date, id DESC
      LIMIT 10
   ) last on i.id = last.id
WHERE i.invoice_date BETWEEN date '2017-10-01' AND date '2018-09-30'
GROUP BY first.invoice_month, last.invoice_month;

invoice output

2 个答案:

答案 0 :(得分:0)

event generate工作正常。是您的查询已损坏。 LIMIT只是此处错误工具的100%;它甚至没有做任何接近您需要的事情。通过将最多10行与最多另外10行连接起来,您最多可以返回100行。也没有理由仅合并过滤器就自行加入。

请考虑使用窗口查询。特别是,我们具有JOIN函数,该函数可以根据组对结果集中的每一行进行编号:

dense_rank

不要被长度吓倒。这个查询实际上非常简单。它只需要几个子查询。

这是其逻辑上的作用:

  • 获取相关会计年度的行
  • 从当月开始和结束算起,在当月的行中分配“等级”
  • 筛选出当月未排在前10名的所有内容(从任一方向计数)
  • 添加一个指示符,指示它是在月初还是月末。 (请注意,如果一个月中的行数少于20行,则会将其中更多行归类为“开始”。)
  • 将ID汇总在一起

这是专为您要完成的工作而设计的工具集。如果确实需要,可以略微调整此方法以使它们进入同一行,但是必须先进行汇总 ,然后再将结果结合在一起,然后在当月加入;您不能加入然后聚合。

答案 1 :(得分:0)

这可以通过递归查询来完成,该查询将生成一个月间隔,以了解我们需要查找前10个发票的人。

WITH RECURSIVE all_months AS (
  SELECT date_trunc('month','2018-01-01'::TIMESTAMP) as c_date, date_trunc('month', '2018-05-11'::TIMESTAMP) as end_date, to_char('2018-01-01'::timestamp, 'YYYY-MM') as current_month
  UNION
  SELECT c_date + interval '1 month' as c_date,
    end_date,
    to_char(c_date + INTERVAL '1 month', 'YYYY-MM') as current_month
  FROM all_months
  WHERE c_date + INTERVAL '1 month' <= end_date
),
  invocies_with_month as (
    SELECT *, to_char(invoice_date::TIMESTAMP, 'YYYY-MM') invoice_month FROM invoice
  )
SELECT current_month, array_agg(first_10.id), 'FIRST 10' as type FROM all_months
JOIN LATERAL (
    SELECT * FROM invocies_with_month
    WHERE all_months.current_month = invoice_month AND invoice_date >= '2018-01-01' AND invoice_date <= '2018-05-11'
    ORDER BY invoice_date ASC limit 10
  ) first_10 ON TRUE
GROUP BY current_month
UNION
SELECT current_month, array_agg(last_10.id), 'LAST 10' as type FROM all_months
JOIN LATERAL (
      SELECT * FROM invocies_with_month
    WHERE all_months.current_month = invoice_month AND invoice_date >= '2018-01-01' AND invoice_date <= '2018-05-11'
    ORDER BY invoice_date DESC limit 10
) last_10 ON TRUE
GROUP BY current_month;

在上面的代码中,“ 2018-01-01”和“ 2018-05-11”代表我们要查找发票之间的日期。根据这些日期,我们生成需要查找其发票的月份(2018-01、2018-02、2018-03、2018-04、2018-05)。 我们将此数据存储在 all_months 中。

在获得月数之后,我们进行横向合并以合并每个月的发票。我们需要2个横向联接才能获得前10个发票。 最后,结果表示为:

当前月-月

array_agg -该月所有选定发票的ID

类型-所选发票的类型(“前10个”或“后10个”)。

因此,在当前实施中,您每个月将有2行(如果该月至少有1张发票)。您可以根据需要轻松地将其加入一行。