2011-03-16 2 views
7

내 연구를 기반으로하면 일반적으로 매우 간단한 해결책이있는 매우 일반적인 문제입니다. 내 작업은 여러 쿼리를 에서 변경하는 것입니다. 모든 결과를에 입력하면 이 그룹당 최고 3 개가됩니다.. 처음에는 이것이 잘 진행되고 있었고이 사이트에서 여러 가지 추천 및 답변을 사용하여 가장 많이 본 제품을 만들었습니다. 그러나 여러 개의 조인 때문에 지난 번 "베스트셀러 제품"에 어려움이 있습니다.상위 N 여러 테이블 조인을 사용하는 그룹 당

기본적으로 나는 을 주문해야합니다. # 당 공급 업체 당 최대 제품 수는 3 개입니다. # 원본 쿼리를 만들기 위해 여러 테이블이 결합되어 있으며, 시도 할 때마다 변수를 사용하여 순위를 생성하면 잘못된 결과가 생성됩니다.

제품 표

productid | vendorid | approved | active | deleted 

공급 업체 표

vendorid | approved | active | deleted 

주문 표

orderid | `status` | deleted 

: 문제를 더 잘 이해하는 데 도움합니다 다음은 (나는 간결 불필요한 필드를 제거했습니다) 주문 항목 표

다음과 같이
orderitemid | orderid | productid | price 

지금, 에 내 원래의 쿼리가이 모든 결과를 얻을 : 마지막으로

SELECT COUNT(oi.price) AS `NumSales`, 
     p.productid, 
     p.vendorid 
FROM products p 
INNER JOIN vendors v ON (p.vendorid = v.vendorid) 
INNER JOIN orders_items oi ON (p.productid = oi.productid) 
INNER JOIN orders o ON (oi.orderid = o.orderid) 
WHERE (p.Approved = 1 AND p.Active = 1 AND p.Deleted = 0) 
AND (v.Approved = 1 AND v.Active = 1 AND v.Deleted = 0) 
AND o.`Status` = 'SETTLED' 
AND o.Deleted = 0 
GROUP BY oi.productid 
ORDER BY COUNT(oi.price) DESC 
LIMIT 100; 

(그리고 여기에 내가 난처한 상황에 빠진거야 곳이다), 그와 같은 위의 문을 변경하기 위해 노력하고있어 벤더 당 상위 3 개 제품 (# 판매 기준) 만 받았습니다. 나는 지금까지 가지고있는 것을 더하고 싶지만 그렇게하기에는 당혹 스럽다. 그리고이 질문은 이미 텍스트의 벽이다. 변수를 시도했지만 잘못된 결과가 계속 나타납니다. 어떤 도움이라도 대단히 감사하겠습니다.

답변

10

LIMIT 100을 지정해도이 유형의 쿼리에는 전체 검색 및 테이블 작성이 필요하며 표시 할 모든 레코드를 검사하고 행 번호를 매기고 마지막으로 필터링 할 값을 100으로 지정해야합니다.

select 
    vendorid, productid, NumSales 
from 
(
    select 
     vendorid, productid, NumSales, 
     @r := IF(@g=vendorid,@r+1,1) RowNum, 
     @g := vendorid 
    from (select @g:=null) initvars 
    CROSS JOIN 
    (
     SELECT COUNT(oi.price) AS NumSales, 
       p.productid, 
       p.vendorid 
     FROM products p 
     INNER JOIN vendors v ON (p.vendorid = v.vendorid) 
     INNER JOIN orders_items oi ON (p.productid = oi.productid) 
     INNER JOIN orders o ON (oi.orderid = o.orderid) 
     WHERE (p.Approved = 1 AND p.Active = 1 AND p.Deleted = 0) 
     AND (v.Approved = 1 AND v.Active = 1 AND v.Deleted = 0) 
     AND o.`Status` = 'SETTLED' 
     AND o.Deleted = 0 
     GROUP BY p.vendorid, p.productid 
     ORDER BY p.vendorid, NumSales DESC 
    ) T 
) U 
WHERE RowNum <= 3 
ORDER BY NumSales DESC 
LIMIT 100; 

접근 방식은 여기

  1. 그룹 NumSales에게 공급 업체/제품 별 판매는
  2. 필터 번호 데이터 세트 3의 최대 허용 할 수있는 행
  3. 사용 변수를 얻을 것입니다 공급 업체별
  4. NumSales DESC로 나머지 주문 및 반환 100
+0

로 재설정됩니다, 감사합니다! CROSS JOIN 성명서는 필자의 시도에서 빠진 핵심 요소였습니다. 필자의 견해로는 매우 유용한 쿼리이며, 꽤 많이 사용할 계획입니다. – Jeremy

+0

@jcargilo - FYI 내부 주문을 수정했습니다. vendorid + NumSales 만 가능합니다! – RichardTheKiwi

+0

우수합니다. 감사합니다. – Jeremy

0

필자는이 우아한 솔루션을 좋아하지만, 내 dev 컴퓨터에서 적응하지만 유사한 쿼리를 실행하면 비 결정적 결과 집합이 반환됩니다. 이것은 MySql Optimizer가 동일한 문 내에서 사용자 변수를 할당하고 읽는 방법을 다루는 것으로 판단됩니다.

the docs에서 :

일반적으로, 당신은 사용자 변수에 값을 할당하지 않고 동일한 명령문 내에서 값을 읽어 안됩니다. 기대하는 결과를 얻을 수도 있지만 이것이 보장되는 것은 아닙니다. 사용자 변수가 포함 된 표현식에 대한 평가 순서는 정의되지 않았으며 주어진 구문에 포함 된 요소에 따라 변경 될 수 있습니다. 또한이 순서는 MySQL 서버 릴리스간에 동일하지 않을 수도 있습니다.

다른 사람이이 이상한 행동을하는 경우에 대비하여 여기에이 메모를 추가하십시오.

0

@RichardTheKiwi가 제공 한 답변은 훌륭하게 작동했으며 99 %의 도움을 받았습니다. 나는 MySQL을 사용하고 행 번호가 표시된 각 그룹의 첫 번째 행만 가져오고 나머지 행은 NULL로 남았습니다. 이로 인해 쿼리는 처음 세 행이 아닌 각 그룹에 대해 최상위 히트 만 반환합니다. 이 문제를 해결하려면 initvars 하위 쿼리에서 @r을 초기화해야했습니다. 나는 또한 0-@r를 초기화 할 수 있으며,이 같은 일을 할

from (select @g:=null) initvars

from (select @g:=null, @r:=null) initvars

에 변경. 그리고 이런 유형의 구문에 익숙하지 않은 경우, 추가 섹션은 각 정렬 된 그룹을 통해 읽습니다. 행에 변수로 추적되는 이전 행과 동일한 vendorid이있는 경우, 행 번호가 증가되어 저장됩니다 변수 @r에 이 과정이 새로운 vendorid와 다음 그룹에 도달하면, IF 문이 더 이상 참으로 평가되지 않으며 (따라서 및 RowNum)에 @r 변수가 1

화려한 리처드의
관련 문제