가정 / 설명
infinity
상한 을 구분할 필요가 없습니다 ( upper(range) IS NULL
). (어느 쪽이든 가질 수 있지만이 방법은 더 간단합니다.)
이후 date
이산 형이며, 모든 범위는 기본이 [)
경계를.
설명서 당 :
내장 범위 유형 int4range
, int8range
및 daterange
모두는 하한을 포함하고 상한을 제외하는 표준 형식을 사용합니다. 즉 [)
.
다른 유형 (예 : tsrange
!)의 경우 가능한 경우 동일하게 적용합니다.
순수한 SQL 솔루션
명확성을 위해 CTE 사용 :
WITH a AS (
SELECT range
, COALESCE(lower(range),'-infinity') AS startdate
, max(COALESCE(upper(range), 'infinity')) OVER (ORDER BY range) AS enddate
FROM test
)
, b AS (
SELECT *, lag(enddate) OVER (ORDER BY range) < startdate OR NULL AS step
FROM a
)
, c AS (
SELECT *, count(step) OVER (ORDER BY range) AS grp
FROM b
)
SELECT daterange(min(startdate), max(enddate)) AS range
FROM c
GROUP BY grp
ORDER BY 1;
또는 하위 쿼리와 동일하지만 더 빠르지 만 읽기는 쉽지 않습니다.
SELECT daterange(min(startdate), max(enddate)) AS range
FROM (
SELECT *, count(step) OVER (ORDER BY range) AS grp
FROM (
SELECT *, lag(enddate) OVER (ORDER BY range) < startdate OR NULL AS step
FROM (
SELECT range
, COALESCE(lower(range),'-infinity') AS startdate
, max(COALESCE(upper(range), 'infinity')) OVER (ORDER BY range) AS enddate
FROM test
) a
) b
) c
GROUP BY grp
ORDER BY 1;
또는 하나의 하위 쿼리 수준이 적지 만 정렬 순서를 뒤집습니다.
SELECT daterange(min(COALESCE(lower(range), '-infinity')), max(enddate)) AS range
FROM (
SELECT *, count(nextstart > enddate OR NULL) OVER (ORDER BY range DESC NULLS LAST) AS grp
FROM (
SELECT range
, max(COALESCE(upper(range), 'infinity')) OVER (ORDER BY range) AS enddate
, lead(lower(range)) OVER (ORDER BY range) As nextstart
FROM test
) a
) b
GROUP BY grp
ORDER BY 1;
ORDER BY range DESC NULLS LAST
(와 NULLS LAST
)으로 두 번째 단계에서 창을 정렬하면 완전히 역순으로 정렬됩니다. 이 값은보다 저렴하고 (제작하기 쉽고, 제안 된 색인의 정렬 순서와 완벽하게 일치), 코너 케이스의 경우 정확해야 합니다 rank IS NULL
.
설명
a
:로 주문하는 동안 창 함수 를 사용하여 상한 ( ) 의 최대 값 을 range
계산하십시오 .
NULL 경계 (무 한계)를 +/-로 바꾸면 간단하게됩니다 (특별한 NULL 경우는 없음).enddate
infinity
b
: 같은 정렬 순서에서 이전 enddate
이 이전보다 startdate
빠르면 간격이 있고 새 범위 ( step
)가 시작 됩니다.
상한은 항상 제외됩니다.
c
: grp
다른 창 기능으로 단계를 계산하여 그룹 ( )을 형성합니다.
외부 SELECT
빌드에서 각 그룹의 하한에서 상한에 이릅니다. 보일라
자세한 설명과 함께 SO에 밀접하게 관련된 답변 :
plpgsql을 사용한 절차 적 솔루션
모든 테이블 / 열 이름에서 작동하지만 type에서만 작동합니다 daterange
.
루프와 절차 솔루션은 일반적으로 느린, 하지만 이 특별한 경우에 나는 함수가 실질적 것으로 예상 빨리 이 만 필요하기 때문에 하나의 순차 검색을 :
CREATE OR REPLACE FUNCTION f_range_agg(_tbl text, _col text)
RETURNS SETOF daterange AS
$func$
DECLARE
_lower date;
_upper date;
_enddate date;
_startdate date;
BEGIN
FOR _lower, _upper IN EXECUTE
format($$SELECT COALESCE(lower(t.%2$I),'-infinity') -- replace NULL with ...
, COALESCE(upper(t.%2$I), 'infinity') -- ... +/- infinity
FROM %1$I t
ORDER BY t.%2$I$$
, _tbl, _col)
LOOP
IF _lower > _enddate THEN -- return previous range
RETURN NEXT daterange(_startdate, _enddate);
SELECT _lower, _upper INTO _startdate, _enddate;
ELSIF _upper > _enddate THEN -- expand range
_enddate := _upper;
-- do nothing if _upper <= _enddate (range already included) ...
ELSIF _enddate IS NULL THEN -- init 1st round
SELECT _lower, _upper INTO _startdate, _enddate;
END IF;
END LOOP;
IF FOUND THEN -- return last row
RETURN NEXT daterange(_startdate, _enddate);
END IF;
END
$func$ LANGUAGE plpgsql;
요구:
SELECT * FROM f_range_agg('test', 'range'); -- table and column name
논리는 SQL 솔루션과 유사하지만 단일 패스로 수행 할 수 있습니다.
SQL 바이올린.
관련 :
동적 SQL에서 사용자 입력을 처리하기위한 일반적인 드릴 :
색인
이러한 각 솔루션에 대해 일반 (기본) btree 인덱스 range
는 큰 테이블에서 성능을 발휘하는 데 도움이됩니다.
CREATE INDEX foo on test (range);
btree 인덱스는 범위 유형에 제한적으로 사용 되지만 미리 정렬 된 데이터와 인덱스 전용 스캔을 얻을 수 있습니다.