2014-01-28 1 views
2

에 의해 GROUP과 (하위 쿼리를 통해) 두 번째로 낮은 값을 선택SQL : ... 위치로 분류 진수 값의 데이터베이스 감안할 때 절

CREATE TABLE ItemPrices 
    (`Company` varchar(11), `LocationID` char(5), `ProductLevel` varchar(11), `LowPrice` decimal(5,2)) 
; 

INSERT INTO ItemPrices 
    (`Company`, `LocationID`, `LowPrice`) 
VALUES 
    ('Company 1', 10001, 100.00), 
    ('Company 2', 10001, 200.00), 
    ('Company 3', 10001, 300.00), 
    ('Company 4', 10001, 400.00), 
    ('Company 1', 10002, 10.00), 
    ('Company 2', 10002, NULL), 
    ('Company 3', 10002, 30.00), 
    ('Company 4', 10002, 40.00) 
; 

그것은 위치에 대한 가장 낮은 값을 선택하기 쉽습니다 : 예상대로 작동하지 않는 가장 낮은 값을 필터링하고 다음으로 낮은 값을 반환하는 하위 쿼리를 사용하여

SELECT LocationID,min(LowPrice) FROM ItemPrices GROUP BY LocationID; 

LOCATIONID MIN(LOWPRICE) 
10001  100 
10002  10 

그러나

:

SELECT LocationID,min(LowPrice) FROM ItemPrices 
WHERE LowPrice > 
(SELECT min(LowPrice) 
    FROM ItemPrices) 
GROUP BY LocationID; 

LOCATIONID MIN(LOWPRICE) 
10001  100 
10002  30 

결과는 위치 ID 10001의 두 번째 최저 가격이 100이 아닌 200으로 표시되어야합니다.

모든 경우에 하위 쿼리가 단지 최저 LowPrice (10)을 반환하는 것으로 보입니다. 적절한 논리는 특정 값이 가장 낮은 값인 LocationID (100)을 찾은 다음 더 큰 값인 LocationID (200)을 찾아 더 큰 값을 반환하는 것입니다.

서브 쿼리가 가격의 하위 집합 만 평가해야한다는 것을 어떻게 인식시킬 수 있습니까?

여기에 GROUP BY을 입력하면 (LowPrice > (SELECT min(LowPrice) FROM ItemPrices GROUP BY LocationID)) Subquery returns more than 1 row이라고 표시됩니다. 당연히 모든 LocationID에 대해 하나의 행을 반환합니다.

답변

2

이 질문에 타이핑 할 때 필자는 상관 된 하위 쿼리가 필요하다는 생각을했습니다. 첫 번째 단계는 테이블 ItemPrices의 별명을 지정하는 것입니다. 외부 쿼리에 i을 사용하고 서브 쿼리에 s을 사용하고 있습니다.

SELECT i.LocationID,min(LowPrice) FROM ItemPrices i 
WHERE 
LowPrice > 
(SELECT min(LowPrice) 
FROM ItemPrices s 
WHERE i.LocationID = s.LocationID) 
GROUP BY LocationID; 

실행 해 봅시다!

LOCATIONID MIN(LOWPRICE) 
10001  200 
10002  30 

좋아요. 어떻게 생각하니?

SQLFiddle : http://sqlfiddle.com/#!2/341ce/5

0

사용에 문제가> 가장 저렴한 가격은 두 개 이상의 위치에 대한 연결하는 경우 일 것이다. 그래서 원하는 행동은 기술적으로는 첫 번째와 동일한 가격이지만 두 번째를 가장 저렴한 것으로 표시하는 것이지만 대신 가장 저렴한 세 번째를 표시합니다.

또 다른 해결 방법은 임시 테이블을 만들어 가장 저렴한 위치에 채운 다음 합치고 합치는 모든 레코드를 제거하는 것입니다.

#create a temp table to hold the cheapest 
create temporary table t (ID int, MINPRICE int, LOCATIONID int); 

#fill it with the very cheapest prices 
insert into t select ID, min(LOWPRICE) as MINPRICE, LOCATIONID from `ItemPrices` group by LOCATIONID; 

#select the second cheapest records by EXCLUDING the very cheapest 
select ItemPrices.ID, min(ItemPrices.LOWPRICE) as MINPRICE, ItemPrices.LOCATIONID 
from ItemPrices 
left join t 
     on t.ID = ItemPrices.ID 
where t.ID is null 
group by LOCATIONID; 

이 방법의 또 다른 장점은 N-1 회 임시 테이블에 결과를 반복하고 결과 마지막 반복을 작성함으로써, 제 N 최저가 당겨 확장 용이하다는 것이다.

고지 사항 : 예제 코드에서 구문을 검사하지는 않았지만 이론은 확실합니다. 50M가 넘는 테이블에서이 문제를 만났습니다. 빠르고 정확합니다.