2011-02-07 3 views
0

어색한 제목을 용서하십시오. 나는 내 질문을 한 문구로 증오하는 데 어려움을 겪었다. 누구든지 더 나은 것을 생각해 낼 수 있다면, 자유롭게 느끼십시오."many"의 일부 기준에 따라 일대 다 연관을 기반으로하는 쿼리의 결과를 그룹화하는 방법은 무엇입니까?

나는 다음과 같은 단순화 된 스키마가 :

SELECT * FROM locations 
WHERE latitude IS NOT NULL AND longitude IS NOT NULL 
    AND ABS(latitude - 30) + ABS(longitude - 30) < 50 
ORDER BY ABS(latitude - 30) + ABS(longitude - 30) ASC 

I :

vendors 
    INT id 

locations 
    INT id 
    INT vendor_id 
    FLOAT latitude 
    FLOAT longitude 

내가 돌아 반경의 근사치에 의해 제한 근접으로 분류 가장 가까운 업체의 목록, 완벽 할 생각을 이 순간에 주문/한도의 반복을 둘러싼 내 길을 찾을 수 없습니다. 처음에는 SELECT 필드 사이에 "거리"로 앨리어싱을 시도했지만 psql은이 별칭을 WHERE 절에서 사용할 수 없다고 말했습니다. 벌금. 이 주위에 멋진 바지 방법이 있다면, 나는 모든 귀에 있지만 내 주요 질문에 대한 :

내가 뭘하고 싶은지, 각각의 가장 가까운 위치와 함께 조인 업체 목록을 반환하는 것입니다 이 목록을 근접으로 정렬하고 반지름으로 제한하십시오.

두 개의 공급 업체가 있다고 가정하면 각각은 두 곳입니다. 반경을 제한하는 쿼리를 사용하여 네 위치 중 하나만이 그 위치의 관련 공급 업체를 공급 업체 자체와 함께 반환하도록합니다. 반경이 모든 위치를 포함한다면, 나는 벤더 1이 가장 가까운 위치에, 벤더 2가 가장 가까운 위치에, 결국 벤더 1과 2를 가까운 위치의 근접을 기준으로 정렬하기를 원할 것이다.

MySQL에서 나는 GROUP BY을 사용하여 각 공급 업체 행에서 가장 가까운 위치를 얻은 다음 MIN(distance)을 사용했습니다. 그러나 PostgreSQL은 GROUP BY의 사용에있어보다 엄격한 것으로 보입니다.

가능하면 SELECT 절에 간섭하는 것을 피하고 싶습니다. 가능한 경우 위 질문에 WHEREORDER 부분을 다시 사용하고 싶습니다. 그러나 이것들은 절대로 절대적인 요구 사항은 아닙니다.

나는 DISTINCT ONGROUP BY에 hackneyed 시도를했다. 그러나 이것들은 내가 다른 곳에서 미러링 된 문장을 놓치고 있다는 점에서 상당한 문제를 일으켰다. 지금은 자세하게 설명하지 않을 것이다.


솔루션

나는 OMG Ponies' excellent answer 기반으로 솔루션을 채택 끝났다. OMG 조랑말 '솔루션에서

SELECT vendors.* FROM (
    SELECT locations.*, 
    ABS(locations.latitude - 2.1) + ABS(locations.longitude - 2.1) AS distance, 
    ROW_NUMBER() OVER(PARTITION BY locations.locatable_id, locations.locatable_type 
     ORDER BY ABS(locations.latitude - 2.1) + ABS(locations.longitude - 2.1) ASC) AS rank 
    FROM locations 
    WHERE locations.latitude IS NOT NULL 
    AND locations.longitude IS NOT NULL 
    AND locations.locatable_type = 'Vendor' 
) ranked_locations 
INNER JOIN vendors ON vendors.id = ranked_locations.locatable_id 
WHERE (ranked_locations.rank = 1) 
    AND (ranked_locations.distance <= 0.5) 
ORDER BY ranked_locations.distance; 

일부 편차 :

  • 위치는 현재 다형 _type를 통해 연결됩니다. 약간의 전제가 바뀝니다.
  • 서브 쿼리 외부로 조인을 이동했습니다. 퍼포먼스에 영향이 있는지는 모르지만 하위 쿼리를 위치 및 분할 된 순위로 가져온 다음 더 큰 쿼리를 모두 가져 오는 행위로 보는 것은 당연한 생각입니다.
  • minor 테이블 이름 별칭을 변경했습니다.비록 내가 앨리어싱에 익숙해졌지만, 그것은 나를 따르기가 더 어려워졌습니다. PostgreSQL에 익숙해 질 때까지 기다릴 것입니다.
+1

테이블 이름 앨리어싱 스타일의 (주로) 점입니다 : 어떤 사람들은 항상 그들을 사용, 어떤 사람들은 그들을 피하십시오. 계산을 내부 쿼리로 이동 한 다음 추가 데이터와 결합하면 "이후에"내게 완전히 이해가됩니다. Explain 출력을 보면 성능에 영향이 있는지를 알 수 있습니다. 이 경우 내 직감은 순위 기능에 의해 암시 된 정렬 동안 적은 데이터를 보유해야하기 때문에 사소한 개선을 제공 할 수 있다는 것입니다. – araqnid

답변

2

PostgreSQL을 8.4+를 들어, analytics like ROW_NUMBER를 사용할 수 있습니다

