繁体   English   中英

在没有 ROW_NUMBER 的多列中选择具有最小值的行

[英]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;

但是,我建议您为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;

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

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

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM