簡體   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