根据两对组合的重量找到最接近的数字

时间:2012-10-17 09:41:10

标签: sql sql-server sql-server-2008 tsql

这是我上一个问题的延续What is the efficient way to generate combination of items in SQL Server?让我解释一下我正在寻找的真实场景以及为什么......

假设我有一个

下的表格
Declare @t table(Number Int)
Insert Into @t Values(10),(20),(30),(40),(18)

Number
10
20
30
40
18

我需要找一个35(或最近)的数字。

Declare @NumberToLookfor = 35

现在搜索将根据两对组合的重量进行。让我解释一下。

10+20 = 30

10+30 = 40

10+40 = 50

10+18 = 28

20 + 30 = 50

20 + 40 = 60

20 + 18 = 38

30 + 40 = 70

30 + 18 = 48

40 + 18 = 58

所以我们可以弄清楚任何两个数字的权重在这里是候选者,例如(10,20),(10,30)......(40,18)

一旦我们得到了,在这种情况下,前3个最接近的对将是(20,18),(10,20),(10,30)。因为35到38(20 + 18)之间的污垢是3,其他对(10,20),(10,30)是5。

我认为解释清楚明白我在寻找什么。(如果不是,请告诉我)

最有效的方式是什么?

我的尝试

Declare @t table(Number Int)
Insert Into @t Values(10),(20),(30),(40),(18)

;WITH Cte1 (Number,Ids,TotalWeight) AS 
( 
    SELECT  Number           
            , ',' + CAST(Number AS VARCHAR(MAX)) 
            ,CAST(Number AS INT) 
    FROM @t 
    UNION ALL 
    SELECT  p.Number 
            ,c.Ids + ',' +  CAST(p.Number AS VARCHAR(MAX)) 
            ,CAST(c.TotalWeight + p.Number AS INT)            
    FROM @t AS p JOIN Cte1 c ON p.Number < c.Number
),Cte2 AS( 
    SELECT          
        *
        ,DENSE_RANK() OVER(ORDER BY ABS(TotalWeight - 35)) [rank] 
    FROM Cte1 
    WHERE (LEN(Ids) - LEN(REPLACE(Ids, ',', '')))/LEN(',') = 2 
)

select *
from Cte2 where [rank] <= 2

有效。

enter image description here

但是如果价值增长很大,说超过50左右,那么它变得非常有效。因为在第一次CTE中,我发现完全排列,在第二次Cte中选择那些只有两个元素参与的值。

因此,当值变大时,第一个Cte表现得非常缓慢。

即使是大桌子,还有其他方法吗?

提供DDL

Declare @t table(Number Int)
Insert Into @t Values  
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20), 
(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),(40), 
(41),(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60), 
(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71),(72),(73),(74),(75),(76),(77),(78),(79),(80), 
(81),(82),(83),(84),(85),(86),(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100) 

非常感谢提前

2 个答案:

答案 0 :(得分:1)

稍微不同的结果集,但如果您关心的只是处理对,那么应该效率更高:

Declare @t table(Number Int)
Insert Into @t Values(10),(20),(30),(40),(18)

;WITH Pairs AS 
( 
    SELECT  t1.Number as p1,t2.Number as p2,t1.Number + t2.Number as TotalWeight
    FROM
        @t t1
            inner join
        @t t2
            on
                t1.Number < t2.Number
),Cte2 AS( 
    SELECT          
        *
        ,DENSE_RANK() OVER(ORDER BY ABS(TotalWeight - 35)) [rank] 
    FROM Pairs 
)

select *
from Cte2 where [rank] <= 2

结果:

p1          p2          TotalWeight rank
----------- ----------- ----------- --------------------
18          20          38          1
10          30          40          2
10          20          30          2

答案 1 :(得分:1)

对于非常小的集合,使用强力方法一般求解对,三元组等,这可能有效。更新两个指定位置的数字。

Declare @t table(Number Int)
Insert Into @t Values(10),(20),(30),(40),(18)

;WITH Cte1 (Counter,Number,Ids,TotalWeight) AS 
( 
    SELECT  1,Number           
            , ',' + CAST(Number AS VARCHAR(MAX)) 
            ,CAST(Number AS INT) 
    FROM @t 
    UNION ALL 
    SELECT  c.Counter+1,p.Number 
            ,c.Ids + ',' +  CAST(p.Number AS VARCHAR(MAX)) 
            ,CAST(c.TotalWeight + p.Number AS INT)            
    FROM @t AS p JOIN Cte1 c ON p.Number < c.Number
    WHERE c.Counter < 2   --<<** we need only up to 2 numbers
),Cte2 AS( 
    SELECT          
        *
        ,DENSE_RANK() OVER(ORDER BY ABS(TotalWeight - 35)) [rank] 
    FROM Cte1 
    WHERE Counter = 2   --<<** use only the pairs
)

select *
from Cte2 where [rank] <= 2