SQL 조인 : 일대 다 관계에서 마지막 레코드 선택
고객 테이블과 구매 테이블이 있다고 가정합니다. 각 구매는 하나의 고객에 속합니다. 하나의 SELECT 문에서 마지막 구매와 함께 모든 고객 목록을 얻고 싶습니다. 모범 사례는 무엇입니까? 인덱스 작성에 대한 조언이 있습니까?
답에 다음 표 / 열 이름을 사용하십시오.
- 고객 : 아이디, 이름
- 구매 : id, customer_id, item_id, 날짜
더 복잡한 상황에서 마지막 구매를 고객 테이블에 넣어 데이터베이스를 비정규 화하는 것이 (성능 측면에서) 유리합니까?
(구매) id가 날짜순으로 정렬되는 것이 보장된다면 LIMIT 1
?
이것은 greatest-n-per-group
StackOverflow에서 정기적으로 나타나는 문제 의 예입니다 .
일반적으로 해결하는 것이 좋습니다.
SELECT c.*, p1.*
FROM customer c
JOIN purchase p1 ON (c.id = p1.customer_id)
LEFT OUTER JOIN purchase p2 ON (c.id = p2.customer_id AND
(p1.date < p2.date OR (p1.date = p2.date AND p1.id < p2.id)))
WHERE p2.id IS NULL;
설명 : 행이 주어지면 동일한 고객 및 이후 날짜를 가진 p1
행이 없어야합니다 p2
(또는 관계가있는 경우 나중 id
). 이 사실을 발견하면 p1
해당 고객에 대한 가장 최근 구매입니다.
인덱스에 대해서, 나는에 복합 인덱스를 만들 것 purchase
열 이상 ( customer_id
, date
, id
). 이는 외부 색인이 포함 색인을 사용하여 수행되도록 할 수 있습니다. 최적화는 구현에 따라 다르므로 플랫폼에서 테스트해야합니다. RDBMS의 기능을 사용하여 최적화 계획을 분석하십시오. 예 EXPLAIN
를 들어 MySQL에서.
일부 사람들은 위에 표시된 솔루션 대신 하위 쿼리를 사용하지만 솔루션을 통해 관계를 쉽게 해결할 수 있습니다.
하위 선택을 사용 하여이 작업을 시도 할 수도 있습니다
SELECT c.*, p.*
FROM customer c INNER JOIN
(
SELECT customer_id,
MAX(date) MaxDate
FROM purchase
GROUP BY customer_id
) MaxDates ON c.id = MaxDates.customer_id INNER JOIN
purchase p ON MaxDates.customer_id = p.customer_id
AND MaxDates.MaxDate = p.date
선택은 모든 고객과 마지막 구매 날짜 에 참여해야 합니다.
데이터베이스를 지정하지 않았습니다. 분석 기능을 허용하는 것이면 GROUP BY보다이 방법을 사용하는 것이 더 빠를 수 있습니다 (Oracle에서는 확실히 빠르며, SQL Server 최신 버전에서는 더 빠를 수 있습니다).
SQL Server의 구문은 다음과 같습니다.
SELECT c.*, p.*
FROM customer c INNER JOIN
(SELECT RANK() OVER (PARTITION BY customer_id ORDER BY date DESC) r, *
FROM purchase) p
ON (c.id = p.customer_id)
WHERE p.r = 1
또 다른 방법은 NOT EXISTS
가입 조건에 조건을 사용하여 이후 구매를 테스트하는 것입니다.
SELECT *
FROM customer c
LEFT JOIN purchase p ON (
c.id = p.customer_id
AND NOT EXISTS (
SELECT 1 FROM purchase p1
WHERE p1.customer_id = c.id
AND p1.id > p.id
)
)
이 스레드를 내 문제에 대한 해결책으로 찾았습니다.
그러나 내가 시도했을 때 성능이 떨어졌습니다. Bellow는 더 나은 성능을위한 제안입니다.
With MaxDates as (
SELECT customer_id,
MAX(date) MaxDate
FROM purchase
GROUP BY customer_id
)
SELECT c.*, M.*
FROM customer c INNER JOIN
MaxDates as M ON c.id = M.customer_id
이것이 도움이 되길 바랍니다.
이것을 시도하십시오, 그것은 도움이 될 것입니다.
나는 이것을 내 프로젝트에서 사용했다.
SELECT
*
FROM
customer c
OUTER APPLY(SELECT top 1 * FROM purchase pi
WHERE pi.customer_id = c.Id order by pi.Id desc) AS [LastPurchasePrice]
SQLite에서 테스트되었습니다.
SELECT c.*, p.*, max(p.date)
FROM customer c
LEFT OUTER JOIN purchase p
ON c.id = p.customer_id
GROUP BY c.id
max()
집계 함수는 최신 구매가 각 그룹에서 선택 (- 경우 일반적이다하지만 날짜 열이) (최대 최신 제공함으로써 형식으로되어 있다고 가정)되어 있는지 확인합니다. 같은 날짜의 구매를 처리하려면을 사용할 수 있습니다 max(p.date, p.id)
.
인덱스 측면에서 (customer_id, date, [선택한 항목으로 반환하려는 다른 구매 열])과 함께 구매시 인덱스를 사용합니다.
LEFT OUTER JOIN
(반대 INNER JOIN
) 확인하여 구매 한 적이없는 고객도 포함되어 있는지 확인합니다.
PostgreSQL을 사용 DISTINCT ON
하는 경우 그룹에서 첫 번째 행을 찾는 데 사용할 수 있습니다 .
SELECT customer.*, purchase.*
FROM customer
JOIN (
SELECT DISTINCT ON (customer_id) *
FROM purchase
ORDER BY customer_id, date DESC
) purchase ON purchase.customer_id = customer.id
참고 그 DISTINCT ON
필드 (들) - 여기 customer_id
-에서 가장 왼쪽 필드 (들)과 일치해야합니다 ORDER BY
절.
주의 사항 : 이것은 비표준 조항입니다.
이것을 시도하십시오
SELECT
c.Id,
c.name,
(SELECT pi.price FROM purchase pi WHERE pi.Id = MAX(p.Id)) AS [LastPurchasePrice]
FROM customer c INNER JOIN purchase p
ON c.Id = p.customerId
GROUP BY c.Id,c.name;
'Programing' 카테고리의 다른 글
안드로이드에서 토스트의 위치를 바꾸는 방법? (0) | 2020.03.31 |
---|---|
Mac에 "watch"또는 "inotifywait"와 같은 명령이 있습니까? (0) | 2020.03.31 |
JavaScript의 "elseif"구문 (0) | 2020.03.31 |
Groovy 문자열을 int로 (0) | 2020.03.31 |
Linux에서 절대 경로로 파일 목록을 생성하려면 어떻게해야합니까? (0) | 2020.03.31 |