programing

각 GROUP BY 그룹에서 첫 번째 행을 선택하시겠습니까?

abcjava 2023. 4. 11. 21:36
반응형

각 GROUP BY 그룹에서 첫 번째 행을 선택하시겠습니까?

알 수 있듯이, 저는 각 의 첫 GROUP BY.

내가 '오빠'를 가지고 있다면, '오빠'를 가지고 있다.purchases츠키다

SELECT * FROM purchases;

마이 출력:

아이디 고객.
1 조. 5
2 샐리 3
3 조. 2
4 샐리 1

i i드니다 for for for for for 。idtotal요.customer 다음과 같이 합니다.

SELECT FIRST(id), customer, FIRST(total)
FROM  purchases
GROUP BY customer
ORDER BY total DESC;

예상 출력:

첫 번째(ID) 고객. 최초(합계)
1 조. 5
2 샐리 3

DISTINCT ON Postgre에서 일반적으로 가장 간단하고 빠른 속도입니다.SQL.
(특정 워크로드의 퍼포먼스 최적화에 대해서는, 이하를 참조해 주세요).

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;

iftotal수 있습니다.null, 더하다NULLS LAST:

...
ORDER  BY customer, total DESC NULLS LAST, id;

어느 쪽이든 작동하지만 기존 인덱스와 일치해야 합니다.

db <>여기에 추가

주요 사항

DISTINCT ON postgre 입니다.표준 SQL 확장자(단,DISTINCTSELECT이치노

때 은 몇 해 주세요.DISTINCT ONclause. 행 값을 조합하여 중복을 정의합니다.매뉴얼:

두 행이 하나 이상의 열 값에서 다른 경우 분명히 구별되는 행으로 간주됩니다.이 비교에서는 null 값이 동일한 것으로 간주됩니다.

과감하게 강조해 주세요.

DISTINCT ON와 조합할 수 있습니다.의 선두 표현은ORDER BY표현 세트 안에 있어야 한다DISTINCT ON다만, 자유롭게 순서를 변경할 수 있습니다.예.
표현식을 추가할 수 있습니다.ORDER BY각 피어 그룹에서 특정 행을 선택합니다.또는 매뉴얼에 기재된 바와 같이:

DISTINCT ON맨 왼쪽에 식과 ORDER BY★★★★ORDER BY으로 구에는 각 DISTINCT ONsyslog.syslog.syslog.

는 ㅇㅇㅇㅇㅇㅇㅇㅇ다를 넣었습니다.id「 」 「 」 、 「 」
.id으로부터, 「최고」를 하고 있습니다.total

첫 순서와 하지 않는 방식으로 쿼리로 .ORDER BY. 예.

iftotal수 있습니다.null대부분의 경우 non-timeout 값이 가장 큰 행을 사용합니다.예시와 같이 추가합니다.참조:

리스트는 다음 식에 의해 제약되지 않습니다.DISTINCT ON ★★★★★★★★★★★★★★★★★」ORDER BY떤떤방방방 :

  • 표현은 에 포함할 필요가 없습니다.DISTINCT ON ★★★★★★★★★★★★★★★★★」ORDER BY.

  • 다른 표현식을 포함할 수 있습니다.SELECT목록. 이것은 복잡한 서브쿼리와 집계/창 기능을 대체하는 데 중요합니다.

Postgres 버전 8.3~15로 테스트했습니다.그러나 이 기능은 적어도 버전 7.1 이후부터 존재하므로 기본적으로 항상 사용할 수 있습니다.

색인

의 쿼리에 대한 완벽한 인덱스는 일치하는 시퀀스와 일치하는 정렬 순서를 가진 3개 열에 걸쳐 있는 다중인덱스입니다.

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

너무 전문화된 것 같습니다.그러나 특정 쿼리에 대한 읽기 성능이 중요한 경우 사용합니다.「 」가 DESC NULLS LAST쿼리에서 정렬 순서가 일치하고 인덱스가 완벽하게 적용되도록 인덱스에서 동일한 항목을 사용합니다.

효과/퍼포먼스 최적화

각 쿼리에 대한 맞춤형 인덱스를 작성하기 전에 비용과 이점을 평가하십시오.상기 지수의 잠재력은 데이터 분포에 크게 좌우된다.

인덱스는 미리 정렬된 데이터를 전달하기 때문에 사용됩니다.Postgres 9.2 이상에서는 인덱스가 기본 테이블보다 작은 경우에만 인덱스 스캔의 이점을 얻을 수 있습니다.그러나 인덱스는 전체를 스캔해야 합니다.예.

고객 한 명당경우(열에서 높은 카디널리티)customer을 사용하다어쨌든 정렬된 출력이 필요한 경우에는 더욱 그렇습니다.고객 1인당 행 수가 증가함에 따라 이점은 축소됩니다.
이상적으로는 RAM에서 관련된 정렬 단계를 충분히 처리하고 디스크에 흘리지 않는 것이 좋습니다.단, 일반적으로 설정work_mem 너무 높으면 부작용이 생길 수 있습니다.고려하다SET LOCAL매우 큰 질의에 사용됩니다. find with much much much find much much much find find find find 에서 필요한 것을 찾아보세요.EXPLAIN ANALYZE. 정렬 단계에서 "Disk:"를 언급하면 다음과 같은 추가 정보가 필요합니다.

고객 1인당 다수의 행(컬럼 내 카디널리티가 낮음)customer), "인덱스 건너뛰기 스캔" 또는 "인덱스 스캔"이 더 효율적입니다.하지만 Postgres 15까지는 구현되지 않았습니다.어떤 식으로든 이를 구현하기 위한 진지한 작업이 몇 년째 진행 중이지만 아직까지는 성공하지 못했습니다.여기랑 여기 보세요.
현재로서는 이를 대체할 더 빠른 쿼리 기법이 있습니다.특히 개별 고객을 수용하는 별도의 테이블이 있는 경우, 이것이 일반적인 사용 사례입니다.그렇지 않은 경우:

