그것은 매우 의존 상황과 정확한 요구 사항에. 질문에 대한 나의 의견을 고려하십시오 .
간단한 솔루션
DISTINCT ON
Postgres 와 함께 :
SELECT DISTINCT ON (i.good, i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good, i.the_date, p.the_date DESC;
주문 결과.
또는 NOT EXISTS
표준 SQL에서 (내가 알고있는 모든 RDBMS에서 작동) :
SELECT i.the_date, p.the_date AS pricing_date, i.good, i.quantity, p.price
FROM inventory i
LEFT JOIN price p ON p.good = i.good AND p.the_date <= i.the_date
WHERE NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good = p.good
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
추가하지 않는 한 동일한 결과이지만 임의의 정렬 순서가 있습니다 ORDER BY
.
데이터 배포, 정확한 요구 사항 및 지수에 따라 이들 중 하나가 더 빠를 수 있습니다.
일반적으로 DISTINCT ON
승자이며 그 위에 정렬 된 결과를 얻습니다. 그러나 어떤 경우에는 다른 쿼리 기술이 훨씬 빠릅니다. 아래를 참조하십시오.
최대 / 최소값을 계산하기위한 하위 쿼리가있는 솔루션은 일반적으로 느립니다. CTE가있는 변형은 일반적으로 더 느립니다.
일반 답변 (다른 답변에서 제안한 것처럼)은 Postgres의 성능에 전혀 도움이되지 않습니다.
SQL 바이올린.
적절한 솔루션
문자열과 콜 레이션
우선, 최적이 아닌 테이블 레이아웃으로 어려움을 겪습니다. 사소한 것처럼 보이지만 스키마 정규화는 먼 길을 갈 수 있습니다.
로케일 , 특히 COLLATION 에 따라 문자 유형 ( text
,, varchar
...)으로 정렬 해야합니다 . 대부분의 DB는 로컬 규칙 세트를 사용합니다 (예 :) . 다음으로 알아보십시오.de_AT.UTF-8
SHOW lc_collate;
정렬 및 인덱스 조회 속도가 느려집니다 . 줄 (상품명)이 길수록 나빠집니다. 실제로 출력 (또는 정렬 순서)에서 데이터 정렬 규칙을 신경 쓰지 않으면 다음을 추가하면 더 빠를 수 있습니다 COLLATE "C"
.
SELECT DISTINCT ON (i.good COLLATE "C", i.the_date)
i.the_date, p.the_date AS pricing_date, i.good, p.price
FROM inventory i
LEFT JOIN price p ON i.good = p.good AND i.the_date >= p.the_date
ORDER BY i.good COLLATE "C", i.the_date, p.the_date DESC;
콜 레이션을 두 곳에 어떻게 추가했는지 주목하십시오.
테스트에서 각각 20k 개의 행과 매우 기본적인 이름 ( 'good123')으로 두 번 빠릅니다.
인덱스
쿼리에서 인덱스를 사용해야하는 경우 문자 데이터가있는 열은 일치하는 데이터 정렬을 사용해야합니다 ( good
예제에서).
CREATE INDEX inventory_good_date_desc_collate_c_idx
ON price(good COLLATE "C", the_date DESC);
SO에 대한이 관련 답변의 마지막 두 장을 읽으십시오.
다른 쿼리에서 다른 (또는 기본) 데이터 정렬에 따라 정렬 된 상품이 필요한 경우 동일한 열에서 다른 데이터 정렬을 가진 여러 인덱스를 가질 수도 있습니다.
정규화
중복 문자열 (좋은 이름)도 부풀게 도 느린 모든 것을 만드는 당신의 테이블과 인덱스를. 적절한 테이블 레이아웃을 사용하면 대부분의 문제를 피할 수 있습니다. 다음과 같이 보일 수 있습니다 :
CREATE TABLE good (
good_id serial PRIMARY KEY
, good text NOT NULL
);
CREATE TABLE inventory (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, quantity int NOT NULL
, PRIMARY KEY(good_id, the_date)
);
CREATE TABLE price (
good_id int REFERENCES good (good_id)
, the_date date NOT NULL
, price numeric NOT NULL
, PRIMARY KEY(good_id, the_date));
기본 키는 필요한 거의 모든 인덱스를 자동으로 제공합니다.
누락 된 세부 사항 에 따라 두 번째 컬럼에서 내림차순 으로 다중 컬럼 인덱스 가 price
성능을 향상시킬 수 있습니다.
CREATE INDEX price_good_date_desc_idx ON price(good, the_date DESC);
다시, 데이터 정렬 (위 참조) 쿼리와 일치해야합니다.
Postgres 9.2 이상 에서 인덱스 전용 스캔을위한 "커버 인덱스"는 특히 테이블에 추가 열이있어 테이블이 커버링 인덱스보다 훨씬 큰 경우에 도움이 될 수 있습니다.
이 결과 쿼리는 훨씬 빠릅니다.
존재하지 않음
SELECT i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
AND NOT EXISTS (
SELECT 1 FROM price p1
WHERE p1.good_id = p.good_id
AND p1.the_date <= i.the_date
AND p1.the_date > p.the_date
);
ON ON
SELECT DISTINCT ON (i.the_date)
i.the_date, p.the_date AS pricing_date, g.good, i.quantity, p.price
FROM inventory i
JOIN good g USING (good_id)
LEFT JOIN price p ON p.good_id = i.good_id AND p.the_date <= i.the_date
ORDER BY i.the_date, p.the_date DESC;
SQL 바이올린.
빠른 솔루션
그래도 여전히 빠르지 않으면 더 빠른 솔루션이있을 수 있습니다.
재귀 CTE / JOIN LATERAL
/ 상관 하위 쿼리
특히 상품당 가격 이 많은 데이터 배포의 경우 :
구체화 된 뷰
이것을 자주 그리고 빠르게 실행해야하는 경우 구체화 된보기를 작성하는 것이 좋습니다. 과거 날짜의 가격과 재고는 거의 변하지 않는다고 가정하는 것이 안전하다고 생각합니다. 결과를 한 번 계산하고 스냅 샷을 구체화 된보기로 저장하십시오.
Postgres 9.3+는 구체화 된 뷰를 자동으로 지원합니다. 이전 버전에서는 기본 버전을 쉽게 구현할 수 있습니다.