sql >> Databáze >  >> RDS >> PostgreSQL

Jak získáte dynamický pohled na 12 pracovních dní v Postgresql?

To lze vyřešit 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);

Nějaké vysvětlení:

  • Vnitřní CTE začíná tím, že se vrací k předchozímu pondělí a kompenzuje current_date který připadá na víkendový den.
  • Rekurzivní výraz pak přidá řádky tak, že se vrátí o celé týdny zpět (back_day - 7 pro kalendářní datum a go_back - 5 pro pracovní dny) do go_back = 0 .
  • Vnější CTE vrací back_day datum, kde go_back = 0 . Jedná se tedy o skalární dotaz a můžete jej použít jako dílčí dotaz ve výrazu filtru.

Počet pracovních dní zpětného pohledu můžete změnit jednoduše změnou čísel 12 a 7 v počátečním SELECT ve vnitřním CTE. Mějte však na paměti, že hodnota by měla být taková, aby se vrátila k předchozímu pondělí, jinak dotaz selže kvůli stejnému počátečnímu SELECT vnitřního CTE.

Daleko flexibilnějším (a pravděpodobně rychlejším*) řešením je použití následující funkce:

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;

Výpočet této funkce může trvat libovolné datum a libovolný počet dní do budoucnosti (kladná hodnota diff ) nebo minulost (záporná hodnota diff ), včetně rozdílů v aktuálním týdnu. A protože vrací datum pracovního dne jako skalární, použití ve vašem dotazu je velmi jednoduché:

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

Kromě toho můžete také předávat pole ze svého stolu a dělat funky věci jako:

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. vlastní připojení po určitém počtu pracovních dnů odloučení.

Všimněte si, že tato funkce předpokládá pracovní den v týdnu od pondělí do pátku.

* Tato funkce provádí pouze jednoduchou aritmetiku na skalárních hodnotách. CTE musí nastavit všechny druhy struktur, které podporují iteraci a výsledné sady záznamů.




  1. Nainstalujte a nakonfigurujte MySQL Workbench na Ubuntu 16.04

  2. Výběr a vložení do více databází pomocí MySQL

  3. jak se vypořádat s přízvuky a podivnými znaky v databázi?

  4. Je COUNT rychlejší než stahování záznamů a počítání v kódu?