벤치마크

개별 답변을 참조하십시오.

CTE 및 윈도우 기능을 지원하는 데이터베이스에서는 다음 작업을 수행합니다.

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

벤치마크

가장 흥미로운 후보를 테스트했습니다.

  • 처음에는 Postgres 9.4 및 9.5사용합니다.
  • 나중에 Postgres 13 악센트 테스트 추가.

기본 테스트 셋업

테이블: ★★★★★★★★★★★★★★★★★★★★:purchases:

CREATE TABLE purchases (
  id          serial  -- PK constraint added below
, customer_id int     -- REFERENCES customer
, total       int     -- could be amount of money in Cent
, some_column text    -- to make the row bigger, more realistic
);

더미 데이터(일부 데드 튜플 포함), PK, 인덱스:

INSERT INTO purchases (customer_id, total, some_column)    -- 200k rows
SELECT (random() * 10000)::int             AS customer_id  -- 10k distinct 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를 사용했습니다.customer_id행이 적다customer_id.

" " " "purchases

설정: " " " 내의 20, 행purchases 10,, 10,000 †customer_id, avg. "1" "20" 입니다.
Postgres 9.5의 경우 86446명의 개별 고객을 대상으로 한 두 번째 테스트를 추가했습니다(고객당 평균 2.3줄).

여기서 가져온 쿼리로 생성됩니다.

Postgres 9.5를 위해 수집:

               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. row_number()CTE에서는 (다른 답변 참조)

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. rCTE †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. customer와 식사하다.LATERAL(여기를 참조)

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. array_agg()ORDER BY(다른 답변 참조)

SELECT (array_agg(id ORDER BY total DESC))[1] AS id
     , customer_id
     , max(total) AS total
FROM   purchases
GROUP  BY customer_id;

결과.

캐시와 비교하기 위해 를 사용한 위의 쿼리의 실행 시간, 베스트5 실행 중 하나입니다.

모든 쿼리가 인덱스 전용 검색을 사용함purchases2_3c_idx(서양속담, 돈속담)일부는 지수의 작은 크기에서 이익을 얻을 뿐이고, 다른 일부는 더 효과적이다.

9, 행 수 A당 최대 . 포스트그레스 9.4 (20,000 °C, 20/20 °C)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  

B와 하지만 C당 2 B - 1 - 2 . 3customer_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간단한 경우 테이블을 완전히 청소합니다.

Postgres의 중요한 변경 사항:

  • 일반적인 퍼포먼스 향상
  • CTE는 Postgres 12 이후 인라인을 사용할 수 있으므로 쿼리 1.과 쿼리 2.는 거의 동일한(같은 쿼리 계획)을 수행합니다.

D. B와 같습니다.고객 ID당 최대 20줄

1. 103 ms
2. 103 ms  
3.  23 ms  -- winner  
4.  71 ms  
5.  22 ms  -- winner
6.  81 ms  

db <>여기에 추가

E. 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을 사용한 악센트 테스트

고객 1인당 10,000 행 대 100 행 대 1.6 행.

F. 고객 1인당 최대 10,000줄

