Skip to content

Instantly share code, notes, and snippets.

@mtarnovan
Created October 17, 2018 13:15
Show Gist options
  • Save mtarnovan/c5d2cd5d9a3a49bc040804cd69a91560 to your computer and use it in GitHub Desktop.
Save mtarnovan/c5d2cd5d9a3a49bc040804cd69a91560 to your computer and use it in GitHub Desktop.
Postgres (date) range coverage function
CREATE IF NOT EXISTS EXTENSION btree_gist;
CREATE TABLE split_vat (
cif varchar,
interval daterange NOT NULL,
applies_split_vat boolean NOT NULL,
EXCLUDE USING
gist (interval WITH &&, cif WITH =)
);
insert into split_vat values('1', daterange('2017-12-15', '2017-12-30', '[]'), true);
insert into split_vat values('1', daterange('2018-02-15', '2018-03-15', '[]'), true);
CREATE OR REPLACE FUNCTION uncovered_split_vat(daterange default daterange('2017-09-01', current_date), varchar default null)
RETURNS TABLE(cif varchar, uncovered_ranges daterange)
AS $$
WITH RECURSIVE calendar AS (
SELECT
$1 AS left,
$1 AS center,
$1 AS right,
$2 AS cif
UNION
SELECT
CASE split_vat.interval && calendar.left
WHEN TRUE THEN daterange(lower(calendar.left), lower(split_vat.interval * calendar.left))
ELSE daterange(lower(calendar.right), lower(split_vat.interval * calendar.right))
END AS left,
CASE split_vat.interval && calendar.left
WHEN TRUE THEN split_vat.interval * calendar.left
ELSE split_vat.interval * calendar.right
END AS center,
CASE split_vat.interval && calendar.right
WHEN TRUE THEN daterange(upper(split_vat.interval * calendar.right), upper(calendar.right))
ELSE daterange(upper(split_vat.interval * calendar.left), upper(calendar.left))
END AS right,
split_vat.cif as cif
FROM calendar
JOIN split_vat ON
split_vat.interval && $1 AND
split_vat.interval <> calendar.center AND (
split_vat.interval && calendar.left OR
split_vat.interval && calendar.right
) AND ($2 IS NULL OR split_vat.cif = $2)
)
SELECT *
FROM (
SELECT
a.cif AS cif,
a.left AS uncovered_ranges
FROM calendar a
LEFT OUTER JOIN calendar b ON
a.left <> b.left AND
a.left @> b.left
GROUP BY a.left, a.cif
HAVING NOT bool_or(COALESCE(a.left @> b.left, FALSE))
UNION
SELECT
a.cif AS cif,
a.right AS uncovered_ranges
FROM calendar a
LEFT OUTER JOIN calendar b ON
a.right <> b.right AND
a.right @> b.right
GROUP BY a.right, a.cif
HAVING NOT bool_or(COALESCE(a.right @> b.right, FALSE))
) a
$$ LANGUAGE SQL STABLE;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment