You need to define "between two dates" more closely. Lower and upper bound included or excluded? A common definition would be to include the lower and exclude the upper bound of an interval. Plus, define the result as 0 when lower and upper bound are identical. This definition happens to coincide with date subtraction exactly.
SELECT date '2017-01-31' - date '2017-01-01' AS days_between
This exact definition is important for excluding Sundays. For the given definition an interval from Sun - Sun (1 week later) does not include the upper bound, so there is only 1 Sunday to subtract.
interval in days | sundays
0 | 0
1-6 | 0 or 1
7 | 1
8-13 | 1 or 2
14 | 2
...
An interval of 7 days always includes exactly one Sunday.
We can get the minimum result with a plain integer division (days / 7), which truncates the result.
The extra Sunday for the remainder of 1 - 6 days depends on the first day of the interval. If it's a Sunday, bingo; if it's a Monday, too bad. Etc. We can derive a simple formula from this:
SELECT days, sundays, days - sundays AS days_without_sundays
FROM (
SELECT z - a AS days
, ((z - a) + EXTRACT(isodow FROM a)::int - 1 ) / 7 AS sundays
FROM (SELECT date '2017-01-02' AS a -- your interval here
, date '2017-01-30' AS z) tbl
) sub;
Works for any given interval.
Note: isodow
, not dow
for EXTRACT()
.
To include the upper bound, just replace z - a
with (z - a) + 1
. (Would work without parentheses, due to operator precedence, but better be clear.)
Performance characteristic is O(1) (constant) as opposed to a conditional aggregate over a generated set with O(N).
Related:
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…