PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana Anda mendapatkan tampilan 12 hari kerja yang dinamis di Postgresql?

Ini dapat diselesaikan dengan CTE:

WITH business_days_back AS (
  WITH RECURSIVE bd(back_day, go_back) AS (
    -- Go back to the previous Monday, allowing for current_date in the weekend
    SELECT CASE extract(dow from current_date)
             WHEN 0 THEN current_date - 6
             WHEN 6 THEN current_date - 5
             ELSE current_date - extract(dow from current_date)::int + 1
           END,
           CASE extract(dow from current_date)
             WHEN 0 THEN 7
             WHEN 6 THEN 7
             ELSE 12 - extract(dow from current_date)::int + 1
           END
    UNION
    -- Go back by the week until go_back = 0
    SELECT CASE
         WHEN go_back >= 5 THEN back_day - 7
         WHEN go_back > 0 THEN back_day - 2 - go_back
       END,
       CASE
         WHEN go_back >= 5 THEN go_back - 5
         WHEN go_back > 0 THEN 0
       END
    FROM bd
  )
  SELECT back_day FROM bd WHERE go_back = 0
)
SELECT * FROM my_table WHERE analysis_date >= (SELECT * FROM business_days_back);

Beberapa penjelasan:

  • CTE bagian dalam dimulai dengan bekerja kembali ke Senin sebelumnya, mengkompensasi current_date yang jatuh pada hari akhir pekan.
  • Istilah rekursif kemudian menambahkan baris dengan kembali ke minggu penuh (back_day - 7 untuk tanggal kalender dan go_back - 5 untuk hari kerja) sampai go_back = 0 .
  • CTE luar mengembalikan back_day tanggal dimana go_back = 0 . Oleh karena itu, ini adalah kueri skalar dan Anda dapat menggunakannya sebagai subkueri dalam ekspresi filter.

Anda dapat mengubah jumlah hari kerja untuk melihat ke belakang hanya dengan mengubah angka 12 dan 7 di awal SELECT di CTE bagian dalam. Namun, perlu diingat bahwa nilainya harus sedemikian rupa sehingga kembali ke Senin sebelumnya atau kueri akan gagal, karena SELECT awal yang sama dari CTE bagian dalam.

Solusi yang jauh lebih fleksibel (dan mungkin lebih cepat*) adalah dengan menggunakan fungsi berikut:

CREATE FUNCTION business_days_diff(from_date date, diff int) RETURNS date AS $$
-- This function assumes Mon-Fri business days
DECLARE
  start_dow int;
  calc_date date;
  curr_diff int;
  weekend   int;
BEGIN
  -- If no diff requested, return the from_date. This may be a non-business day.
  IF diff = 0 THEN
    RETURN from_date;
  END IF;

  start_dow := extract(dow from from_date)::int;
  calc_date := from_date;

  IF diff < 0 THEN -- working backwards
    weekend := -2;
    IF start_dow = 0 THEN -- Fudge initial Sunday to the previous Saturday
      calc_date := calc_date - 1;
      start_dow := 6;
    END IF;
    IF start_dow + diff >= 1 THEN -- Stay in this week
      RETURN calc_date + diff;
    ELSE                             -- Work back to Monday
      calc_date := calc_date - start_dow + 1;
      curr_diff := diff + start_dow - 1;
    END IF;
  ELSE -- Working forwards
    weekend := 2;
    IF start_dow = 6 THEN -- Fudge initial Saturday to the following Sunday
      calc_date := calc_date + 1;
      start_dow := 0;
    END IF;
    IF start_dow + diff <= 5 THEN -- Stay in this week
      RETURN calc_date + diff;
    ELSE                             -- Work forwards to Friday
      calc_date := calc_date + 5 - start_dow;
      curr_diff := diff - 5 + start_dow;
    END IF;
  END IF;

  -- Move backwards or forwards by full weeks
  calc_date := calc_date + (curr_diff / 5) * 7;

  -- Process any remaining days, include weekend
  IF curr_diff % 5 != 0 THEN
    RETURN calc_date + curr_diff % 5 + weekend;
  ELSE
    RETURN calc_date;
  END IF;
END; $$ LANGUAGE plpgsql STRICT IMMUTABLE;

Fungsi ini dapat mengambil tanggal berapa pun untuk dihitung dari dan sejumlah hari ke depan (nilai positif diff ) atau masa lalu (nilai negatif diff ), termasuk perbedaan dalam minggu ini. Dan karena ini mengembalikan tanggal hari kerja sebagai skalar, penggunaan dalam kueri Anda sangat mudah:

SELECT * 
FROM table
WHERE analysis_date >= business_days_diff(current_date, -12);

Selain itu, Anda juga dapat meneruskan kolom dari meja Anda dan melakukan hal-hal funky seperti:

SELECT t1.some_value - t2.some_value AS value_diff
FROM table t1
JOIN table t2 ON t2.analysis_date = business_days_diff(t1.analysis_date, -12);

yaitu bergabung sendiri pada pemisahan hari kerja dalam jumlah tertentu.

Perhatikan bahwa fungsi ini mengasumsikan hari kerja Senin-Jumat dalam seminggu.

* Fungsi ini hanya melakukan aritmatika sederhana pada nilai skalar. CTE harus menyiapkan segala macam struktur untuk mendukung iterasi dan kumpulan record yang dihasilkan.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara terhubung ke server PostgreSQL melalui JDBC di Android?

  2. Apa yang menentukan apakah Rails menyertakan id::serial dalam definisi tabel?

  3. Menggunakan Docker, apa yang memicu PANIC:tidak dapat menemukan catatan pos pemeriksaan yang valid

  4. Opsi Pemulihan Bencana untuk PostgreSQL yang Di-deploy ke Hybrid Cloud

  5. PostgreSQL - Tabel kosong