← 所有文章

FIELD NOTES / 資料記錄與報表

歷史查詢跨月時如何確認邊界筆沒有遺漏

資料記錄與報表作者:站長預估 5 分鐘閱讀

以半開時間區間、時區換算與唯一排序核對跨月資料,避免月底最後一筆及零點事件遺漏。

本文目錄

把月份變成明確的時間範圍

跨月報表少一筆,常見原因不是資料庫吞掉資料,而是上個月查到最後一秒、下個月又從零點之後才開始。先用明確起點與下一個月起點建立範圍,開始包含、結束不包含,讓邊界事件只屬於一個區間。

例如查九月,條件概念是事件時間大於等於九月一日零點,且小於十月一日零點。不要把結束寫成九月三十日二十三點五十九分五十九秒,因為系統可能保存毫秒或微秒,最後一秒之後仍有合法資料。

先固定以哪個時間欄分月:事件發生時間、接收時間或入庫時間。九月發生、十月收到的事件,三種查法可能分到不同月份;這不是SQL本身判斷錯誤,而是報表定義不同。欄位名稱與選擇理由要留在查詢規格。

本文適用於有時間戳的歷史事件與報表匯出,以PostgreSQL 18說明分區及分頁觀念。專用歷史庫可能有自己的端點、插值與壓縮回傳規則,不能直接套用關聯式資料列筆數驗收;先查產品實際查詢模式。

建立五個邊界事件做小測試

準備五筆已知事件:九月三十日23:59:59.000、23:59:59.999、十月一日00:00:00.000、00:00:00.001,以及十月一日00:00:01.000。每筆有不同識別,時間精度依測試系統能保存的格式設定。

九月查詢應得到前兩筆,十月查詢應得到後三筆。兩個月合併應有五個唯一事件,不能四筆也不能六筆。再以跨越這五筆的一次完整範圍查詢比對,事件識別集合應相同,不只比較總筆數。

若兩段都使用包含結束的條件,零點事件可能被兩邊取得而重複;若兩段都排除起點與終點,零點可能消失。不要靠最後合併時隨意刪重來掩蓋邊界錯誤,先修正區間定義,再把去重當成補送或來源重複的獨立處理。

時間欄若只保存整秒,第二筆與第三筆的精度不能照抄毫秒案例。先了解寫入時是截斷、四捨五入或拒絕超出精度,再設計可區分的測試值。畫面只顯示到秒,也不代表資料庫內沒有更細的時間。

完成後應留下測試事件表、兩個月各自的識別集合與合併比對結果。總數相同仍可能是一筆漏掉、另一筆重複,因此要核對差集、重複鍵與個別時間,不能只看到五筆就宣布驗收完成。

讓時區與實體分區各自一致

假設報表按固定UTC加八小時分月,九月一日零點對應八月三十一日16:00 UTC,十月一日零點對應九月三十日16:00 UTC。資料以UTC保存時,先把報表兩個端點轉成UTC,再拿相同時間軸查詢。

不要只把顯示時間加八小時,查詢條件卻仍使用UTC九月一日至十月一日,否則會漏掉本地九月最前八小時並多收十月最前八小時。固定偏移是本例條件;有日光節約時間的地區,要按指定時區規則換算兩端,不能硬加固定小時。

實體資料可能按UTC月份分區,而報表按本地月份分組。本地九月可能需要讀取部分UTC八月與九月資料,不能只因報表叫九月就只打開名為九月的檔案或子表。邏輯區間與儲存切分方式要分開盤點。

PostgreSQL範圍分區採下界包含、上界不包含,查詢父表可涵蓋仍掛接的分區。若舊分區已解除掛接移往封存區,父表不會自動讀到它;跨月缺資料時,檢查封存查詢路徑與分區範圍,而不只調整日期條件。

遲到資料若按接收月份落盤,九月事件可能存在十月檔案。查詢要能透過事件時間索引或目錄找到這些資料;若系統只允許查固定檔案,必須明訂補送存放與重建方法,避免日報完成後永遠找不到遲到事件。

分頁與補送也會造成邊界缺漏

大量匯出需要分頁時,排序應穩定且能唯一區分列,例如事件時間加事件識別。只按時間排序,多筆相同時間的列可能在不同查詢中換位置;只用最後時間當下一頁起點,也會漏掉同時刻尚未讀完的事件。

若資料在匯出過程持續插入或刪除,使用OFFSET分頁可能跳過或重複資料。可依產品能力固定一致快照,或使用完整排序鍵與明確截止範圍的游標設計;仍須處理截止前的遲到事件,不能以分頁技術保證來源永遠不再補送。

失敗時依序查時間欄、時區換算、端點包含規則、實際時間精度、分區是否可見與分頁鍵。若只在月初出錯,優先查分區建立與時區;若每次少的筆數不同,優先查匯出期間資料變動與排序不唯一。

驗收再加上二月底、閏年二月與十二月跨年。下一個月起點應按日曆計算,不能一律加三十天。將查詢版本、兩端原始時間與UTC時間、資料截止點及匯出頁數保存,讓別人能重現同一份事件集合。

另外核對查詢回應是否有筆數上限、截斷旗標或續傳識別。有些介面只回傳第一批資料,畫面卻沒有明顯錯誤;必須追到所有續頁完成,才可比較月份總數。測試時刻意讓資料量超過單次上限,確認跨頁的同時刻事件與月初事件仍完整保留,並保存實際回應的完成狀態。

練習與常見問題

練習:一月查詢取得一百筆,二月取得二百筆,合併後只有二百九十九個唯一識別。先查兩段是否都包含二月一日零點,以及來源是否重送。正確分段在資料本身無重複的前提下,應有三百個唯一事件,邊界只出現一次。

問:月底用23:59:59足夠嗎?答:不一定,可能漏掉同秒內更細精度的資料。

問:兩個月總數對上就代表沒有漏筆嗎?答:不一定,還要核對事件集合與重複識別。

問:UTC分區一定等於本地月報範圍嗎?答:不一定,端點換算後可能跨到相鄰UTC月份。

問:分頁只記最後時間可以嗎?答:同時刻多筆時不足,還要保存能唯一排序的識別與查詢條件。

參考:PostgreSQL 18 Table Partitioning:範圍端點與分區掛接概念。

參考:PostgreSQL 18 LIMIT and OFFSET:分頁與可預測排序。

延伸閱讀