การสร้างอนุกรมเวลาระหว่างวันที่สองวันใน PostgreSQL


92

ฉันมีข้อความค้นหาเช่นนี้ซึ่งสร้างชุดของวันที่ระหว่าง 2 วันที่กำหนด:

select date '2004-03-07' + j - i as AllDate 
from generate_series(0, extract(doy from date '2004-03-07')::int - 1) as i,
     generate_series(0, extract(doy from date '2004-08-16')::int - 1) as j

มันสร้าง 162 วันที่ระหว่าง2004-03-07และ2004-08-16และนี่คือสิ่งที่ฉันต้องการ ปัญหากับรหัสนี้ก็คือว่ามันจะไม่ให้คำตอบที่ถูกต้องเมื่อวันที่สองวันจากปีที่แตกต่างกันเช่นเมื่อฉันพยายามและ2007-02-012008-04-01

มีทางออกที่ดีกว่านี้หรือไม่?


คำตอบ:


175

สามารถทำได้โดยไม่ต้องแปลงเป็น / จาก int (แต่เป็น / จากการประทับเวลาแทน)

SELECT date_trunc('day', dd):: date
FROM generate_series
        ( '2007-02-01'::timestamp 
        , '2008-04-01'::timestamp
        , '1 day'::interval) dd
        ;

3
ทำไมถึงdate_truncต้องการ?
Idefixx

2
เป็นเพียงการนำเสนอ จะช่วยลดการพิมพ์ส่วนเวลาของการประทับเวลาซึ่งเป็นเลขศูนย์ในกรณีนี้
beemtee

73

ในการสร้างชุดวันที่นี่เป็นวิธีที่ดีที่สุด :

SELECT t.day::date 
FROM   generate_series(timestamp '2004-03-07'
                     , timestamp '2004-08-16'
                     , interval  '1 day') AS t(day);
  • เพิ่มเติมdate_trunc()ไม่จำเป็น การส่งไปยังdate( day::date) ทำเช่นนั้นโดยปริยาย

  • แต่ก็ไม่มีประเด็นในการแคสต์ตัวอักษรวันที่dateเป็นพารามิเตอร์อินพุต Au contraire, เป็นตัวเลือกที่ดีที่สุดtimestamp ข้อได้เปรียบในด้านประสิทธิภาพคือเล็กน้อย แต่ไม่มีเหตุผลที่จะไม่ใช้มัน และคุณไม่จำเป็นต้องเกี่ยวข้องกับกฎ DST (เวลาออมแสง) ควบคู่ไปกับการแปลงจากdateเป็นtimestamp with time zoneและกลับ ดูด้านล่าง

ไวยากรณ์สั้นที่เทียบเท่าและชัดเจนน้อยกว่า:

SELECT day::date 
FROM   generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;

หรือด้วยฟังก์ชัน set-return ในSELECTรายการ:

SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;

ASคำหลักที่จำเป็นในตัวแปรที่ผ่านมา Postgres จะตีความผิดนามแฝงคอลัมน์dayมิฉะนั้น และฉันจะไม่แนะนำตัวแปรนั้นก่อน Postgres 10 - อย่างน้อยก็ไม่ต้องมีฟังก์ชันคืนค่ามากกว่าหนึ่งSELECTรายการในรายการเดียวกัน:

(นอกจากนี้ตัวแปรสุดท้ายมักจะเร็วที่สุดโดยมีระยะขอบเล็กน้อย)

ทำไมtimestamp [without time zone]?

มีรูปแบบที่โอเวอร์โหลดจำนวนมากของgenerate_series(). ปัจจุบัน (Postgres 11):

SELECT oid::regprocedure   AS function_signature
     , prorettype::regtype AS return_type
FROM   pg_proc
where  proname = 'generate_series';
function_signature | return_type                
: ------------------------------------------------- ------------------------------- | : --------------------------
create_series (จำนวนเต็ม, จำนวนเต็ม, จำนวนเต็ม) | จำนวนเต็ม                    
create_series (จำนวนเต็มจำนวนเต็ม) | จำนวนเต็ม                    
create_series (bigint, bigint, bigint) | bigint                     
create_series (bigint, bigint) | bigint                     
create_series (ตัวเลขตัวเลขตัวเลข) | ตัวเลข                    
create_series (ตัวเลข, ตัวเลข) | ตัวเลข                    
create_series (การประทับเวลาโดยไม่มีเขตเวลาการประทับเวลาโดยไม่มีเขตเวลาช่วงเวลา) | การประทับเวลาโดยไม่มีเขตเวลา
create_series (การประทับเวลาด้วยเขตเวลาการประทับเวลาด้วยเขตเวลาช่วงเวลา) | การประทับเวลาด้วยเขตเวลา

