我最近才刚开始使用Postgresql。我有一个名为“ sales”的表。
create table sales
(
cust varchar(20),
prod varchar(20),
day integer,
month integer,
year integer,
state char(2),
quant integer
)
insert into sales values ('Bloom', 'Pepsi', 2, 12, 2001, 'NY', 4232);
insert into sales values ('Knuth', 'Bread', 23, 5, 2005, 'PA', 4167);
insert into sales values ('Emily', 'Pepsi', 22, 1, 2006, 'CT', 4404);
insert into sales values ('Emily', 'Fruits', 11, 1, 2000, 'NJ', 4369);
insert into sales values ('Helen', 'Milk', 7, 11, 2006, 'CT', 210);
...
共有500行,共有10个不同的产品和5个不同的客户。
现在,我需要找到12个月中每个月最受欢迎,最受欢迎的产品(总销售数量最多和最少的产品)和相应的总销售数量(即SUM)。不论年份)。
结果应该是这样的:现在我只能这样写查询:
select month,
prod,
sum(quant)
from sales
group by month,prod
order by month,prod;
它给了我这样的结果:)。还有水平组合 ...我对此一无所知...现在我需要获取每个月的最大值。例如,第1个月的前10个总和中的最大值,依此类推...
我还需要获得总和的最小值(
不管年份
您的问题是一个非常有趣的教科书案例,因为它涉及Postgres的多个方面。
我经常发现将问题分解为多个子问题,然后再将它们组合在一起以得到最终结果集,这非常有帮助。
在您的情况下,我看到两个子问题:查找每月最受欢迎的产品,以及查找每月最受欢迎的产品。
让我们从最受欢迎的产品开始:
WITH months AS (
SELECT generate_series AS month
FROM generate_series(1, 12)
)
SELECT DISTINCT ON (month)
month,
prod,
SUM(quant)
FROM months
LEFT JOIN sales USING (month)
GROUP BY month, prod
ORDER BY month, sum DESC;
说明:
WITH
是common table
expression,作为临时表(在查询期间),并且帮助澄清查询。如果您感到困惑,也可以选择用于子查询。generate_series(1, 12)
是一个Postgres function,它生成一系列整数,在这种情况下为1到12。LEFT JOIN
允许我们将每个销售关联到相应的月份。如果在给定的月份内找不到销售,则返回该月份的行,并以NULL
值连接列。有关联接的更多信息,请参见here。在您的情况下,使用LEFT JOIN
很重要,因为使用INNER JOIN
会排除从未销售过的产品(在这种情况下,它应该是最受欢迎的产品)。GROUP BY
用于求和。DISTINCT ON
为此特别有用。给定一列,它使我们能够保留每个值的第一次迭代。因此,重要的是要先按总和来ORDER
,因为只会选择第一个。我们首先需要较大的数字,因此应使用DESC
(降序)。WITH months AS (
SELECT generate_series AS month
FROM generate_series(1, 12)
)
SELECT DISTINCT ON (month)
month,
prod,
SUM(quant)
FROM months
LEFT JOIN sales USING (month)
GROUP BY month, prod
ORDER BY month, sum;
结论(和TLDR):
现在我们需要将两个查询合并为一个最终查询。
WITH months AS ( SELECT generate_series AS month FROM generate_series(1, 12) ), agg_sales AS ( SELECT month, prod, SUM(quant) FROM months LEFT JOIN sales USING (month) GROUP BY month, prod ), most_popular AS ( SELECT DISTINCT ON (month) month, prod, sum FROM agg_sales ORDER BY month, sum DESC ), last_popular AS ( SELECT DISTINCT ON (month) month, prod, sum FROM agg_sales ORDER BY month, sum ) SELECT most_popular.month, most_popular.prod AS most_popular_prod, most_popular.sum AS most_pop_total_q, least_popular.prod AS least_popular_prod, least_popular.sum AS least_pop_total_q FROM most_popular JOIN least_popular USING (month);
[请注意,我使用中间agg_sales
CTE来尝试使查询更清晰,并避免重复相同的操作两次,尽管对于Postgres的优化程序来说这不是问题。希望您的回答令人满意。不要犹豫,否则发表评论!
编辑:尽管此解决方案应按原样工作,但我建议您将日期存储为
TIMESTAMPTZ
类型的单列。使用这种类型的日期通常更容易操作,并且在需要进一步分析和审核数据库的情况下,这总是一个好习惯。您只需使用
EXTRACT(MONTH FROM date)
即可获得任何日期的月份。