질문자 :David Wolever
GROUP BY
그룹화된 각 행 집합의 첫 번째 행을 선택하고 싶습니다.
특히, purchases
테이블이 있는 경우:
SELECT * FROM purchases;
내 출력:
ID | 고객 | 총 |
---|
1 | 조 | 5 |
2 | 출격 | 삼 |
삼 | 조 | 2 |
4 | 출격 | 1 |
각 customer
이 가장 많이 구매( total
id
를 조회하고 싶습니다. 이 같은:
SELECT FIRST(id), customer, FIRST(total) FROM purchases GROUP BY customer ORDER BY total DESC;
예상 출력:
FIRST(아이디) | 고객 | FIRST(총) |
---|
1 | 조 | 5 |
2 | 출격 | 삼 |
WITH summary AS ( SELECT p.id, p.customer, p.total, ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) AS rank FROM PURCHASES p) SELECT * FROM summary WHERE rank = 1
모든 데이터베이스에서 지원:
그러나 관계를 끊으려면 논리를 추가해야 합니다.
SELECT MIN(x.id), -- change to MAX if you want the highest x.customer, x.total FROM PURCHASES x JOIN (SELECT p.customer, MAX(total) AS max_total FROM PURCHASES p GROUP BY p.customer) y ON y.customer = x.customer AND y.max_total = x.total GROUP BY x.customer, x.total
OMG PoniesPostgreSQL에서 이것은 일반적으로 더 간단하고 빠릅니다 (아래에서 더 많은 성능 최적화).
SELECT DISTINCT ON (customer) id, customer, total FROM purchases ORDER BY customer, total DESC, id;
또는 출력 열의 서수를 사용하여 더 짧습니다(명확하지 않은 경우).
SELECT DISTINCT ON (2) id, customer, total FROM purchases ORDER BY 2, 3 DESC, 1;
total
이 NULL일 수 있는 경우(어느 쪽이든 다치지는 않지만 기존 인덱스와 일치 하고 싶을 것입니다):
... ORDER BY customer, total DESC NULLS LAST , id;
주요 포인트
DISTINCT ON
은 표준의 PostgreSQL 확장입니다(여기서 전체 SELECT
DISTINCT
만 정의됨).
DISTINCT ON
절에 임의의 수의 표현식을 나열합니다. 결합된 행 값은 중복을 정의합니다. 설명서:
분명히 두 행은 최소한 하나의 열 값이 다른 경우 구별되는 것으로 간주됩니다. 이 비교에서는 Null 값이 동일한 것으로 간주됩니다.
굵게 강조합니다.
DISTINCT ON
ORDER BY
와 결합될 수 있습니다. ORDER BY
선행 표현식은 DISTINCT ON
의 표현식 세트에 있어야 하지만 순서를 자유롭게 재정렬할 수 있습니다. 예시.
당신은 추가적인 표현을 추가 할 수 있습니다 ORDER BY
동료의 각 그룹에서 특정 행을 선택 할 수 있습니다. 또는 설명서에 따르면 다음과 같습니다.
DISTINCT ON
표현식은 맨 ORDER BY
표현식과 일치해야 합니다. ORDER BY
DISTINCT ON
그룹 내에서 원하는 행 우선 순위를 결정하는 추가 표현식이 포함됩니다.
나는 관계를 끊기 위해 마지막 항목으로 id
를 추가했습니다.
total
공유하는 각 그룹에서 id
를 가진 행을 선택하십시오."
그룹당 첫 번째를 결정하는 정렬 순서와 일치하지 않는 방식으로 결과를 정렬하려면 다른 ORDER BY
사용하여 외부 쿼리에서 위 쿼리를 중첩할 수 있습니다. 예시.
total
이 NULL일 수 있는 경우 가장 큰 null이 아닌 값을 가진 행을 원할 것입니다. 시연된 것처럼 NULLS LAST
추가합니다. 보다:
SELECT
목록 은 어떤 식으로든 DISTINCT ON
또는 ORDER BY
표현식에 의해 제한되지 않습니다. (위의 간단한 경우에는 필요하지 않음):
저는 Postgres 버전 8.3 – 13으로 테스트했습니다. 그러나 이 기능은 적어도 버전 7.1부터 존재했기 때문에 기본적으로 항상 그렇습니다.
색인
위 쿼리에 대한 완벽한 인덱스는 일치하는 순서와 일치하는 정렬 순서로 세 열 모두에 걸쳐 있는 다중 열 인덱스입니다.
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
너무 전문적일 수 있습니다. 그러나 특정 쿼리에 대한 읽기 성능이 중요한 경우 사용하십시오. DESC NULLS LAST
있는 경우 정렬 순서가 일치하고 인덱스를 적용할 수 있도록 인덱스에서 동일한 것을 사용합니다.
효율성 / 성능 최적화
각 쿼리에 대해 맞춤형 인덱스를 생성하기 전에 비용과 이점을 고려하십시오. 위 지수의 잠재력은 데이터 분포 에 크게 좌우됩니다.
인덱스는 미리 정렬된 데이터를 전달하기 때문에 사용됩니다. Postgres 9.2 이상에서 쿼리는 인덱스가 기본 테이블보다 작은 경우 인덱스 전용 스캔의 이점도 얻을 수 있습니다. 그러나 인덱스 전체를 스캔해야 합니다.
고객 (열 높은 기수 당 몇 행의 경우 customer
)이 매우 효율적입니다. 어쨌든 정렬된 출력이 필요한 경우에는 더욱 그렇습니다. 고객당 행 수가 증가하면 이점이 줄어듭니다.
이상적으로는 RAM에서 관련된 정렬 단계를 처리하고 디스크로 유출되지 않을 work_mem
그러나 일반적으로 work_mem
너무 높게 설정하면 역효과가 발생할 수 있습니다. 예외적으로 큰 쿼리의 경우 SET LOCAL
을 고려하십시오. EXPLAIN ANALYZE
필요한 양을 찾으십시오. 정렬 단계에서 " 디스크: "를 언급하면 다음 항목이 더 필요함을 나타냅니다.
고객 당 많은 행 (열 낮은 카디널리티를 들어 customer
)하는 느슨한 인덱스 스캔 (대) 것보다 효율적으로 (일명 "스킵 스캔")하지만이 포스트 그레스 (14) (대한 구현까지 구현되지 지수-아니라 검색 Postgres 15용으로 개발 중입니다. 여기 및 여기를 참조하십시오.)
현재로서는 이를 대체할 더 빠른 쿼리 기술이 있습니다. 특히 고유 고객을 보유하는 별도의 테이블이 있는 경우 이는 일반적인 사용 사례입니다. 그러나 다음을 수행하지 않는 경우에도:
벤치마크
별도의 답변을 참조하십시오.
Erwin Brandstetter벤치마크
Postgres 9.4 및 9.5 에서 가장 흥미로운 후보를 purchases
200k 행 과 10k 고유 customer_id
( 고객당 평균 20행 )로 구성된 실제 테이블로 테스트합니다.
Postgres 9.5의 경우 효과적으로 86446명의 개별 고객을 대상으로 두 번째 테스트를 실행했습니다. 아래를 참조하십시오( 고객당 평균 2.3행 ).
설정
메인 테이블
CREATE TABLE purchases ( id serial , customer_id int -- REFERENCES customer , total int -- could be amount of money in Cent , some_column text -- to make the row bigger, more realistic );
serial
(아래에 추가된 PK 제약 조건)과 정수 customer_id
왜냐하면 이것이 더 일반적인 설정이기 때문입니다. 또한 일반적으로 더 많은 열을 보충하기 위해 some_column
더미 데이터, PK, 인덱스 - 일반적인 테이블에도 데드 튜플이 있습니다.
INSERT INTO purchases (customer_id, total, some_column) -- insert 200k rows SELECT (random() * 10000)::int AS customer_id -- 10k customers , (random() * random() * 100000)::int AS total , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int) FROM generate_series(1,200000) g; ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id); DELETE FROM purchases WHERE random() > 0.9; -- some dead rows INSERT INTO purchases (customer_id, total, some_column) SELECT (random() * 10000)::int AS customer_id -- 10k customers , (random() * random() * 100000)::int AS total , 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int) FROM generate_series(1,20000) g; -- add 20k to make it ~ 200k CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id); VACUUM ANALYZE purchases;
customer
테이블 - 우수한 쿼리:
CREATE TABLE customer AS SELECT customer_id, 'customer_' || customer_id AS customer FROM purchases GROUP BY 1 ORDER BY 1; ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id); VACUUM ANALYZE customer;
9.5에 대한 내 두 번째 테스트에서 나는 같은 설정을 사용하지만, 함께 random() * 100000
생성 customer_id
당 몇 행 얻을 customer_id
.
purchases
위한 개체 크기
이 관련 답변에서 가져온 쿼리로 생성됨:
what | bytes/ct | bytes_pretty | bytes_per_row -----------------------------------+----------+--------------+--------------- core_relation_size | 20496384 | 20 MB | 102 visibility_map | 0 | 0 bytes | 0 free_space_map | 24576 | 24 kB | 0 table_size_incl_toast | 20529152 | 20 MB | 102 indexes_size | 10977280 | 10 MB | 54 total_size_incl_toast_and_indexes | 31506432 | 30 MB | 157 live_rows_in_text_representation | 13729802 | 13 MB | 68 ------------------------------ | | | row_count | 200045 | | live_tuples | 200045 | | dead_tuples | 19955 | |
쿼리
1. CTE의 row_number()
(다른 답변 참조 )
WITH cte AS ( SELECT id, customer_id, total , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn FROM purchases ) SELECT id, customer_id, total FROM cte WHERE rn = 1;
2. row_number()
(내 최적화)
SELECT id, customer_id, total FROM ( SELECT id, customer_id, total , row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn FROM purchases ) sub WHERE rn = 1;
3. DISTINCT ON
( 다른 답변 참조 )
SELECT DISTINCT ON (customer_id) id, customer_id, total FROM purchases ORDER BY customer_id, total DESC, id;
4. LATERAL 하위 쿼리가 LATERAL
( 여기 참조 )
WITH RECURSIVE cte AS ( ( -- parentheses required SELECT id, customer_id, total FROM purchases ORDER BY customer_id, total DESC LIMIT 1 ) UNION ALL SELECT u.* FROM cte c , LATERAL ( SELECT id, customer_id, total FROM purchases WHERE customer_id > c.customer_id -- lateral reference ORDER BY customer_id, total DESC LIMIT 1 ) u ) SELECT id, customer_id, total FROM cte ORDER BY customer_id;
5. LATERAL
customer
테이블( 여기 참조 )
SELECT l.* FROM customer c , LATERAL ( SELECT id, customer_id, total FROM purchases WHERE customer_id = c.customer_id -- lateral reference ORDER BY total DESC LIMIT 1 ) l;
6. ORDER BY
array_agg()
( 다른 답변 참조 )
SELECT (array_agg(id ORDER BY total DESC))[1] AS id , customer_id , max(total) AS total FROM purchases GROUP BY customer_id;
결과
EXPLAIN ANALYZE
(및 모든 옵션 끄기 )를 사용한 위 쿼리의 실행 시간 , 최고 5회 실행 .
모든 쿼리 purchases2_3c_idx
에서 인덱스 전용 스캔 을 사용했습니다(다른 단계 중에서). 그들 중 일부는 인덱스의 더 작은 크기를 위한 것이고 다른 것들은 더 효율적입니다.
A. 200,000개의 행이 있는 Postgres 9.4 및 customer_id
1. 273.274 ms 2. 194.572 ms 3. 111.067 ms 4. 92.922 ms -- ! 5. 37.679 ms -- winner 6. 189.495 ms
B. Postgres 9.5에서 A.와 동일
1. 288.006 ms 2. 223.032 ms 3. 107.074 ms 4. 78.032 ms -- ! 5. 33.944 ms -- winner 6. 211.540 ms
C. B.와 동일하지만 customer_id당 ~ 2.3개의 행이 customer_id
1. 381.573 ms 2. 311.976 ms 3. 124.074 ms -- winner 4. 710.631 ms 5. 311.976 ms 6. 421.679 ms
2021-08-11에 Postgres 13으로 다시 테스트
VACUUM ANALYZE
가 간단한 경우에 대해 테이블을 완전히 정리하기 때문에 행을 삭제하지 않습니다.
중요 변경 사항:
- 일반적인 성능 향상.
- CTE는 Postgres 12부터 인라인될 수 있으므로 쿼리 1과 쿼리 2는 이제 거의 동일하게 수행됩니다(동일한 쿼리 계획).
D. Like B. ~ customer_id당 20행
1. 103 ms 2. 103 ms 3. 23 ms -- winner 4. 71 ms 5. 22 ms -- winner 6. 81 ms
db<> 여기에 바이올린
E. Like C. ~ customer_id당 2.3행
1. 127 ms 2. 126 ms 3. 36 ms -- winner 4. 620 ms 5. 145 ms 6. 203 ms
db<> 여기에 바이올린
Postgres 13을 사용한 악센트 테스트
100만 행 , 고객당 10.000 대 100 대 1.6 행.
F. 고객당 ~ 10.000행
1. 526 ms 2. 527 ms 3. 127 ms 4. 2 ms -- winner ! 5. 1 ms -- winner ! 6. 356 ms
db<> 여기에 바이올린
G. 고객당 ~ 100행
1. 535 ms 2. 529 ms 3. 132 ms 4. 108 ms -- ! 5. 71 ms -- winner 6. 376 ms
db<> 여기에 바이올린
H. 고객당 ~ 1.6행
1. 691 ms 2. 684 ms 3. 234 ms -- winner 4. 4669 ms 5. 1089 ms 6. 1264 ms
db<> 여기에 바이올린
결론:
DISTINCT ON
은 인덱스를 효과적으로 사용하며 일반적으로 그룹당 소수의 행에 대해 최상의 성능을 발휘합니다. 그리고 그룹당 많은 행이 있어도 괜찮은 성능을 보입니다.
그룹당 많은 행의 경우 rCTE를 사용하여 인덱스 건너뛰기 스캔을 에뮬레이트하는 것이 최상의 성능을 발휘합니다. 별도의 조회 테이블(사용 가능한 경우)을 사용하는 쿼리 기술에 이어 두 번째입니다.
row_number()
(현재 허용되는 답변 기술)를 사용하면 성능 테스트에서 결코 승리할 수 없습니다 . 그때는 아니고 지금은 아닙니다. 데이터 분포가 불리한 경우에도 DISTINCT ON
가까이 오지 않습니다. 그것에 대한 유일한 좋은 점은 끔찍하게 확장되지 않고 그저 평범하다는 것입니다.
관련 벤치마크
다음은 Postgres 11.5 (2019년 9월 현재) 에서 1,000만 행과 60,000명의 고유한 "고객" 에 대한 "ogr" 테스트에 의한 새로운 것입니다. 결과는 우리가 지금까지 본 것과 여전히 일치합니다.
2011년의 원본(오래된) 벤치마크
65579개의 행으로 구성된 실제 테이블과 관련된 세 개의 열 각각에 대해 단일 열 btree 인덱스에서 PostgreSQL 9.1 을 사용하여 세 가지 테스트를 실행했으며 5회 실행 중 가장 좋은 실행 시간을 보였습니다.
@OMGPonies의 첫 번째 쿼리( A
)를 위의 DISTINCT ON
솔루션 ( B
)과 비교:
- 전체 테이블을 선택하면 이 경우 5958개의 행이 생성됩니다.
A: 567.218 ms B: 386.673 ms
-
WHERE customer BETWEEN x AND y
조건을 사용하면 1000개의 행이 생성됩니다.
A: 249.136 ms B: 55.111 ms
-
WHERE customer = x
단일 고객을 선택하십시오.
A: 0.143 ms B: 0.072 ms
다른 답변에 설명된 색인으로 동일한 테스트를 반복했습니다.
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Community Wiki이것은 이미 잘 테스트되고 고도로 최적화된 솔루션 이 있는 일반적인 가장 큰 그룹당 문제입니다. 개인적으로 Bill Karwin의 왼쪽 조인 솔루션을 선호합니다( 다른 솔루션이 많은 원본 게시물 ).
이 일반적인 문제에 대한 많은 솔루션은 놀랍게도 가장 공식 소스 중 하나인 MySQL 매뉴얼 에서 찾을 수 있습니다. 일반적인 쿼리의 예 :: 특정 열의 그룹별 최대값을 보유하는 행 을 참조하십시오.
TomasPostgres에서는 다음과 같이 array_agg
를 사용할 수 있습니다.
SELECT customer, (array_agg(id ORDER BY total DESC))[1], max(total) FROM purchases GROUP BY customer
이것은 각 고객의 가장 큰 구매의 id
를 제공합니다.
주의할 사항:
-
array_agg
는 집계 함수이므로 GROUP BY
와 함께 작동합니다. -
array_agg
사용하면 그 자체로 범위가 지정된 순서를 지정할 수 있으므로 전체 쿼리의 구조를 제한하지 않습니다. 기본값과 다른 작업을 수행해야 하는 경우 NULL을 정렬하는 방법에 대한 구문도 있습니다. - 배열을 빌드하면 첫 번째 요소를 가져옵니다. (Postgres 배열은 0 인덱스가 아니라 1 인덱스입니다).
- 세 번째 출력 열에 대해 비슷한 방식으로
array_agg
를 사용할 수 max(total)
가 더 간단합니다. -
DISTINCT ON
과 달리 array_agg
사용하면 다른 이유로 원하는 경우에 대비 GROUP BY
를 유지할 수 있습니다.
Paul A JungwirthSubQ가 있기 때문에 솔루션은 Erwin이 지적한 것처럼 매우 효율적이지 않습니다.
select * from purchases p1 where total in (select max(total) from purchases where p1.customer=customer) order by total desc;
cosmos쿼리:
SELECT purchases.* FROM purchases LEFT JOIN purchases as p ON p.customer = purchases.customer AND purchases.total < p.total WHERE p.total IS NULL
어떻게 작동합니까! (난 거기에 가본 적있어)
우리는 각 구매에 대해 가장 높은 합계만 가지고 있는지 확인하고 싶습니다.
일부 이론적인 내용 (쿼리만 이해하려는 경우 이 부분을 건너뛰십시오)
Total을 이름과 id가 주어진 값을 반환하는 함수 T(customer,id)로 둡니다. 주어진 합계(T(customer,id))가 가장 높다는 것을 증명하려면 다음 중 하나를 증명해야 합니다.
- ∀x T(customer,id) > T(customer,x) (이 총계는 해당 고객에 대한 다른 모든 총계보다 높음)
또는
- ¬∃x T(customer, id) < T(customer, x) (해당 고객에 대한 더 높은 합계가 존재하지 않음)
첫 번째 접근 방식은 내가 별로 좋아하지 않는 이름에 대한 모든 레코드를 가져와야 합니다.
두 번째는 이보다 높은 기록이 있을 수 없다고 말하는 현명한 방법이 필요합니다.
SQL로 돌아가기
이름에 테이블을 조인하고 합계가 조인된 테이블보다 작은 경우:
LEFT JOIN purchases as p ON p.customer = purchases.customer AND purchases.total < p.total
동일한 사용자에 대해 더 높은 합계를 가진 다른 레코드가 있는 모든 레코드가 조인되도록 합니다.
+--------------+---------------------+-----------------+------+------------+---------+ | purchases.id | purchases.customer | purchases.total | p.id | p.customer | p.total | +--------------+---------------------+-----------------+------+------------+---------+ | 1 | Tom | 200 | 2 | Tom | 300 | | 2 | Tom | 300 | | | | | 3 | Bob | 400 | 4 | Bob | 500 | | 4 | Bob | 500 | | | | | 5 | Alice | 600 | 6 | Alice | 700 | | 6 | Alice | 700 | | | | +--------------+---------------------+-----------------+------+------------+---------+
그러면 그룹화할 필요 없이 각 구매에 대해 가장 높은 총계를 필터링하는 데 도움이 됩니다.
WHERE p.total IS NULL +--------------+----------------+-----------------+------+--------+---------+ | purchases.id | purchases.name | purchases.total | p.id | p.name | p.total | +--------------+----------------+-----------------+------+--------+---------+ | 2 | Tom | 300 | | | | | 4 | Bob | 500 | | | | | 6 | Alice | 700 | | | | +--------------+----------------+-----------------+------+--------+---------+
그리고 그것이 우리에게 필요한 대답입니다.
khaled_gomaaSQL Server에서는 다음을 수행할 수 있습니다.
SELECT * FROM ( SELECT ROW_NUMBER() OVER(PARTITION BY customer ORDER BY total DESC) AS StRank, * FROM Purchases) n WHERE StRank = 1
설명:여기서 Group by 는 고객을 기준으로 한 다음 합계로 주문한 다음 이러한 각 그룹에 StRank로 일련 번호가 부여되고 StRank가 1인 첫 번째 1명의 고객을 꺼냅니다.
Diwas Poudel이 방법을 사용합니다(postgresql만 해당): https://wiki.postgresql.org/wiki/First/last_%28aggregate%29
-- Create a function that always returns the first non-NULL item CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement ) RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$ SELECT $1; $$; -- And then wrap an aggregate around it CREATE AGGREGATE public.first ( sfunc = public.first_agg, basetype = anyelement, stype = anyelement ); -- Create a function that always returns the last non-NULL item CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement ) RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$ SELECT $2; $$; -- And then wrap an aggregate around it CREATE AGGREGATE public.last ( sfunc = public.last_agg, basetype = anyelement, stype = anyelement );
그러면 예제가 거의 그대로 작동해야 합니다.
SELECT FIRST(id), customer, FIRST(total) FROM purchases GROUP BY customer ORDER BY FIRST(total) DESC;
CAVEAT: NULL 행을 무시합니다.
편집 1 - 대신 postgres 확장을 사용하십시오.
이제 저는 이 방법을 사용합니다: http://pgxn.org/dist/first_last_agg/
우분투 14.04에 설치하려면:
apt-get install postgresql-server-dev-9.3 git build-essential -y git clone git://github.com/wulczer/first_last_agg.git cd first_last_app make && sudo make install psql -c 'create extension first_last_agg'
처음과 마지막 기능을 제공하는 포스트그레스 확장입니다. 분명히 위의 방법보다 빠릅니다.
편집 2 - 정렬 및 필터링
집계 함수(이와 같은)를 사용하는 경우 데이터를 이미 정렬할 필요 없이 결과를 정렬할 수 있습니다.
http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES
따라서 주문과 동일한 예는 다음과 같습니다.
SELECT first(id order by id), customer, first(total order by id) FROM purchases GROUP BY customer ORDER BY first(total);
물론 집계에 적합하다고 생각되는 대로 주문하고 필터링할 수 있습니다. 매우 강력한 구문입니다.
matiuPostgreSQL , U-SQL , IBM DB2 및 Google BigQuery SQL에 ARRAY_AGG
함수 사용:
SELECT customer, (ARRAY_AGG(id ORDER BY total DESC))[1], MAX(total) FROM purchases GROUP BY customer
Valentin Podkamennyi매우 빠른 솔루션
SELECT a.* FROM purchases a JOIN ( SELECT customer, min( id ) as id FROM purchases GROUP BY customer ) b USING ( id );
테이블이 id로 인덱싱되면 정말 매우 빠릅니다.
create index purchases_id on purchases (id);
Alejandro Salamanca MazueloSnowflake/Teradata는 창 함수에 대해 HAVING
처럼 작동QUALIFY
SELECT id, customer, total FROM PURCHASES QUALIFY ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) = 1
Lukasz Szozda승인된 OMG Ponies의 "모든 데이터베이스에서 지원" 솔루션은 내 테스트에서 속도가 좋습니다.
여기에서는 동일한 접근 방식을 제공하지만 더 완전하고 깨끗한 모든 데이터베이스 솔루션을 제공합니다. 동점은 고려되며(각 고객에 대해 하나의 행만 얻고 고객당 최대 합계에 대해 여러 레코드를 얻으려는 경우) 구매 테이블의 실제 일치 행에 대해 다른 구매 필드(예: purchase_payment_id)가 선택됩니다.
모든 데이터베이스에서 지원:
select * from purchase join ( select min(id) as id from purchase join ( select customer, max(total) as total from purchase group by customer ) t1 using (customer, total) group by customer ) t2 using (id) order by customer
이 쿼리는 특히 구매 테이블에 (customer, total)과 같은 복합 인덱스가 있는 경우 상당히 빠릅니다.
주목:
t1, t2는 데이터베이스에 따라 제거될 수 있는 하위 쿼리 별칭입니다.
주의 사항 : using (...)
절은 현재 MS-SQL 및 Oracle db에서 2017년 1월 이 편집본에서 지원되지 않습니다. 예를 들어 on t2.id = purchase.id
등으로 확장해야 합니다. USING 구문 SQLite, MySQL 및 PostgreSQL에서 작동합니다.
Johnny WongPostgreSQL에서 또 다른 가능성은 SELECT DISTINCT
와 함께 first_value
창 함수를 사용하는 것입니다.
select distinct customer_id, first_value(row(id, total)) over(partition by customer_id order by total desc, id) from purchases;
나는 합성 (id, total)
만들었으므로 두 값 모두 동일한 집계에서 반환됩니다. first_value()
두 번 적용할 수 있습니다.
user8870331이 방법은 저에게 효과적입니다.
SELECT article, dealer, price FROM shop s1 WHERE price=(SELECT MAX(s2.price) FROM shop s2 WHERE s1.article = s2.article GROUP BY s2.article) ORDER BY article;
각 기사에서 가장 높은 가격을 선택하십시오.
uncle bob 다음 하위 쿼리를 사용할 수 있습니다.
SELECT ( SELECT **id** FROM t2 WHERE id = ANY ( ARRAY_AGG( tf.id ) ) AND amount = MAX( tf.amount ) ) id, name, MAX(amount) ma, SUM( ratio ) FROM t2 tf GROUP BY name
amount = MAX( tf.amount )
를 원하는 조건으로 바꿀 수 있습니다. 이 하위 쿼리는 둘 이상의 행을 반환해서는 안 됩니다.
그러나 그러한 일을하고 싶다면 아마도 창 기능을 찾고있을 것입니다.
Eugen KonkovSQl Server의 경우 가장 효율적인 방법은 다음과 같습니다.
with ids as ( --condition for split table into groups select i from (values (9),(12),(17),(18),(19),(20),(22),(21),(23),(10)) as v(i) ) ,src as ( select * from yourTable where <condition> --use this as filter for other conditions ) ,joined as ( select tops.* from ids cross apply --it`s like for each rows ( select top(1) * from src where CommodityId = ids.i ) as tops ) select * from joined
사용된 열에 대해 클러스터형 인덱스를 만드는 것을 잊지 마십시오.
BazSTR창 기능 dbfiddle을 통한 내 접근 방식 :
- 각 그룹에
row_number
할당 row_number() over (partition by agreement_id, order_id ) as nrow
- 그룹에서 첫 번째 행만 사용:
filter (where nrow = 1)
with intermediate as (select *, row_number() over ( partition by agreement_id, order_id ) as nrow, (sum( suma ) over ( partition by agreement_id, order_id ))::numeric( 10, 2) as order_suma, from <your table>) select *, sum( order_suma ) filter (where nrow = 1) over (partition by agreement_id) from intermediate
Eugen Konkov출처 : http:www.stackoverflow.com/questions/3800551/select-first-row-in-each-group-by-group