如何获得Postgres过去12周的发票数量

时间:2015-06-17 21:34:02

标签: sql postgresql asp.net-mvc-3 aggregate-functions generate-series

发票数据库包含发票日期:

create table dok (
  dokumnr serial primary key,
  invoicedate date not null
);

仪表板需要以逗号分隔的列表,其中包含过去12周的发票数量,e.q

4,8,0,6,7,6,0,6,0,4,5,6

列表总是包含12个元素。如果在7天的间隔内没有发票,则应显示0。 每个元素应包含7天的发票数量。

查询应该在当前日期之前找到最大日期:

select max(invoicedate) as last_date from dok;

之后可能会使用count(*)和string_agg()来创建列表。

最后(第12个)元素应包含

的发票数量
  last_date .. last_date-interval'6days'

11个元素(前一个)应包含天数的发票数

  last_date-interval'7days' .. last_date-interval'14days'

如何在Postgres 9.1+中编写此查询? 这是ASP.NET MVC3 C#应用程序,如果有帮助,查询的某些部分也可以用C#代码完成。

我以

结束
with list as (

SELECT count(d.invoicedate) as cnt
   FROM  (
      SELECT max(invoicedate) AS last_date
      FROM   dok
      WHERE   invoicedate< current_date
      ) l
   CROSS  JOIN generate_series(0, 11*7, 7) AS g(days)
   LEFT   JOIN dok d ON d.invoicedate>  l.last_date - g.days - 7
                    AND d.invoicedate<= l.last_date - g.days
   GROUP  BY g.days
   ORDER  BY g.days desc
)

SELECT string_agg( cnt::text,',')
from list

1 个答案:

答案 0 :(得分:1)

CROSS JOIN generate_series()的最新日期,然后是主表的LEFT JOIN

SELECT ARRAY(
   SELECT count(d.invoicedate) AS ct
   FROM  (
      SELECT max(invoicedate) AS last_date
      FROM   dok
      WHERE  invoicedate < current_date  -- "maximum date before current date"
      ) l
   CROSS  JOIN generate_series(0, 11*7, 7) AS g(days)
   LEFT   JOIN dok d ON d.invoicedate >  l.last_date - g.days - 7
                    AND d.invoicedate <= l.last_date - g.days
   GROUP  BY g.days
   ORDER  BY g.days
   );

假设表格中至少有一个有效条目,
这将返回一个bigint数组(bigint[]),其中包含最新的第一周

current_date取决于您会话的timezone设置。

如果您需要将结果设置为以逗号分隔的字符串,则可以使用另一个string_agg()的查询图层。或者您将以上内容提供给array_to_string()

SELECT array_to_string(ARRAY(SELECT ...), ',');

您的查询已审核:

这是一个实施细节,but it's documented:

  

汇总函数array_aggjson_aggjsonb_agg,   json_object_aggjsonb_object_aggstring_aggxmlagg   以及类似的用户定义的聚合函数,产生有意义的   不同的结果值取决于输入值的顺序。   默认情况下,此排序未指定,但可以通过控制   在聚合调用中编写ORDER BY子句,如图所示   Section 4.2.7或者,从a提供输入值   排序的子查询通常会起作用。例如:

SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
     

请注意,如果外部查询级别包含,则此方法可能会失败   附加处理,例如连接,因为这可能会导致   子查询的输出在计算聚合之前要重新排序。

大胆强调我的 为了保持标准,你可以写:

WITH list AS (
   SELECT g.days, count(d.invoicedate)::text AS cnt
   FROM  (
      SELECT max(invoicedate) AS last_date
      FROM   dok
      WHERE  invoicedate < current_date
      ) l
   CROSS  JOIN generate_series(0, 11*7, 7) AS g(days)
   LEFT   JOIN dok d ON d.invoicedate >  l.last_date - g.days - 7
                    AND d.invoicedate <= l.last_date - g.days
   GROUP  BY 1
   )
SELECT string_agg(cnt, ',' ORDER BY days DESC)
FROM   list;

但这有点慢。此外,CTE 在技术上不是必需的,也比子查询慢一点 像我提出的SELECT array_to_string(ARRAY( SELECT ...), ',')是最快的,因为数组构造函数对于单个结果比聚合函数string_agg()更快。