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

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

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

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

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



답변

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
 )


답변

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

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되어 있으면 동의 할 여지가 없습니다.


답변

쿼리의 문제점은 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를 사용할 수도 있습니다. 일치하는 행이 하나만있는 경우 열 값을 반환하는 함수 만 사용해야합니다.


답변

하나의 열 ‘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)


답변

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

select distinct(col1, col2) from table

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

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

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


답변