PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak uzyskać dynamiczny widok 12 dni roboczych w Postgresql?

Można to rozwiązać za pomocą 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);

Kilka wyjaśnień:

  • Wewnętrzny CTE zaczyna się od powrotu do poprzedniego poniedziałku, kompensując current_date który wypada w weekend.
  • Termin cykliczny dodaje następnie wiersze, cofając się o pełne tygodnie (back_day - 7 dla daty kalendarza i go_back - 5 za dni robocze) do go_back = 0 .
  • Zewnętrzny CTE zwraca back_day data, w której go_back = 0 . Jest to zatem zapytanie skalarne i można go używać jako podzapytania w wyrażeniu filtrującym.

Możesz zmienić liczbę dni roboczych na przeglądanie wstecz, po prostu zmieniając liczby 12 i 7 w początkowym SELECT w wewnętrznym CTE. Pamiętaj jednak, że wartość powinna być taka, aby wracała do poprzedniego poniedziałku, w przeciwnym razie zapytanie się nie powiedzie, z powodu tego samego początkowego SELECT wewnętrznego CTE.

O wiele bardziej elastycznym (i prawdopodobnie szybszym*) rozwiązaniem jest użycie następującej funkcji:

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;

Ta funkcja może liczyćod dowolnej daty i dowolnej liczby dni w przyszłość (dodatnia wartość diff ) lub przeszłość (wartość ujemna diff ), w tym różnice w bieżącym tygodniu. A ponieważ zwraca datę dnia roboczego jako wartość skalarną, użycie w zapytaniu jest bardzo proste:

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

Poza tym możesz również przekazywać pola ze swojego stołu i robić takie zabawne rzeczy, jak:

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);

tj. samodzielne dołączenie po separacji określonej liczby dni roboczych.

Zwróć uwagę, że ta funkcja zakłada pracę w tygodniu od poniedziałku do piątku.

* Ta funkcja wykonuje tylko prostą arytmetykę na wartościach skalarnych. CTE musi skonfigurować wszystkie rodzaje struktur do obsługi iteracji i wynikowych zestawów rekordów.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Adnotacja Spring Boot Query z nativeQuery nie działa w Postgresql

  2. Czy jest jakiś sposób, aby PostgreSQL nie zwijał interpunkcji i spacji podczas porównywania przy użyciu języka?

  3. Postgres — pobierz kopię całej bazy danych ze zdalnego serwera na lokalną maszynę deweloperską

  4. Połączenie db Psycopg2 zawiesza się po utracie połączenia sieciowego

  5. Konwertuj tablicę obiektów na tablicę zgodną z nodejs/pg/unnest