sql >> Database >  >> RDS >> PostgreSQL

Hoe krijg je een dynamische weergave van 12 werkdagen in Postgresql?

Dit kan worden opgelost met een 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);

Enige uitleg:

  • De innerlijke CTE begint door terug te werken naar de vorige maandag, ter compensatie van een current_date dat valt op een weekenddag.
  • De recursieve term voegt dan rijen toe door hele weken terug te gaan (back_day - 7 voor de kalenderdatum en go_back - 5 voor de werkdagen) tot go_back = 0 .
  • De buitenste CTE retourneert de back_day datum waar go_back = 0 . Dit is dus een scalaire query en u kunt deze gebruiken als subquery in een filterexpressie.

U kunt het aantal werkdagen om terug te kijken wijzigen door simpelweg de cijfers 12 . te wijzigen en 7 in de eerste SELECT in de binnenste CTE. Houd er echter rekening mee dat de waarde zo moet zijn dat deze teruggaat naar de vorige maandag, anders mislukt de zoekopdracht vanwege dezelfde initiële SELECT van de innerlijke CTE.

Een veel flexibelere (en waarschijnlijk snellere*) oplossing is om de volgende functie te gebruiken:

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;

Deze functie kan elke datum gebruiken om vanaf te berekenen en een willekeurig aantal dagen in de toekomst (positieve waarde van diff ) of het verleden (negatieve waarde van diff ), inclusief verschillen binnen de huidige week. En aangezien het de werkdagdatum als een scalaire waarde retourneert, is het gebruik in uw zoekopdracht heel eenvoudig:

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

Afgezien daarvan kun je ook in velden van je tafel passen en funky dingen doen zoals:

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

d.w.z. een self-join op een bepaald aantal werkdagen scheiding.

Houd er rekening mee dat deze functie uitgaat van een werkdag van maandag tot en met vrijdag.

* Deze functie voert alleen eenvoudige berekeningen uit op scalaire waarden. De CTE moet allerlei structuren opzetten om de iteratie en de resulterende recordsets te ondersteunen.




  1. Schakel native JSF-implementatie van webLogic-server uit en gebruik myfaces-implementatie

  2. Mysql-groepsgegevens op tijd

  3. MySQL INSERT- of REPLACE-opdrachten

  4. JSON_STORAGE_SIZE() - Vind de opslaggrootte van een JSON-document in MySQL