2011-04-28 270 views
11

我正在構建一個分析數據庫(我對數據和業務目標以及只有基本到中等的數據庫技能有着深刻的理解)。PostgreSQL中的日曆表9

我遇到了一些建立類似的倉庫,實現'日曆表'的概念的參考。這是有道理的,很容易完成。然而,我所看到的大多數例子都是日曆表,它們將範圍限制爲「日」。我的數據需要分析到小時級別。可能分鐘。

我的問題:在空間效率和查詢/排序速度方面,實現小時/分鐘級粒度的日曆表是​​否有價值?如果是這樣,你能推薦一個表結構和人口方法/例子嗎?

我的主數據表在任何給定的時間將包含2000萬行數據,典型的分析子集在1到500萬的範圍內。所以,如你所見,這是很多時間戳字段。

回答

8

日曆表實現空間/時間折衷。通過使用更多空間,某些種類的查詢可以在更短的時間內運行,因爲它們可以利用索引。只要您謹慎使用CHECK()約束,並且只要您有管理進程來處理您的dbms不支持的任何約束,它們就是安全的。

如果您的粒度爲一分鐘,則每年需要生成約50萬行。最小的日曆表將如下所示。

2011-01-01 00:00:00 
2011-01-01 00:01:00 
2011-01-01 00:02:00 
2011-01-01 00:03:00 
2011-01-01 00:04:00 

如果你正在做「桶」分析,你可能會更喜歡這樣的事情。

bucket_start   bucket_end 
-- 
2011-01-01 00:00:00 2011-01-01 00:01:00 
2011-01-01 00:01:00 2011-01-01 00:02:00 
2011-01-01 00:02:00 2011-01-01 00:03:00 
2011-01-01 00:03:00 2011-01-01 00:04:00 
2011-01-01 00:04:00 2011-01-01 00:05:00 

由於SQL的BETWEEN運算符包含端點,因此通常需要避免使用它。這是因爲它包含端點,並且很難將bucket_end表示爲「bucket_start加上一分鐘,減去此服務器可識別的最小時間」。 (危險值比bucket_end大一微秒,但仍小於bucket_start的下一個值。)

如果我打算構建該表,我可能會這樣做。 (雖然我覺得我是否應該把它更難「日曆」。)

create table calendar (
    bucket_start timestamp primary key, 
    bucket_end timestamp unique, 
    CHECK (bucket_end = bucket_start + interval '1' minute) 
    -- You also want a "no gaps" constraint, but I don't think you 
    -- can do that in a CHECK constraint in PostgreSQL. You might 
    -- be able to use a trigger that counts the rows, and compares 
    -- that count to the number of minutes between min(bucket_start) 
    -- and max(bucket_start). Worst case, you can always run a report 
    -- that counts the rows and sends you an email. 
); 

UNIQUE約束創建PostgreSQL中的隱式索引。

此查詢將一次插入一天的行數(24小時* 60分鐘)。

insert into calendar 
select coalesce(
       (select max(bucket_start) from calendar), 
       cast('2011-01-01 00:00:00' as timestamp) 
       ) 
      + cast((n || 'minute') as interval) as bucket_start, 
     coalesce(
       (select max(bucket_start) from calendar), 
       cast('2011-01-01 00:00:00' as timestamp) 
       ) 
      + cast((n + 1 || ' minute') as interval) as bucket_end 
from generate_series(1, (24*60)) n; 

您可以將其包裝在一個函數中以一次生成一年。我可能會嘗試一次提交少於50萬行。

不應該花太長時間才能生成2000萬行用於測試,並且還需要2000萬行「日曆」分鐘。午餐很長。也許在陽光下的一個下午。

+0

什麼你說的究竟是「索引的優勢」? – Quassnoi 2011-04-28 20:11:01

12

PostgreSQL,可以在運行中生成任意長度和粒度的日曆表:

SELECT CAST('2011-01-01' AS DATE) + (n || ' hour')::INTERVAL 
FROM generate_series(0, 23) n 

這不需要遞歸(與其它系統)和是產生揮發性結果集的優選方法。

+0

是的,但在大概2000萬行上加入generate_series()的結果可能會將性能下拉到廁所中。日曆表上的查詢可以利用索引。 – 2011-04-28 16:51:00

+1

@Catcall:日曆表上的查詢通常假設日曆表中的所有值和事實表中的一些值之間存在左連接,以便在沒有事實記錄的情況下返回「NULL」記錄。你能否提供一個示例查詢,它可以通過用實際數據表替換'generate_series'來獲益? – Quassnoi 2011-04-28 16:56:02

+0

有趣。謝謝。所以我的想法是,我可以創建三個這樣的表格:天,小時,分鐘,我的數據集表中會有'day_id','hour_id','minute_id'等鍵,可以一起使用或獨立使用,具體取決於我的分析?如果是這樣,那真棒。如果沒有,我錯過了一些東西。 – 2011-04-28 17:06:41

1

在我建立的數據倉庫中,我使用了單獨的CALENDAR和TIME_OF_DAY尺寸。第一個維度的每日粒度爲1天,第二個維度爲1分鐘粒度。

在另外兩個案例中,我事先知道在小於15分鐘的粒度下不需要報告。在那種情況下,爲了簡單起見,我每天使用一個96條記錄的單個CALENDAR維度。

到目前爲止,我在Oracle倉庫中使用這種方法,但今年夏天我可能會參與到PostgreSQL倉庫項目中。