postgresql에서 월 및 연도별로 그룹 쿼리 결과


156

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

내가 제공하는 쿼리를 만드시겠습니까 SUMSales월과 연도 다음과 같이하여 열 및 그룹 결과를 :

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

그렇게하는 간단한 방법이 있습니까?

답변:


217
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, : "date"속성을 짧은 달 형식의 정의 된 형식으로 변환합니다.

extract(year from date) as yyyy : Postgresql의 "추출"기능은 "날짜"속성에서 YYYY 연도를 추출하는 데 사용됩니다.

sum("Sales") as "Sales" : SUM () 함수는 모든 "Sales"값을 더하고 대소 문자를 구분하여 대소 문자를 구분하여 대소 문자를 구분합니다.

group by 1,2: GROUP BY 함수는 집계의 일부가 아닌 SELECT 목록의 모든 열 (일명 SUM / AVG / MIN / MAX 등의 함수가 아닌 모든 열)을 포함해야합니다. 쿼리에 SUM ()을 각 고유 한 열 조합 (이 경우 월 및 연도 열)에 적용해야한다고 지시합니다. "1,2"부분은 열 별명을 사용하는 대신 속기이지만 가독성을 위해 전체 "to_char (...)"및 "extract (...)"표현식을 사용하는 것이 가장 좋습니다.


5
나는 설명없이 대답을하는 것이 특히 초보자에게 좋은 생각이라고 생각하지 않습니다. 당신은 당신의 대답의 논리를 적어도 조금 설명해야 할 것입니다.
Radu Gheorghiu

1
@BurakArslan 결과는 OP가 구체적으로 요구 한 것과 비슷합니까?
bma

2
@rogerdpack의 출력은 date_trunc아스 커 원하는 것을 정확히되지 않습니다 : select date_trunc('month', timestamp '2001-02-16 20:38:40')::date=>2001-02-01
pisaruk

2
나는 절 date_trunc에서 사용하는 아이디어를 좋아한다 group by.
pisaruk

1
가능한 "필드는 절별로 그룹화되어야합니다"문제 ... OVER (PARTITION BY)를 사용하는 것이 좋습니다.
Zon

317

나는 받아 들여진 대답이 너무 많은 찬사를 받았다고 믿을 수 없다. 그것은 끔찍한 방법이다.

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)

날짜를 선택하지 않으려면


6
불행히도의 출력은 date_truncasker가 예상 한 것과 다릅니다 : select date_trunc('month', timestamp '2001-02-16 20:38:40')=> 2001-02-01 00:00:00.
pisaruk

4
이 방법이 더 낫다는 데 동의합니다. 확실하지 않지만 두 그룹 대신 하나의 그룹 만 있기 때문에 더 효율적이라고 생각합니다. 날짜를 다시 포맷해야하는 경우 다른 답변에 설명 된 방법을 사용하여 나중에 할 수 있습니다.to_char(date_trunc('month', txn_date), 'YY-Mon')
Paweł Sokołowski

1
예, 수락 된 답변에 대한 투표 수는 마음이 흔들립니다. date_trunc이 정확한 목적을 위해 만들어졌습니다. 두 개의 열을 만들 이유가 없다
allenwlee

2
아주 좋아요! 이것은 특히 주문할 수 있기 때문에 탁월한 답변입니다. 공감!
bobmarksie

1
가장 답답한 답변이 승인 된 답변 앞에 나타나야하는 또 다른 예
Brian Risk

33

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;

6
테이블에 적절한 양의 데이터가 있으면이 작업을 수행하지 않는 것이 좋습니다. 이것은 그룹별로 수행 할 때 의 방법 보다 훨씬 나쁩니다 date_trunc. 270k 개의 행이있는 테이블에서 date_trunc 메서드는 TO_CHAR 속도의 두 배 이상입니다.
Chris Clark

@ChrisClark 성능이 중요하다면 date_trunc를 사용하는 것이 합리적이지만 일부 경우에는 형식이 지정된 날짜 문자열을 사용하는 것이 바람직하며 성능 데이터웨어 하우스를 사용하는 경우 추가 계산이 거래 차단기가 아닐 수 있습니다 . 예를 들어, 적색 편이를 사용하여 빠른 분석 보고서를 실행하는 데 보통 3 초가 걸리면 6 초의 쿼리로 문제가 없을 수 있습니다 (보고서를 실행하는 경우 추가 계산으로 인해 속도가 느려질 수 있음). 큰 계산 오버 헤드)가
mgoldwasser

1
쿼리를 통해 그룹을 '래핑'하여 별도의 단계로 서식을 지정하면됩니다. 예를 들어 SELECT to_char (d, 'YYYY-DD') FROM (SELECT date_trunc ( 'month', d) AS "d"FROM tbl) AS foo. 두 세계의 최고!
Chris Clark

1
이 솔루션은 간단하고 우아합니다. 나는 그것을 좋아하고 내 경우에는 충분히 빠릅니다. 이 답변에 감사드립니다!
guettli

5

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)

감사


1

bma 답변은 훌륭합니다! ActiveRecords와 함께 사용했습니다. 누구든지 Rails에서 필요로하는 경우입니다.

Model.find_by_sql(
  "SELECT TO_CHAR(created_at, 'Mon') AS month,
   EXTRACT(year from created_at) as year,
   SUM(desired_value) as desired_value
   FROM desired_table
   GROUP BY 1,2
   ORDER BY 1,2"
)

3
또는 당신은 할 수 yourscopeorclass.group("extract(year from tablename.colname)")있고 당신은 그 연도, 월, 일을 얻기 위해 함께 세 번 체인 수 있습니다
진실

1

이 튜토리얼의 예제 E를보십시오-> https://www.postgresqltutorial.com/postgresql-group-by/

select에서 생성 한 가상 속성의 이름을 호출하는 대신 GROUP BY에서 함수를 호출해야합니다. 위의 모든 답변이 권장하는 것을하고 있었고 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)

0

Postgres에는 몇 가지 유형의 타임 스탬프가 있습니다.

시간대없는 타임 스탬프 -(UTC 타임 스탬프를 저장하는 것이 좋습니다) 다국적 데이터베이스 저장소에서 찾을 수 있습니다. 이 경우 고객은 각 국가의 시간대 오프셋을 처리합니다.

시간대가있는 타임 스탬프 -시간대 오프셋이 이미 타임 스탬프에 포함되어 있습니다.

경우에 따라 데이터베이스에서 시간대를 사용하지 않지만 현지 시간대 및 일광 절약 시간제와 관련하여 레코드를 그룹화해야합니다 (예 : https://www.timeanddate.com/time/zone/romania/bucharest )

시간대를 추가하려면이 예제를 사용하고 시간대 오프셋을 사용자의 시간대 오프셋으로 바꾸십시오.

"your_date_column" at time zone '+03'

DST에 특정한 +1 서머 타임 오프셋을 추가하려면 타임 스탬프가 서머 DST에 해당하는지 확인해야합니다. 이러한 간격은 1-2 일마다 다르므로 월말 레코드에 영향을 미치지 않는 근사를 사용 하므로이 경우 매년 정확한 간격을 무시할 수 있습니다.

보다 정확한 쿼리를 작성해야하는 경우 더 많은 사례를 작성하기위한 조건을 추가해야합니다. 그러나 대략 데이터베이스에서 시간대가없는 타임 스탬프를 찾을 때 시간대 및 서머 타임과 관련하여 매월 데이터분할 할 때 제대로 작동 합니다.

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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.