1. 526 ms
2. 527 ms  
3. 127 ms
4.   2 ms  -- winner !
5.   1 ms  -- winner !
6. 356 ms  

db <>여기에 추가

G. 고객 1인당 최대 100줄의 행이 있습니다.

1. 535 ms
2. 529 ms  
3. 132 ms
4. 108 ms  -- !
5.  71 ms  -- winner
6. 376 ms  

db <>여기에 추가

고객 1인당 최대 1.6줄의 H.

1.  691 ms
2.  684 ms  
3.  234 ms  -- winner
4. 4669 ms
5. 1089 ms
6. 1264 ms  

db <>여기에 추가

결론들

  • DISTINCT ON는 인덱스를 효과적으로 사용하며 일반적으로 그룹별로 몇 줄의 행에 대해 최적의 성능을 발휘합니다.또한 그룹당 행이 여러 개라도 성능이 상당히 우수합니다.

  • 그룹당 많은 행의 경우 rCTE를 사용하여 인덱스 건너뛰기 스캔을 에뮬레이트하면 별도의 룩업 테이블(사용 가능한 경우)을 사용하는 쿼리 기법 다음으로 성능이 우수합니다.

  • 현재 승인된 답변에서 시연된 기술은 성능 테스트에서 이긴 적이 없습니다.그때는 안 돼, 지금은 안 돼.결코 와는 거리가 멀다DISTINCT ON데이터 배포가 후자에 불리한 경우에도 마찬가지입니다.★★★★★★의 row_number()되는 것이 이치노

기타 벤치마크

Postgres 11.5에서는 1,000만 행과 60,000개의 고유 고객으로 구성된 "ogr"에 의한 벤치마크.결과는 지금까지의 결과와 일치합니다.

2011년부터의 오리지널(구식) 벤치마크

Postgre와 세 가지 테스트를 해봤는데65579개의 행과 관련된 각 열의 단일 열 btree 인덱스로 구성된 실제 테이블에서 SQL 9.1수행했으며 실행 시간이 5번으로 가장 길었습니다.
@OMGPonies의 첫 번째 쿼리()A의 솔루션()B 비교:

  1. 전체 테이블을 선택하면 이 경우 5958 행이 됩니다.
A: 567.218 ms
B: 386.673 ms
  1. 사용WHERE customer BETWEEN x AND y1000달러입니다.
A: 249.136 ms
B:  55.111 ms
  1. 1개의 .WHERE customer = x.
A:   0.143 ms
B:   0.072 ms

다른 답변에 설명된 지수를 사용하여 동일한 테스트를 반복했다.

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

1A: 277.953 ms  
1B: 193.547 ms

2A: 249.796 ms -- special index not used  
2B:  28.679 ms

3A:   0.120 ms  
3B:   0.048 ms

이는 그룹당 큰 n개의 일반적인 문제로, 이미 충분히 테스트되고 최적화된 솔루션을 보유하고 있습니다.개인적으로 나는 Bill Karwin의 왼쪽 결합 솔루션선호한다(다른 솔루션이 많은 원본 게시물).

MySQL 매뉴얼에는 이 일반적인 문제에 대한 해결책이 나와 있습니다.문제가 MySQL이 아닌 Postgres에 있는 경우에도 대부분의 SQL 변종에서 사용할 수 있습니다.일반 쿼리의 예: 특정 열의 그룹별 최대값을 유지하는 행을 참조하십시오.

에서는 Postgres를 사용할 수 .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이 아니라 인덱스화 됩니다).
  • 하면 .array_agg컬럼에서도 할 수 있습니다.max(total)더 심플합니다.
  • ★★★★★★★★★★★★★★★와 달리DISTINCT ON 를 사용합니다.array_agg「」를 해 둘 수 .GROUP BY이치노

쿼리:

SELECT purchases.*
FROM purchases
LEFT JOIN purchases as p 
ON 
  p.customer = purchases.customer 
  AND 
  purchases.total < p.total
WHERE p.total IS NULL

어떻게 하는 거야! (가봤어!)

우리는 각각의 구매에 대해 가장 높은 합계만을 가지고 있는지 확인하고 싶습니다.


이론적인 내용(쿼리만 이해하려면 이 부분은 생략)

합계를 T(고객, ID) 함수로 합니다.이 함수에 의해 이름과 ID가 지정된 값이 반환됩니다.T(고객, ID)가 최대치임을 증명하려면 다음 중 하나를 증명해야 합니다.

  • x x T ( customer , id )> T ( customer , x ) (이 합계는 해당 고객의 다른 합계보다 높습니다)

