여러 열에서 어떻게 DISTINCT를 선택합니까?


415

결합 된 두 열이 모두 다른 테이블에서 모든 행을 검색해야합니다. 그래서 같은 날 같은 가격에 다른 판매가 없었던 모든 판매를 원합니다. 일 및 가격을 기준으로 고유 한 판매가 활성 상태로 업데이트됩니다.

그래서 나는 생각하고있다 :

UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
             FROM sales
             HAVING count = 1)

그러나 나의 뇌는 그것보다 더 멀리가는 것을 아프다.

답변:


436
SELECT DISTINCT a,b,c FROM t

이다 대략 동일합니다 :

SELECT a,b,c FROM t GROUP BY a,b,c

더 강력하기 때문에 GROUP BY 구문에 익숙해지는 것이 좋습니다.

귀하의 쿼리에 대해 다음과 같이하십시오.

UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
    SELECT id
    FROM sales S
    INNER JOIN
    (
        SELECT saleprice, saledate
        FROM sales
        GROUP BY saleprice, saledate
        HAVING COUNT(*) = 1 
    ) T
    ON S.saleprice=T.saleprice AND s.saledate=T.saledate
 )

117
이 쿼리는 정확하고 현재 몇 년 동안 받아 들여지고 있지만 매우 비효율적 이며 불필요합니다. 이것을 사용하지 마십시오. 다른 답변에 대안과 설명을 제공했습니다.
Erwin Brandstetter

1
SELECT DISTINCT a, b, c FROM t 가 SELECT a, b, c FROM t GROUP BY a, b, c와 정확히 같은 것이 아닙니까?
famargar

8
간단한 경우는 @famargar이지만 의미 상 의미가 다르며 더 큰 쿼리를 작성할 때 수행 할 수있는 단계가 다릅니다. 또한 기술 포럼의 사람들은 종종 사물에 대해 극도로 비판적 일 수 있습니다.이 맥락에서 내 게시물에 족제비 단어를 추가하는 것이 종종 유용하다는 것을 알았습니다.
Joel Coehoorn

344

지금까지 답변을 정리하고 정리하고 개선하면 다음과 같은 우수한 쿼리에 도달하게됩니다.

UPDATE sales
SET    status = 'ACTIVE'
WHERE  (saleprice, saledate) IN (
    SELECT saleprice, saledate
    FROM   sales
    GROUP  BY saleprice, saledate
    HAVING count(*) = 1 
    );

어느 쪽 보다 훨씬 빠릅니다. PostgreSQL 8.4 및 9.1에 대한 테스트에서 요소 10-15에 의해 현재 허용되는 답변의 성능을 깎습니다.

그러나 이것은 여전히 ​​최적과는 거리가 멀다. NOT EXISTS더 나은 성능을 위해서는 (반) 반 접합을 사용하십시오 . EXISTS표준 SQL이며 (이 질문이 나오기 오래 전부터 PostgreSQL 7.2 이후로) 영원히 존재했으며 제시된 요구 사항을 완벽하게 충족시킵니다.

UPDATE sales s
SET    status = 'ACTIVE'
WHERE  NOT EXISTS (
   SELECT FROM sales s1                     -- SELECT list can be empty for EXISTS
   WHERE  s.saleprice = s1.saleprice
   AND    s.saledate  = s1.saledate
   AND    s.id <> s1.id                     -- except for row itself
   )
AND    s.status IS DISTINCT FROM 'ACTIVE';  -- avoid empty updates. see below

db <> fiddle here
이전 SQL 바이올린

행을 식별하는 고유 키

테이블에 대한 기본 또는 고유 키가없는 경우 ( id예제에서) ctid이 쿼리의 목적으로 시스템 열 을 대체 할 수 있습니다 (다른 목적으로는 사용하지 않음).

   AND    s1.ctid <> s.ctid

모든 테이블에는 기본 키가 있어야합니다. 아직없는 경우 추가하십시오. Postgres 10+에서 a serial또는 IDENTITY열을 제안합니다 .

관련 :

이것이 얼마나 빠릅니까?

EXISTS안티-세미-조인 의 하위 쿼리 는 첫 번째 속줄이 발견되는 즉시 평가를 중지 할 수 있습니다 (더 이상 볼 필요가 없습니다). 중복이 거의없는 기본 테이블의 경우 약간 더 효율적입니다. 중복이 많은이 될 방법이 더 효율적입니다.

빈 업데이트 제외

이미이 status = 'ACTIVE'업데이트가 있는 행의 경우 아무 것도 변경하지 않지만 여전히 전체 비용으로 새 행 버전을 삽입합니다 (사소한 예외 적용). 일반적으로 이것을 원하지 않습니다. WHERE이것을 피하고 더 빠르게 만들기 위해 위에서 설명한 것과 같은 다른 조건을 추가하십시오 .

경우 status정의됩니다 NOT NULL, 당신은 할 수 단순화 할 수 있습니다 :

AND status <> 'ACTIVE';

