簡體   English   中英

具有多個表連接的每個組的前N個

[英]Top N Per Group with Multiple Table Joins

根據我的研究,這是一個非常普遍的問題,通常有一個相當簡單的解決方案。 我的任務是改變幾個查詢, 將所有結果變為每組前三名 起初這很順利,我使用了幾個建議和本網站的答案來實現這一目標(觀看次數最多的產品)。 然而,由於多次加入,我的最后一個“暢銷產品”遇到了困難。

基本上,我需要按每個產品的最高銷售額排序所有產品,其中每個供應商的最大產品數為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個產品(#sold)。 我到目前為止添加了我所擁有的內容,但我很尷尬,這個問題已經是一面文字了。 我已經嘗試過變量,但一直收到無效的結果。 任何幫助將不勝感激。

即使您指定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

我喜歡這個優雅的解決方案,但是當我在我的開發機器上運行一個改編但相似的查詢時,我得到一個非確定性的結果集返回。 我相信這是由於MySql優化器處理在同一語句中分配和讀取用戶變量的方式。

來自文檔

作為一般規則,您不應該為用戶變量賦值並在同一語句中讀取值。 您可能會得到您期望的結果,但這不能保證。 涉及用戶變量的表達式的評估順序是未定義的,可能會根據給定語句中包含的元素進行更改; 此外,MySQL服務器版本之間的訂單不保證相同。

只是在這里添加這個注釋,以防其他人遇到這種奇怪的行為。

@RichardTheKiwi給出的答案非常好,讓我99%的路程! 我正在使用MySQL,並且只獲得每個組的第一行標記行號,而其余行保持為NULL。 這導致查詢僅返回每個組的最高命中而不是前三行。 為了解決這個問題,我必須初始化@rinitvars子查詢。 我變了,

from (select @g:=null) initvars

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

您也可以將@r初始化為0,它的工作原理相同。 對於那些不太熟悉這種語法的人,附加部分是讀取每個已排序的組,如果一行與上一行具有相同的vendorid ,並使用@g變量進行跟蹤,則會增加行號,即存儲在變量@r 當這個過程達到一個新的下一組vendorid ,則IF語句將不再評估為真和@r變量(並由此RowNum )將被重置為1。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM