여러 열에서 DISTINCT를 어떻게 선택합니까(또는 할 수 있습니까)?
-
09-06-2019 - |
문제
결합된 2개의 열이 모두 다른 테이블에서 모든 행을 검색해야 합니다.그래서 같은 날 같은 가격으로 발생한 다른 판매가 없는 모든 판매를 원합니다.요일 및 가격을 기준으로 고유한 판매가 활성 상태로 업데이트됩니다.
그래서 나는 생각하고 있습니다 :
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
);
어느 것이 많이 둘 중 누구보다 빠릅니다.현재 허용되는 답변의 성능을 10 - 15 요소로 누크합니다(PostgreSQL 8.4 및 9.1에 대한 테스트에서).
그러나 이는 여전히 최적과는 거리가 멀다.사용 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<>바이올린 여기
이전 SQL 바이올린
행을 식별하는 고유 키
테이블에 대한 기본 키 또는 고유 키가 없는 경우(id
예에서는 시스템 열로 대체할 수 있습니다. ctid
이 쿼리의 목적에 따라(다른 목적에는 적용되지 않음):
AND s1.ctid <> s.ctid
모든 테이블에는 기본 키가 있어야 합니다.아직 없는 경우 추가하세요.나는 제안한다 serial
또는 IDENTITY
Postgres 10+의 열.
관련된:
어떻게 이게 더 빨라?
하위 쿼리는 EXISTS
anti-semi-join은 첫 번째 속임수가 발견되는 즉시 평가를 중지할 수 있습니다(더 이상 살펴볼 필요가 없음).중복이 거의 없는 기본 테이블의 경우 이는 약간 더 효율적입니다.중복이 많으면 이렇게 됩니다. 방법 더 효율적입니다.
빈 업데이트 제외
이미 있는 행의 경우 status = 'ACTIVE'
이 업데이트는 아무 것도 변경하지 않지만 여전히 전체 비용으로 새 행 버전을 삽입합니다(사소한 예외가 적용됨).일반적으로 사용자는 이를 원하지 않습니다.다른 것을 추가하세요 WHERE
이를 방지하고 더욱 빠르게 만들려면 위에서 설명한 것과 같은 조건을 사용하세요.
만약에 status
정의된다 NOT NULL
, 다음과 같이 단순화할 수 있습니다.
AND status <> 'ACTIVE';
NULL 처리의 미묘한 차이
이 쿼리는 ( 현재 Joel의 답변이 허용되었습니다.)는 NULL 값을 동일하게 취급하지 않습니다.다음 두 행은 (saleprice, saledate)
"뚜렷한" 것으로 간주됩니다(인간의 눈과 동일하게 보임에도 불구하고).
(123, NULL)
(123, NULL)
또한 NULL 값은 SQL 표준에 따라 동일하게 비교되지 않기 때문에 고유 인덱스 및 거의 모든 곳에서 전달됩니다.보다:
오토, GROUP BY
, DISTINCT
또는 DISTINCT ON ()
NULL 값을 동일하게 취급합니다.달성하려는 목표에 따라 적절한 쿼리 스타일을 사용하십시오.이 더 빠른 쿼리를 계속 사용할 수 있습니다. IS NOT DISTINCT FROM
대신에 =
NULL 비교를 동일하게 만들기 위한 일부 또는 모든 비교.더:
비교되는 모든 열이 정의된 경우 NOT NULL
, 이견의 여지가 없습니다.
쿼리의 문제점은 GROUP BY 절(기본적으로 구별을 사용하여 수행)을 사용할 때 그룹화 기준 또는 집계 함수를 사용하는 열만 사용할 수 있다는 것입니다.잠재적으로 다른 값이 있으므로 열 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' 열에서 고유한 값을 선택하고 싶지만 '정렬' 열에 지정된 순서대로 정렬해야 합니다.다음을 사용하여 단 하나의 열의 고유 값을 얻을 수 없습니다.
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에서 작동할 수 있으며 그룹화 기능을 피하므로 솔루션별 그룹보다 더 빠를 것으로 예상됩니다.