sql >> Database teknologi >  >> RDS >> PostgreSQL

Hvordan får du en dynamisk 12 hverdagsvisning i Postgresql?

Dette kan løses med en 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);

Lidt forklaring:

  • Den indre CTE starter med at arbejde tilbage til den foregående mandag og kompensere for en current_date der falder på en weekenddag.
  • Den rekursive term tilføjer derefter rækker ved at gå hele uger tilbage (back_day - 7 for kalenderdatoen og go_back - 5 for hverdagene) indtil go_back = 0 .
  • Den ydre CTE returnerer back_day dato hvor go_back = 0 . Dette er derfor en skalær forespørgsel, og du kan bruge den som en underforespørgsel i et filterudtryk.

Du kan ændre antallet af hverdage for at se tilbage ved blot at ændre tallene 12 og 7 i den indledende SELECT i den indre CTE. Husk dog, at værdien skal være sådan, at den går tilbage til den foregående mandag, ellers mislykkes forespørgslen på grund af den samme indledende SELECT af den indre CTE.

En langt mere fleksibel (og sandsynligvis hurtigere*) løsning er at bruge følgende funktion:

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;

Denne funktion kan tage en hvilken som helst dato at beregne fra og et hvilket som helst antal dage ud i fremtiden (positiv værdi af diff ) eller fortiden (negativ værdi af diff ), inklusive forskelle inden for den aktuelle uge. Og da det returnerer hverdagsdatoen som en skalar, er brugen i din forespørgsel meget ligetil:

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

Udover det, kan du også gå ind i felter fra dit bord og lave funky ting som:

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

dvs. en selvtilslutning på et bestemt antal arbejdsdages adskillelse.

Bemærk, at denne funktion forudsætter en mandag-fredag ​​hverdag.

* Denne funktion udfører kun simpel aritmetik på skalarværdier. CTE'en skal opsætte alle slags strukturer for at understøtte iterationen og de resulterende rekordsæt.




  1. Forsøg på at genåbne en allerede lukket objekt sqlitedatabase

  2. Cayenne, Postgres:primær nøglegenerering

  3. Genererer du unikke koder i PHP/MySQL?

  4. Sådan bruger du en datamapper med sql-forespørgsler