[英]Select row with least value in multiple columns without ROW_NUMBER
我想用兩列的最小值獲取每組的行。
我有一張桌子,上面列出了我想要的物品,以及它們的成本和與我的距離。
mytable:
item | cost | dist
-----+------+---------
1 | $2 | 1.0
1 | $3 | 0.5
1 | $4 | 2.0
2 | $2 | 2.0
2 | $2 | 1.5
2 | $2 | 4.0
2 | $8 | 1.0
2 | $12 | 3.0
3 | $1 | 5.0
對於每個項目,我想獲取具有最小成本的行,然后如果有多個最小成本,則獲取具有最小分布的那個
所以我的結果是
item | cost | dist
-----+------+---------
1 | $2 | 1.0
2 | $2 | 1.5
3 | $1 | 5.0
我知道我可以使用
SELECT *
, ROW_NUMBER() OVER(PARTITION BY item ORDER BY cost ASC, dist ASC) as [RID]
FROM mytable
WHERE [RID] = 1
但是當我有 100,000 個項目和 100,000 個列表時,問題就出現了,並且對整個表格進行排序變得非常耗時。
由於我只需要每個組的前 1 個,我想知道是否有另一種方法可以獲得我想要的結果,而無需對 10,000,000,000 個條目的整個表進行排序。
當前使用 SQL Server 2012
Itzik Ben Gan - Optimizing TOP N Per Group Queries撰寫了有關此主題的一篇不錯的文章。 這討論了串聯方法。
例如,如果您的桌子是
CREATE TABLE #YourTable
(
item INT,
cost MONEY CHECK (cost >= 0),
dist DECIMAL(10, 2) CHECK (dist >= 0)
)
你可能會用
WITH T AS
(
SELECT item,
MIN(FORMAT(CAST(cost * 100 AS INT), 'D10') + FORMAT(CAST(dist * 100 AS INT), 'D10')) AS MinConcat
FROM #YourTable
GROUP BY item
)
SELECT item,
CAST(LEFT(MinConcat,10)/100.0 AS MONEY),
CAST(RIGHT(MinConcat,10)/100.0 AS DECIMAL(10,2))
FROM T
所以這可以在id
上的單個分組操作中完成(它可以是沒有任何排序的散列聚合)。
您需要注意連接結果的值在作為cost, dist
處理的字符串時具有相同的順序cost, dist
當作為原始列值處理時cost, dist
將具有相同的順序,因此如果您的數據類型不同,上面的查詢可能需要調整。
它目前保留最左邊的 10 個字符作為cost
表示為便士整數並用前導零填充,類似地將dist
為 10 位整數。
你可以這樣做
; with c as
(select min(cost) as cost, item
from mytable
group by item)
select t.* from mytable t
inner join c
on c.item = t.item and c.cost=t.cost;
但是,我建議您為item
和cost
列添加索引以加快查詢速度。
[編輯]重新閱讀OP問題后,當成本有關系時,它應該如下所示,
; with c as
(select min(cost) as cost, item
from mytable
group by item)
, c2 as (
select t.cost, t.item, min(dist) as dist from mytable t
inner join c
on c.item = t.item and c.cost=t.cost
group by t.cost, t.item)
select t.item,t.cost, c2.dist from mytable t
inner join c2
on c2.item = t.item, and c2.cost = t.cost;
也許有更好的方法,但這應該有效。
如果您有一個項目表,那么這可能有效:
select i.*, t.*
from items i cross apply
(select top (1) t.*
from t
where t.item = i.item
order by cost, dist
) t;
為了使其高效,您需要(item, cost, dist)
上的索引。
這樣的事情應該工作:
select
t.item, MIN(t.cost) as mincost, min(t2.mindist) as mindist
from mytable t
inner join (
select item, cost, MIN(dist) as mindist
from mytable
group by
item, cost
) t2 on t.item = t2.item
group by t.item,t2.cost
having MIN(t.cost) = t2.cost
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.