[英]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;
這里的方法是
我喜歡這個優雅的解決方案,但是當我在我的開發機器上運行一個改編但相似的查詢時,我得到一個非確定性的結果集返回。 我相信這是由於MySql優化器處理在同一語句中分配和讀取用戶變量的方式。
來自文檔 :
作為一般規則,您不應該為用戶變量賦值並在同一語句中讀取值。 您可能會得到您期望的結果,但這不能保證。 涉及用戶變量的表達式的評估順序是未定義的,可能會根據給定語句中包含的元素進行更改; 此外,MySQL服務器版本之間的訂單不保證相同。
只是在這里添加這個注釋,以防其他人遇到這種奇怪的行為。
@RichardTheKiwi給出的答案非常好,讓我99%的路程! 我正在使用MySQL,並且只獲得每個組的第一行標記行號,而其余行保持為NULL。 這導致查詢僅返回每個組的最高命中而不是前三行。 為了解決這個問題,我必須初始化@r
在initvars
子查詢。 我變了,
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.