在没有ROW_NUMBER的情况下选择多列中值最小的行

问题描述 投票:1回答:4

我想获得每组的行,其中最小值为两列。

我有一张桌子,上面有我想要的物品,以及它们的成本和我的距离。

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

sql sql-server tsql sql-server-2012 greatest-n-per-group
4个回答
1
投票

关于这个主题的一篇很好的文章是由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位整数。


0
投票

如果你有一个项目表,那么这可能有效:

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)的索引。


0
投票

这样的事情应该有效:

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

0
投票

你可以这样做

; 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;

但是,我建议您添加索引到itemcost列以快速查询。

[编辑]重新阅读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;

也许有更好的方法,但这应该有效。

© www.soinside.com 2019 - 2024. All rights reserved.