PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как да получите динамичен изглед за 12 работни дни в Postgresql?

Това може да се реши с 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);

Някои обяснения:

  • Вътрешният CTE започва, като се върне към предходния понеделник, компенсирайки current_date който се пада в почивен ден.
  • След това рекурсивният термин добавя редове, като се връща цели седмици назад (back_day - 7 за календарната дата и go_back - 5 за работните дни) до go_back = 0 .
  • Външният CTE връща back_day дата, където go_back = 0 . Следователно това е скаларна заявка и можете да я използвате като подзаявка във филтърен израз.

Можете да промените броя на работните дни, за да погледнете назад, като просто промените числата 12 и 7 в началния SELECT във вътрешния CTE. Имайте предвид обаче, че стойността трябва да е такава, че да се връща към предишния понеделник или заявката ще бъде неуспешна поради същия първоначален SELECT на вътрешния CTE.

Много по-гъвкаво (и вероятно по-бързо*) решение е да използвате следната функция:

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;

Тази функция може да вземе всяка дата за изчисляване и произволен брой дни в бъдещето (положителна стойност на diff ) или миналото (отрицателна стойност на diff ), включително разликите в рамките на текущата седмица. И тъй като връща датата на работния ден като скалар, използването във вашата заявка е много лесно:

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

Освен това можете също да предавате полета от вашата таблица и да правите странни неща като:

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

т.е. самостоятелно присъединяване при разделяне на определен брой работни дни.

Имайте предвид, че тази функция предполага работен ден от понеделник до петък в седмицата.

* Тази функция прави само проста аритметика със скаларни стойности. CTE трябва да настрои всякакъв вид структури, за да поддържа итерацията и произтичащите набори от записи.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ляво съединение с име на динамична таблица, получено от колона

  2. ГРЕШКА:изтриването на таблицата нарушава ограничението за външен ключ. Идентификаторът на ключ все още се използва от таблицата (много)

  3. Защо това правило не предотвратява дублирани нарушения на ключове?

  4. Как мога да получа списъка с бази данни в Postgresql в python

  5. SQLAlchemy:филтриране на стойности, съхранени във вложен списък на полето JSONB