또는

  • ∃xx 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 |      |        |         |
+--------------+----------------+-----------------+------+--------+---------+

그게 우리에게 필요한 답이야

SubQ가 존재하기 때문에 이 솔루션은 Erwin이 지적한 것처럼 효율적이지 않습니다.

select * from purchases p1 where total in
(select max(total) from purchases where p1.customer=customer) order by total desc;

이 방법을 사용합니다(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;

경고: NULL 행을 무시합니다.


편집 1 - 대신 postgres 확장자를 사용합니다.

다음 방법을 사용합니다.http://pgxn.org/dist/first_last_agg/

ubuntu 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);

물론 집약에 적합하다고 생각되는 대로 정렬하고 필터링할 수 있습니다.이것은 매우 강력한 구문입니다.

SQL 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명을 제외합니다.

ARRAY_AGGPostgre의 함수SQL, U-SQL, IBM DB2Google BigQuery SQL:

SELECT customer, (ARRAY_AGG(id ORDER BY total DESC))[1], MAX(total)
FROM purchases
GROUP BY customer

매우 빠른 솔루션

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);

Snowflake/Teradata는 다음과 같은 절을 지원합니다.HAVING 기능 " " " " : "

SELECT id, customer, total
FROM PURCHASES
QUALIFY ROW_NUMBER() OVER(PARTITION BY p.customer ORDER BY p.total DESC) = 1

포스트그레SQL, 다른 방법으로는 window 기능을 와 조합하여 사용할 수 있습니다.SELECT DISTINCT:

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()

이 방법은 다음과 같습니다.

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;

각 기사의 최고가 선택

Windows 의 기능을 사용하면, 다음과 같이 할 수 있습니다.

    create table purchases (id int4, customer varchar(10), total integer);
    insert into purchases values (1, 'Joe', 5);
    insert into purchases values (2, 'Sally', 3);
    insert into purchases values (3, 'Joe', 2);
    insert into purchases values (4, 'Sally', 1);
    
    select ID, CUSTOMER, TOTAL from (
    select ID, CUSTOMER, TOTAL,
    row_number () over (partition by CUSTOMER order by TOTAL desc) RN
    from purchases) A where RN = 1;

여기에 이미지 설명 입력

승인된 OMG Pornes의 "Supported by any database" 솔루션은 테스트 속도가 좋습니다.

여기에서는, 같은 어프로치를 제공하지만, 보다 완전하고 깨끗한 데이타베이스 솔루션을 제공하고 있습니다.동점(고객당 최대 합계에 대해 여러 레코드가 있는 경우에도 고객당 1줄만 취득할 수 있다고 가정)이 고려되며, 구매 테이블 내의 실제 일치하는 행에 대해 기타 구매 필드(예: 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

특히 구매 테이블에 (고객, 합계)와 같은 복합 인덱스가 있는 경우에는 이 쿼리가 상당히 빠릅니다.

비고:

  1. t1, t2는 데이터베이스에 따라 삭제할 수 있는 서브쿼리 에일리어스입니다

  2. 경고:using (...) 및 .직접 확장해야 합니다. on t2.id = purchase.idSQLite를 사용하여 MySQL Postgre를 사용합니다.SQL.

  • 집계된 행 세트에서 (특정 조건에 따라) 행을 선택하는 경우.

  • 것을 (</FONT CHANGE:></FONT CHANGE:></FONT CHANGE:>)sum/avg의 집약 기능 의 집약 기능max/min할 수 없습니다.DISTINCT ON

다음 서브쿼리를 사용할 수 있습니다.

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 )다음 한 가지 제한으로 원하는 조건을 모두 충족합니다.는 여러 할 수 .

하지만 만약 당신이 그런 것을 하고 싶다면 당신은 아마도 윈도우 기능을 찾고 있을 것입니다.

SQL 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

그리고 사용된 열에 대해 클러스터화된 인덱스를 생성하는 것을 잊지 마십시오.

이는 전체 MAX FUNTION 및 GROUP BY ID 및 고객으로 쉽게 달성할 수 있습니다.

SELECT id, customer, MAX(total) FROM  purchases GROUP BY id, customer
ORDER BY total DESC;

창 기능 dbfiddle을 통한 접근법:

  1. 「」의 할당row_number 각 at at:row_number() over (partition by agreement_id, order_id ) as nrow
  2. 첫 합니다.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

언급URL : https://stackoverflow.com/questions/3800551/select-first-row-in-each-group-by-group

반응형