โดยส่วนตัวถ้าเป็นวันที่หรืออาจเป็นวันที่ฉันขอแนะนำให้เก็บไว้เป็นหนึ่งเสมอ ง่ายกว่าที่จะทำงานด้วยเป็นกฎง่ายๆ
- วันที่คือ 4 ไบต์
- อักษรขนาดเล็กคือ 2 ไบต์ (เราต้องการสอง)
- ... 2 ไบต์: หนึ่งขนาดเล็กสำหรับปี
- ... 2 ไบต์: หนึ่งฉบับต่อเดือน
คุณสามารถมีหนึ่งวันที่จะรองรับวันถ้าคุณต้องการหรือหนึ่งsmallint
สำหรับปีและเดือนซึ่งจะไม่สนับสนุนความแม่นยำพิเศษ
ข้อมูลตัวอย่าง
ลองดูตัวอย่างตอนนี้ .. ลองสร้างตัวอย่าง 1 ล้านวันสำหรับตัวอย่างของเรา นี่คือประมาณ 5,000 แถวเป็นเวลา 200 ปีระหว่างปีพ. ศ. 2444 และปี 2100 ทุกปีควรมีบางอย่างสำหรับทุกเดือน
CREATE TABLE foo
AS
SELECT
x,
make_date(year,month,1)::date AS date,
year::smallint,
month::smallint
FROM generate_series(1,1e6) AS gs(x)
CROSS JOIN LATERAL CAST(trunc(random()*12+1+x-x) AS int) AS month
CROSS JOIN LATERAL CAST(trunc(random()*200+1901+x-x) AS int) AS year
;
CREATE INDEX ON foo(date);
CREATE INDEX ON foo (year,month);
VACUUM FULL ANALYZE foo;
การทดสอบ
ง่าย WHERE
ตอนนี้เราสามารถทดสอบทฤษฎีเหล่านี้โดยไม่ใช้วันที่ .. ฉันวิ่งไปแต่ละอันสองสามครั้งเพื่ออุ่นเครื่อง
EXPLAIN ANALYZE SELECT * FROM foo WHERE date = '2014-1-1'
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=11.56..1265.16 rows=405 width=14) (actual time=0.164..0.751 rows=454 loops=1)
Recheck Cond: (date = '2014-04-01'::date)
Heap Blocks: exact=439
-> Bitmap Index Scan on foo_date_idx (cost=0.00..11.46 rows=405 width=0) (actual time=0.090..0.090 rows=454 loops=1)
Index Cond: (date = '2014-04-01'::date)
Planning time: 0.090 ms
Execution time: 0.795 ms
ทีนี้ลองวิธีอื่นแยกกัน
EXPLAIN ANALYZE SELECT * FROM foo WHERE year = 2014 AND month = 1;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=12.75..1312.06 rows=422 width=14) (actual time=0.139..0.707 rows=379 loops=1)
Recheck Cond: ((year = 2014) AND (month = 1))
Heap Blocks: exact=362
-> Bitmap Index Scan on foo_year_month_idx (cost=0.00..12.64 rows=422 width=0) (actual time=0.079..0.079 rows=379 loops=1)
Index Cond: ((year = 2014) AND (month = 1))
Planning time: 0.086 ms
Execution time: 0.749 ms
(7 rows)
ในความเป็นธรรมพวกเขาไม่ใช่ทั้งหมด 0.749 .. บางคนอาจจะมากหรือน้อย แต่ก็ไม่สำคัญ พวกเขาทั้งหมดค่อนข้างเหมือนกัน มันไม่จำเป็นเลย
ภายในหนึ่งเดือน
ทีนี้มาสนุกกับมันกันเถอะสมมติว่าคุณต้องการหาช่วงเวลาทั้งหมดภายใน 1 เดือนของเดือนมกราคม 2014 (เดือนเดียวกันกับที่เราใช้ด้านบน)
EXPLAIN ANALYZE
SELECT *
FROM foo
WHERE date
BETWEEN
('2014-1-1'::date - '1 month'::interval)::date
AND ('2014-1-1'::date + '1 month'::interval)::date;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=21.27..2310.97 rows=863 width=14) (actual time=0.384..1.644 rows=1226 loops=1)
Recheck Cond: ((date >= '2013-12-01'::date) AND (date <= '2014-02-01'::date))
Heap Blocks: exact=1083
-> Bitmap Index Scan on foo_date_idx (cost=0.00..21.06 rows=863 width=0) (actual time=0.208..0.208 rows=1226 loops=1)
Index Cond: ((date >= '2013-12-01'::date) AND (date <= '2014-02-01'::date))
Planning time: 0.104 ms
Execution time: 1.727 ms
(7 rows)
เปรียบเทียบกับวิธีการรวมกัน
EXPLAIN ANALYZE
SELECT *
FROM foo
WHERE year = 2013 AND month = 12
OR ( year = 2014 AND ( month = 1 OR month = 2) );
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=38.79..2999.66 rows=1203 width=14) (actual time=0.664..2.291 rows=1226 loops=1)
Recheck Cond: (((year = 2013) AND (month = 12)) OR (((year = 2014) AND (month = 1)) OR ((year = 2014) AND (month = 2))))
Heap Blocks: exact=1083
-> BitmapOr (cost=38.79..38.79 rows=1237 width=0) (actual time=0.479..0.479 rows=0 loops=1)
-> Bitmap Index Scan on foo_year_month_idx (cost=0.00..12.64 rows=421 width=0) (actual time=0.112..0.112 rows=402 loops=1)
Index Cond: ((year = 2013) AND (month = 12))
-> BitmapOr (cost=25.60..25.60 rows=816 width=0) (actual time=0.218..0.218 rows=0 loops=1)
-> Bitmap Index Scan on foo_year_month_idx (cost=0.00..12.62 rows=420 width=0) (actual time=0.108..0.108 rows=423 loops=1)
Index Cond: ((year = 2014) AND (month = 1))
-> Bitmap Index Scan on foo_year_month_idx (cost=0.00..12.38 rows=395 width=0) (actual time=0.108..0.108 rows=401 loops=1)
Index Cond: ((year = 2014) AND (month = 2))
Planning time: 0.256 ms
Execution time: 2.421 ms
(13 rows)
มันช้ากว่าและน่าเกลียดกว่าเดิม
GROUP BY
/ORDER BY
วิธีการรวม
EXPLAIN ANALYZE
SELECT date, count(*)
FROM foo
GROUP BY date
ORDER BY date;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Sort (cost=20564.75..20570.75 rows=2400 width=4) (actual time=286.749..286.841 rows=2400 loops=1)
Sort Key: date
Sort Method: quicksort Memory: 209kB
-> HashAggregate (cost=20406.00..20430.00 rows=2400 width=4) (actual time=285.978..286.301 rows=2400 loops=1)
Group Key: date
-> Seq Scan on foo (cost=0.00..15406.00 rows=1000000 width=4) (actual time=0.012..70.582 rows=1000000 loops=1)
Planning time: 0.094 ms
Execution time: 286.971 ms
(8 rows)
และอีกครั้งด้วยวิธีการคอมโพสิต
EXPLAIN ANALYZE
SELECT year, month, count(*)
FROM foo
GROUP BY year, month
ORDER BY year, month;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Sort (cost=23064.75..23070.75 rows=2400 width=4) (actual time=336.826..336.908 rows=2400 loops=1)
Sort Key: year, month
Sort Method: quicksort Memory: 209kB
-> HashAggregate (cost=22906.00..22930.00 rows=2400 width=4) (actual time=335.757..336.060 rows=2400 loops=1)
Group Key: year, month
-> Seq Scan on foo (cost=0.00..15406.00 rows=1000000 width=4) (actual time=0.010..70.468 rows=1000000 loops=1)
Planning time: 0.098 ms
Execution time: 337.027 ms
(8 rows)
ข้อสรุป
โดยทั่วไปให้คนฉลาดทำงานหนัก Datemath นั้นยากลูกค้าของฉันไม่จ่ายเงินให้ฉันมากพอ ฉันเคยทำแบบทดสอบเหล่านี้ date
ผมก็กดยากที่จะเคยสรุปว่าฉันจะได้รับผลลัพธ์ที่ดีกว่า ฉันหยุดพยายาม
การปรับปรุง
@a_horse_with_no_name แนะนำของฉันภายในหนึ่งเดือนWHERE (year, month) between (2013, 12) and (2014,2)
ทดสอบ ในความเห็นของฉันในขณะที่เจ๋งมากนั้นเป็นคำถามที่ซับซ้อนมากขึ้นและฉันก็ควรหลีกเลี่ยงเว้นแต่จะได้รับ อนิจจามันก็ยังช้าลงแม้ว่าจะใกล้ - ซึ่งเป็นมากกว่าที่จะออกไปจากการทดสอบนี้ มันไม่สำคัญอะไรมาก
EXPLAIN ANALYZE
SELECT *
FROM foo
WHERE (year, month) between (2013, 12) and (2014,2);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=5287.16..15670.20 rows=248852 width=14) (actual time=0.753..2.157 rows=1226 loops=1)
Recheck Cond: ((ROW(year, month) >= ROW(2013, 12)) AND (ROW(year, month) <= ROW(2014, 2)))
Heap Blocks: exact=1083
-> Bitmap Index Scan on foo_year_month_idx (cost=0.00..5224.95 rows=248852 width=0) (actual time=0.550..0.550 rows=1226 loops=1)
Index Cond: ((ROW(year, month) >= ROW(2013, 12)) AND (ROW(year, month) <= ROW(2014, 2)))
Planning time: 0.099 ms
Execution time: 2.249 ms
(7 rows)
month
ที่มีจำนวนเต็มสองจำนวน แต่ฉันคิดว่าถ้าคุณไม่เคยต้องการวันของเดือนการใช้จำนวนเต็มสองจำนวนน่าจะง่ายกว่านี้