( numericสายพันธุ์ที่ถูกเพิ่มเข้ามาด้วย Postgres 9.5.) คนที่เกี่ยวข้องเป็นสองคนสุดท้ายที่เป็นตัวหนาจดและกลับ/timestamptimestamptz

มีตัวแปรที่ไม่มีการหรือกลับ datedateหล่ออย่างชัดเจนเป็นสิ่งจำเป็นที่จะกลับมา การเรียกที่มีtimestampอาร์กิวเมนต์จะเปลี่ยนเป็นตัวแปรที่ดีที่สุดโดยตรงโดยไม่ต้องลดระดับลงในกฎการแก้ปัญหาประเภทฟังก์ชันและไม่มีการแคสต์เพิ่มเติมสำหรับอินพุต

timestamp '2004-03-07'ถูกต้องอย่างสมบูรณ์ btw. ส่วนเวลาที่ละไว้จะ00:00มีค่าเริ่มต้นเป็นรูปแบบ ISO

ด้วยความละเอียดประเภทฟังก์ชันที่เรายังส่งผ่านdateได้ แต่ต้องทำงานเพิ่มเติมจาก Postgres มีการโยนโดยปริยายจากdateถึงtimestampหนึ่งจากdateถึงtimestamptzหนึ่ง จะไม่ชัดเจน แต่timestamptzเป็น"ต้องการ"ในหมู่ "ประเภทวันที่ / เวลา" ดังนั้นการแข่งขันจะถูกตัดสินที่ขั้นตอนที่4d :

ดำเนินการตามผู้สมัครทั้งหมดและเก็บผู้ที่ยอมรับประเภทที่ต้องการ (ของประเภทประเภทข้อมูลอินพุต) ไว้ที่ตำแหน่งส่วนใหญ่ที่จะต้องมีการแปลงประเภท เก็บผู้สมัครทั้งหมดไว้หากไม่มีใครยอมรับประเภทที่ต้องการ หากมีผู้สมัครเพียงคนเดียวให้ใช้ อื่น ๆ ให้เข้าสู่ขั้นตอนต่อไป

นอกเหนือจากงานพิเศษในความละเอียดประเภทฟังก์ชันแล้วยังเพิ่มการแคสต์พิเศษtimestamptzซึ่งไม่เพียง แต่จะเพิ่มต้นทุนมากขึ้นเท่านั้น แต่ยังสามารถทำให้เกิดปัญหากับ DST ซึ่งนำไปสู่ผลลัพธ์ที่ไม่คาดคิดในบางกรณี (DST เป็นแนวคิดที่ปัญญาอ่อนไม่สามารถเน้นสิ่งนี้ได้เพียงพอ)

ฉันเพิ่มการสาธิตให้กับซอที่แสดงแผนการสืบค้นที่แพงกว่า:

db <> ซอที่นี่

ที่เกี่ยวข้อง:


7
รุ่นที่สั้นยิ่งขึ้น:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
VáclavKužel

ไวยากรณ์ของ t (day) มีความหมายว่าอย่างไร
rendang

@rendang: AS t(day)ในSELECT * FROM func() AS t(day)เป็นตารางและคอลัมน์นามแฝง ASคำหลักคือเสียงตัวเลือกในบริบทนี้ ดู: stackoverflow.com/a/20230716/939860
Erwin Brandstetter

35

คุณสามารถสร้างชุดข้อมูลได้โดยตรงด้วยวันที่ ไม่จำเป็นต้องใช้ ints หรือการประทับเวลา:

select date::date 
from generate_series(
  '2004-03-07'::date,
  '2004-08-16'::date,
  '1 day'::interval
) date;

สิ่งนี้อาจส่งคืนผลลัพธ์ที่ไม่คาดคิดทั้งนี้ขึ้นอยู่กับเขตเวลาของคุณ ฉันมีปัญหานี้ ใช้การประทับเวลาแทน SET เซสชัน TIME โซน 'America / Sao_Paulo' SELECT d :: date FROM create_series ('2019-11-01' :: date, '2019-11-03' :: date, '1 day') d SELECT d :: date จาก create_series ('2019-11-01' :: date, '2019-11-04' :: date, '1 day') d
palhares

1

คุณยังสามารถใช้สิ่งนี้

select generate_series  ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.