열의 데이터 유형은 <>연산자 를 지원해야합니다 . json그렇지 않은 유형도 있습니다 . 보다:

NULL 처리의 미묘한 차이

이 쿼리는 Joel현재 허용하는 답변 과 달리 NULL 값을 동일하게 취급하지 않습니다. 다음 두 행은 (saleprice, saledate)"고유 한"것으로 간주됩니다 (사람의 눈과 동일하게 보이지만).

(123, NULL)
(123, NULL)

NULL 값은 SQL 표준에 따라 동일하게 비교되지 않기 때문에 고유 인덱스 및 거의 다른 곳에서도 전달됩니다. 보다:

OTOH, GROUP BY, DISTINCT또는 DISTINCT ON ()동일 등을 취급 NULL 값. 달성하려는 대상에 따라 적절한 쿼리 스타일을 사용하십시오. NULL 비교를 동일하게 만들기 위해 일부 또는 모든 비교 IS NOT DISTINCT FROM대신 이 빠른 쿼리를 계속 사용할 수 있습니다 =. 더:

비교중인 모든 열이 정의 NOT NULL되어 있으면 동의 할 여지가 없습니다.


16
좋은 대답입니다. 나는 SQL 서버 녀석이므로 IN () 검사와 함께 튜플을 사용하는 첫 번째 제안은 나에게 일어나지 않을 것입니다. 존재하지 않는 제안은 일반적으로 SQL Server에서 내부 조인과 동일한 실행 계획으로 끝날 것입니다.
Joel Coehoorn

2
좋은. 설명은 답의 가치를 크게 증가시킵니다. 계획이 Postgres 및 SQLServer와 어떻게 비교되는지 확인하기 위해 Oracle에서 몇 가지 테스트를 수행하려고합니다.
피터

2
@alairock : 어디서 얻었습니까? Postgres의 경우 그 반대 입니다. 모든 행을 계산하는 동안, count(*)이다 이상 효율적 count(<expression>). 먹어봐. Postgres는이 집계 함수 변형을 더 빠르게 구현합니다. 어쩌면 Postgres를 다른 RDBMS와 혼동하고 있습니까?
Erwin Brandstetter

6
@ alairock : 나는 그 페이지의 공동 저자가되고 어떤 종류의 말도하지 않습니다.
Erwin Brandstetter

2
@ ErwinBrandstetter, 당신은 항상 스택에 걸쳐 당신의 대답을 지적합니다. 수년 동안 거의 상상할 수없는 방식으로 도움을주었습니다. 이 예에서 나는 내 문제를 해결하는 몇 가지 다른 방법을 알고 있었지만 누군가가 가능성 사이의 효율성을 테스트했다는 것을 알고 싶었습니다. 감사합니다.
WebWanderer

24

쿼리의 문제점은 GROUP BY 절을 사용할 때 (기본적으로 distinct를 사용하여 수행함) 함수별로 그룹화하거나 집계하는 열만 사용할 수 있다는 것입니다. 다른 값이있을 수 있으므로 열 ID를 사용할 수 없습니다. 귀하의 경우에는 HAVING 절로 인해 항상 하나의 값만 있지만 대부분의 RDBMS는이를 인식하기에 충분하지 않습니다.

그러나 이것은 작동해야하며 조인이 필요하지 않습니다.

UPDATE sales
SET status='ACTIVE'
WHERE id IN (
  SELECT MIN(id) FROM sales
  GROUP BY saleprice, saledate
  HAVING COUNT(id) = 1
)

MIN 대신 MAX 또는 AVG를 사용할 수도 있습니다. 일치하는 행이 하나만있는 경우 열 값을 반환하는 함수 만 사용해야합니다.


1

하나의 열 'GrondOfLucht'에서 고유 값을 선택하고 싶지만 'sortering'열에 주어진 순서대로 정렬해야합니다. 하나의 열에 대한 고유 값을 사용할 수 없습니다.

Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering

또한 '정렬'열을 제공하고 'GrondOfLucht'AND '정렬'이 고유하지 않으므로 결과는 모든 행이됩니다.

GROUP을 사용하여 'GrondOfLucht'의 레코드를 'sortering'에 의해 주어진 순서대로 선택하십시오.

SELECT        GrondOfLucht
FROM            dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)

이것은 기본적으로 허용되는 답변이 무엇인지 설명하지만 그러한 이름을 예로 사용하지 않는 것이 좋습니다 (적어도 번역하십시오). 추신 : 네덜란드 인이더라도 모든 프로젝트에서 항상 영어로 모든 것을 명명하는 것이 좋습니다.
Kerwin Sneijders

0

DBMS가 다음과 같이 여러 열로 구별을 지원하지 않는 경우 :

select distinct(col1, col2) from table

일반적으로 다중 선택은 다음과 같이 안전하게 실행할 수 있습니다.

select distinct * from (select col1, col2 from table ) as x

이것은 대부분의 DBMS에서 작동 할 수 있으며 그룹화 기능을 피하기 때문에 솔루션별로 그룹화하는 것보다 빠를 것으로 예상됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.