Postgresql

我想在不創建表格的情況下獲取一個月的所有星期以及這些星期的開始和結束日期

  • June 6, 2020

我的 SQL 是:

SELECT date_trunc('week', '2020-06-01'::timestamp)::date ||'-'|| 
(date_trunc('week', '2020-06-01'::timestamp)+ '6 days'::interval)::date;

但是,使用此程式碼僅顯示一周的輸出(2020-06-01-2020-06-07)。我想要的實際輸出是:

(2020-06-01-2020-06-07,
2020-06-08-2020-06-14,
2020-06-15-2020-06-21,
2020-06-22-2020-06-28)

帶有時間戳參數的generate_series()將做你想要的。

testdb=# select d::date::text||'-'||(d+interval '6 days')::date::text from
generate_series('2020-06-01', '2020-06-01'::date + interval '3 weeks', interval '1 weeks') d;
      ?column?        
-----------------------
2020-06-01-2020-06-07
2020-06-08-2020-06-14
2020-06-15-2020-06-21
2020-06-22-2020-06-28
(4 rows)

Time: 0.645 ms

編輯:

感謝@AdamKG here的回答,可以在使用s 的情況下一次性獲得 OP 所需的確切結果CTE,如下所示(fiddle available here):

SELECT 
 REPLACE
 (
   '(' ||
   STRING_AGG(d::DATE::TEXT || '-' || (d + INTERVAL '6 days')::DATE::TEXT || ',' , E'\n') 
   || ')', ',)', ')'
 ) AS str
FROM GENERATE_SERIES
    (
      '2020-06-01', '2020-06-01'::DATE + INTERVAL '3 weeks', INTERVAL '1 weeks'
    ) d;

結果:

str
(2020-06-01-2020-06-07,
2020-06-08-2020-06-14,
2020-06-15-2020-06-21,
2020-06-22-2020-06-28)

就在這裡

對於那些希望遵循邏輯的人,我將在下面完整保留我的原始答案 - 它與上面的查詢基本相同,只是一步完成!


原答案:

事實證明,這很棘手,無法完全進入要求的格式,但我最終還是做到了!一個 9.6 fiddle 在這裡可用。

這條 SQL 終於做到了:

WITH cte1 AS
(
 SELECT t.d1::DATE
 FROM GENERATE_SERIES
      (
        TIMESTAMP '2020-06-01',
        TIMESTAMP '2020-06-30',
        INTERVAL  '7 DAY'
      ) AS t(d1)
),
cte2 AS
(
 SELECT d1, (d1 + '6 DAY'::INTERVAL)::DATE AS d2
 FROM cte1
 WHERE (d1 + '6 DAY'::INTERVAL)::DATE < '2020-06-30'::DATE
),
cte3 AS
(
 SELECT 
   '(' || 
   STRING_AGG((d1::TEXT) || '-' || (d2::TEXT) || ',', E'\n') 
   || ')' 
   AS date_string1
 FROM cte2
)
SELECT REPLACE(date_string1, ',)', ')') AS date_string FROM cte3;

結果:

date_string
(2020-06-01-2020-06-07,
2020-06-08-2020-06-14,
2020-06-15-2020-06-21,
2020-06-22-2020-06-28)

這正是要求的格式!

邏輯:

我將逐步了解邏輯,因為a)我相信它對 OP 很有幫助,並且b)有助於我加強我在回答問題時所學到的知識(順便說一句,+1 是發人深省的問題)。小提琴中還概述了這些步驟!

cte1

在這裡,我使用了(非常有用的)GENERATE_SERIES功能。這得到了 6 月的所有星期一 - 2020 年 6 月從星期一開始可能是巧合 - 但無論如何,這就是 OP 想要的 - 無論如何,如果需要其他月份的星期一,您只需從相關的第一個星期一。我發現這個答案對本節非常有幫助!

SELECT t.d1::DATE
FROM GENERATE_SERIES
    (
      TIMESTAMP '2020-06-01',
      TIMESTAMP '2020-06-30',
      INTERVAL  '7 DAY'
    ) AS t(d1)

結果:

d1
2020-06-01
2020-06-08
2020-06-15
2020-06-22
2020-06-29

cte2

這為一周結束的結果增加了 6 天cte1- 即 6 月的每個星期天 - 事實上,它還選擇了 7 月的第一個星期日,沒有WHEREOP 顯然不想要的條款,所以我刪除了它使用WHERE. 我取結果cte1並簡單地將添加的結果'6 DAY'放在它旁邊:

SELECT d1, (d1 + '6 DAY'::INTERVAL)::DATE AS d2
FROM cte1
WHERE (d1 + '6 DAY'::INTERVAL)::DATE < '2020-06-30'::DATE

SELECT * FROM cte2

d1          d2
2020-06-01  2020-06-07
2020-06-08  2020-06-14
2020-06-15  2020-06-21
2020-06-22  2020-06-28

這是我們滿足 OP 要求的所有日期。

cte3

在這個 cte 中,我使用STRING_AGG基本上將結果集轉換為 .csv 列表的函式 - 通常不是最佳實踐,但考慮到有關在 .csv 之間來迴轉換的問題數量,非常有用並且是 OP 的要求。我也發現這篇文章很有幫助。

還有這個換行符的問題 - 我不確定這是否可能,但感謝我的朋友 Google 先生,我發現這篇文章解釋了一個可以使用E\n或者CHR(10)- 我嘗試了兩者並且兩者都工作正常 -在這裡使用的決定E\n對我來說是任意的。

我還在. 的開頭和結尾添加了(&括號。)``STRING_AGG

SELECT 
 '(' || 
 STRING_AGG((d1::TEXT) || '-' || (d2::TEXT) || ',', E'\n') 
 || ')' 
 AS date_string1
FROM cte2

SELECT * FROM cte3給出:

date_string1
(2020-06-01-2020-06-07,
2020-06-08-2020-06-14,
2020-06-15-2020-06-21,
2020-06-22-2020-06-28,)

所以,我們快到了!唯一的“美中不足”是最後的 ‘,)’ - OP 的要求是字元串結束2020-06-28)而不是2020-06-28,),所以 SQL 語句的最後一部分是使用REPLACE函式來刪除這個 final逗號。

SELECT REPLACE(date_string1, ',)', ')') AS date_string FROM cte3;

Etvoilà - OP 要求的結果。

ps 歡迎來到論壇!

引用自:https://dba.stackexchange.com/questions/268580