207

我在 Postgres 服务器上有以下数据库表:

id      date          Product Sales
1245    01/04/2013    Toys    1000     
1245    01/04/2013    Toys    2000
1231    01/02/2013    Bicycle 50000
456461  01/01/2014    Bananas 4546

我想创建一个查询,该查询给出列的SUM值,Sales并按月和年对结果进行分组,如下所示:

Apr    2013    3000     Toys
Feb    2013    50000    Bicycle
Jan    2014    4546     Bananas

有没有一种简单的方法可以做到这一点?

4

7 回答 7

387

我不敢相信接受的答案有这么多赞成 - 这是一种可怕的方法。

这是使用date_trunc的正确方法:

   SELECT date_trunc('month', txn_date) AS txn_month, sum(amount) as monthly_sum
     FROM yourtable
 GROUP BY txn_month

这是不好的做法,但如果你使用,你可能会被原谅

 GROUP BY 1

在一个非常简单的查询中。

你也可以使用

 GROUP BY date_trunc('month', txn_date)

如果您不想选择日期。

于 2014-11-18T18:14:00.690 回答
252
select to_char(date,'Mon') as mon,
       extract(year from date) as yyyy,
       sum("Sales") as "Sales"
from yourtable
group by 1,2

应 Radu 的要求,我将解释该查询:

to_char(date,'Mon') as mon,: 将“日期”属性转换为月份短格式的定义格式。

extract(year from date) as yyyy: Postgresql 的 "extract" 函数用于从 "date" 属性中提取 YYYY 年份。

sum("Sales") as "Sales":SUM() 函数将所有“Sales”值相加,并提供区分大小写的别名,并使用双引号保持区分大小写。

group by 1,2: GROUP BY 函数必须包含 SELECT 列表中不属于聚合的所有列(也就是不在 SUM/AVG/MIN/MAX 等函数内的所有列)。这告诉查询应该对每个唯一的列组合应用 SUM(),在本例中是月份和年份列。"1,2" 部分是一种速记,而不是使用列别名,尽管最好使用完整的 "to_char(...)" 和 "extract(...)" 表达式以提高可读性。

于 2013-07-05T15:26:58.663 回答
49

to_char居然让你一举抽出年月!

select to_char(date('2014-05-10'),'Mon-YY') as year_month; --'May-14'
select to_char(date('2014-05-10'),'YYYY-MM') as year_month; --'2014-05'

或者在上面的用户示例中:

select to_char(date,'YY-Mon') as year_month
       sum("Sales") as "Sales"
from some_table
group by 1;
于 2014-10-03T20:02:20.243 回答
7

还有另一种方法可以使用 postgres 中的 date_part() 函数来实现结果。

 SELECT date_part('month', txn_date) AS txn_month, date_part('year', txn_date) AS txn_year, sum(amount) as monthly_sum
     FROM yourtable
 GROUP BY date_part('month', txn_date)

谢谢

于 2018-04-11T07:11:45.623 回答
4

看一下本教程的示例 6) -> https://www.postgresqltutorial.com/postgresql-group-by/

您需要在 GROUP BY 上调用该函数,而不是调用您在 select 上创建的虚拟属性的名称。我正在按照上述所有答案的建议进行操作,但遇到了column 'year_month' does not exist错误。

对我有用的是:

SELECT 
    date_trunc('month', created_at), 'MM/YYYY' AS month
FROM 
    "orders"  
GROUP BY 
    date_trunc('month', created_at)
于 2020-05-21T20:32:28.633 回答
2

为什么不只使用date_part函数。https://www.postgresql.org/docs/8.0/functions-datetime.html

SELECT date_part('year', txn_date) AS txn_year,
       date_part('month', txn_date) AS txn_month,
       sum(amount) as monthly_sum
FROM payment
GROUP BY txn_year, txn_month
order by txn_year;
于 2021-09-24T05:59:09.193 回答
0

Postgres 有几种类型的时间戳:

没有时区的时间戳 - (最好存储 UTC 时间戳)您可以在多国数据库存储中找到它。在这种情况下,客户将负责每个国家/地区的时区偏移。

带时区的时间戳 - 时区偏移量已包含在时间戳中。

在某些情况下,您的数据库不使用时区,但您仍然需要根据本地时区和夏令时对记录进行分组(例如https://www.timeanddate.com/time/zone/romania/bucharest

要添加时区,您可以使用此示例并将时区偏移量替换为您的。

"your_date_column" at time zone '+03'

要添加特定于 DST 的 +1 夏季时间偏移,您需要检查您的时间戳是否属于夏季 DST。由于这些间隔随 1 或 2 天而变化,我将使用不影响月末记录的近似值,因此在这种情况下,我可以忽略每年的确切间隔。

如果必须构建更精确的查询,则必须添加条件以创建更多案例。但粗略地说,当您在数据库中找到没有时区的时间戳时,这可以很好地根据时区和 SummerTime 每月拆分数据:

SELECT 
    "id", "Product", "Sale",
    date_trunc('month', 
        CASE WHEN 
            Extract(month from t."date") > 03 AND
            Extract(day from t."date") > 26 AND
            Extract(hour from t."date") > 3 AND
            Extract(month from t."date") < 10 AND
            Extract(day from t."date") < 29 AND
            Extract(hour from t."date") < 4
        THEN 
            t."date" at time zone '+03' -- Romania TimeZone offset + DST
        ELSE
            t."date" at time zone '+02' -- Romania TimeZone offset 
        END) as "date"
FROM 
    public."Table" AS t
WHERE 1=1
    AND t."date" >= '01/07/2015 00:00:00'::TIMESTAMP WITHOUT TIME ZONE
    AND t."date" < '01/07/2017 00:00:00'::TIMESTAMP WITHOUT TIME ZONE
GROUP BY date_trunc('month', 
    CASE WHEN 
        Extract(month from t."date") > 03 AND
        Extract(day from t."date") > 26 AND
        Extract(hour from t."date") > 3 AND
        Extract(month from t."date") < 10 AND
        Extract(day from t."date") < 29 AND
        Extract(hour from t."date") < 4
    THEN 
        t."date" at time zone '+03' -- Romania TimeZone offset + DST
    ELSE
        t."date" at time zone '+02' -- Romania TimeZone offset 
    END)
于 2017-07-17T16:29:58.573 回答