2013-07-05 57 views
84

我有一個Postgres服務器上的以下數據庫表:組查詢結果按月份和年份的PostgreSQL

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 

我想創建一個查詢,給出了Sales列並對結果進行分組通過的SUM月份和年份如下:

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

有沒有簡單的方法來做到這一點?

+1

向我們展示你的企圖 –

+0

你有沒有問這裏之前嘗試新鮮事物。如果嘗試添加細節,否則不要問。 – ST3

回答

101
select to_char(date,'Mon') as mon, 
     extract(year from date) as yyyy, 
     sum("Sales") as "Sales" 
from yourtable 
group by 1,2 

在拉杜的要求,我將解釋該查詢:

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(...)」表達式。

+0

我是begginer。抱歉,謝謝! – Frechi

+0

我不認爲沒有解釋給出答案是一個非常好的主意,特別是對於初學者。你應該已經解釋了你的答案背後的邏輯,也許至少有一點(儘管對我們其他人來說可能看起來簡單直接)。 –

+0

@Frechi - 作爲一種獎勵,有一種方法可以**保存**查詢** bma **作爲函數提供,您可以在需要時再次加載和使用。閱讀關於'plpgsql'和'CREATE FUNCTION'的文檔來了解如何做到這一點:-) –

3

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" 
) 
+2

或者你可以做'yourscopeorclass.group(「extract(year from tablename.colname)」)'並且你可以將它連在一起3次得到年,月,日 – nruth

23

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; 
+1

如果你有一個像樣的樣子,我會強烈建議不要這樣做表中的數據量。執行group by時,這會比'date_trunc'方法執行*差*。在一個數據庫上進行試驗我在一個具有270k行的表上使用方便,如果性能是一個問題,date_trunc方法的速度是TO_CHAR –

+0

@ChrisClark的兩倍,我同意使用date_trunc可能有意義,但在某些情況下格式化日期字符串更可取,如果您使用的是高性能數據倉庫,則額外計算可能不是交易斷路器。例如,如果您正在使用Redshift運行快速分析報告,並且通常需要3秒鐘,那麼6秒查詢可能是可以的(但是,如果您正在運行報告,則額外的計算可能會使速度減慢一個較小的百分比,因爲有一個更大的計算開銷) – mgoldwasser

+0

你仍然可以這樣做 - 只需將格式設置爲一個單獨的步驟即可通過查詢「包裝」該組。例如。 SELECT to_char(d,'YYYY-DD')FROM(SELECT date_trunc('month',d)AS「d」FROM tbl)AS foo。兩全其美! –

133

我不能相信接受的答案有這麼多的upvotes - 這是一個可怕的方法。

這裏做到這一點,正確的方法與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) 

,如果你不想選擇日期。

+1

不幸的是,'date_trunc'的輸出不是asker預期的結果:'select date_trunc('month',timestamp'2001-02-16 20:38:40')'=>'2001-02-01 00:00: 00'。 – pisaruk

+2

我同意這種方法更好。我不確定,但我認爲它更高效,因爲只有一個分組而不是兩個。如果您需要重新格式化日期,則可以使用其他答案中描述的方法重新格式化日期:'to_char(date_trunc('month',txn_date),'YY-Mon')' –

+1

yes,接受答案的投票數令人難以置信。 'date_trunc'是爲了這個確切的目的而創建的。沒有理由根據已經接受的答案創建兩個列 – allenwlee

0

Postgress在postgress的時間戳幾個tipes:

時間戳沒有時區 - (優先存儲UTC時間戳)您在跨國數據庫存儲找到它。在這種情況下,客戶端將處理每個國家/地區的時區偏移量。

帶時區的時間戳 - 時區已包含在時間戳中。

在某些情況下,你的數據庫不使用的時區,但你仍然需要組記錄與當地時區和夏令時的尊重(如https://www.timeanddate.com/time/zone/romania/bucharest

要添加時區,你可以用這個例子並更換時區偏移與你的。

"your_date_column" at time zone '+03' 

要添加特定於夏令時的+1夏令時偏移,您需要檢查您的時間戳是否屬於夏令時。由於這些間隔隨着1或2天不等,我將使用不會影響月末記錄的aproximation,因此在這種情況下,我可以忽略每年的確切時間間隔。

如果需要構建更精確的查詢,則必須添加條件以創建更多案例。但是,粗略計算,這些將正常工作每月分割數據與時區和夏令尊重,當你發現沒有時間戳在數據庫中時區:

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)