SELECT x.* 
    FROM (SELECT v.*, 
       t.*, 
       ABS(t.latitude - 30) + ABS(t.longitude - 30) AS distance, 
       ROW_NUMBER() OVER(PARTITION BY v.id 
            ORDER BY ABS(t.latitude - 30) + ABS(t.longitude - 30)) AS rank 
      FROM VENDORS v 
      JOIN LOCATIONS t ON t.vendor_id = v.id 
     WHERE t.latitude IS NOT NULL 
      AND t.longitude IS NOT NULL) x 
    WHERE x.rank = 1 
    AND x.distance < 50 
ORDER BY x.distance 

나는 경우 순위 값이 50 이상이었다 상단이 때문에 공급 업체가 나타나지 않습니다 거리에 필터링을 떠났다. 이런 일이 일어나기를 원하지 않으면 거리 검사가 50 점 미만이되도록 제거하십시오.

ROW_NUMBER는이 예에서 모든 공급 업체에 대해 재설정되는 고유 한 순차 값을 반환합니다. 중복을 원하면 DENSE_RANK를 사용하여 조사해야합니다.

this article for emulating ROW_NUMBER on PostgreSQL pre-8.4을 참조하십시오.

+0

전화해야하는 이유가'RANK()'보다'ROW_NUMBER()'를 선호합니까? 허락하신다면, 어느 쪽도 이해하지 못 하겠지만, 후자는 동일한 결과를 산출하는 것처럼 보이고 최상위 쿼리에서 별칭을 필요로하지 않으므로 편리합니다. –

+0

나는 그것들을 비교하는 [문서의 일부] (http://www.postgresql.org/docs/8.4/interactive/functions-window.html)를 발견했지만, 그 차이를 줄이는데 어려움을 겪고있다. –

+0

내 자신의 질문에 답변 해 드려 죄송 합니다만, 제 목적을 위해'ROW_NUMBER'가 바람직한 이유를 발견했습니다. 2 개의 "위치"는'ROW_NUMBER'에 대해 결코 묶이지 않습니다. 실제로, 거리에있는 넥타이는 매우 드물지만 가장자리 케이스는 테스트 케이스에서 아주 쉽게 튀어 나와 처리해야합니다. 추가 할 사항이 있으면 알려주세요. –

1

MySQL은 GROUP BY를 확장하며 모든 열이 집계 일 필요는 없습니다. http://dev.mysql.com/doc/refman/5.0/en/group-by-hidden-columns.html

동일한 문제로 많은 질문을 보았습니다. 트릭은 하위 쿼리에서 nececssary 열을 얻는 것입니다 그리고 자기가 외부 쿼리에 조인

create temp table locations (id int, vender_id int, latitude int, longitude int); 
CREATE TABLE 
insert into locations values 
     (1, 1, 50, 50), 
     (2, 1, 35, 30), 
     (3, 2, 5, 30) 
; 
SELECT 
    locations.*, distance 
    FROM 
    (
      SELECT 
       vender_id, 
       MIN(ABS(latitude - 30) + ABS(longitude - 30)) as distance 
       FROM locations 
       WHERE latitude IS NOT NULL AND longitude IS NOT NULL 
        GROUP BY vender_id 
    ) AS min_locations 
     JOIN locations ON 
      ABS(latitude - 30) + ABS(longitude - 30) = distance 
      AND min_locations.vender_id = locations.vender_id 
     WHERE distance < 50 
     ORDER BY distance 
; 
id | vender_id | latitude | longitude | distance 
----+-----------+----------+-----------+---------- 
    2 |   1 |  35 |  30 |  5 
    3 |   2 |  5 |  30 |  25 
+0

나는 창조적 인 'ABS (위도 - 30) + ABS (경도 - 30) = 거리'조목에 감명 받았습니다. 당신의 솔루션은 MySQL에 이식 가능하다는 점을 이해합니다. 그래도 하위 쿼리'min_locations'에서 MIN (...)을 담당하는 행의 나머지 부분을 "끌고가는"다른 방법은 없습니까? 나는 이것이 매우 유용한 기능처럼 보일 것이라고 말합니다.그룹화되지 않은, 집합되지 않은 필드 ("평균"집계에서는 의미가 있음)를 표시 할 때 psql의 fussiness를 이해하는 동안 "min"으로 보는 것이 좋을 것입니다. 라인이 흐릿 해지는 것 같아요. –

+0

다른 방식과 비교할 수없는 조인 솔루션의 불행한 결과는 두 위치 (조인 조건에 vendor_id 제약 조건을 포함하면 동일한 공급 업체 아래에 있음)이 같은 거리에있을 때 문제가되는 것입니다. 어떤 제안? –

+0

@Steven Xu : Postgresql은 GROUP BY 표준을 따릅니다. MySQL은 확장이며 그룹화되지 않은 행은 선택 방법에서 '불확정'(문서에 따라)됩니다. 공급 업체 당 하나의 행만 선택되도록하려면 min (id)을 제외한 모든 열이 그룹화 된 외부 쿼리에서 그룹을 사용할 수 있습니다. 비 통상적 인 기능에 관해서는, 특히 오픈 소스의 경우 윈도우 기능이 상당히 새롭습니다. SQL은 70 년대부터 사용되어 왔습니다. 테이블이 어떻게 든 결합되어 있어야합니다 ... :-) –

관련 문제