我想获得每组的行,其中最小值为两列。
我有一张桌子,上面有我想要的物品,以及它们的成本和我的距离。
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
保留最左边的10个字符,表示为整数个便士,并用前导零填充,dist
类似于10位整数。
如果你有一个项目表,那么这可能有效:
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
你可以这样做
; 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;
也许有更好的方法,但这应该有效。