SQL查询表中所有可能的组合

时间:2019-05-13 14:37:27

标签: sql sql-server

根据SQL数据库的一些计算,我有一张表,它看起来像这样:

[ID] [PAR1] [PAR2]
[A]   [110]  [0.5]
[B]   [105]  [1.5]
[C]   [120]  [2.0]
[D]   [130]  [3.0]
[E]   [115]  [5.5]
[F]   [130]  [6.5]
[G]   [120]  [7.0]
[H]   [110]  [7.5]
[I]   [105]  [8.0]
[J]   [120]  [9.0]
[K]   [110]  [9.5]

按PAR2排序-更少意味着更好的结果。 我需要从3行中找到 SUM PAR2的最佳结果,其中PAR1的总和至少为350(至少为350)。例如:

  • A + B + C的组合给出的总和PAR2的最佳结果(0.5 + 1.5 + 2.0 = 4.0),但是PAR1的总和:110 + 105 + 120 = 335 <(350 )-条件不正确,无法使用结果,
  • A + B + D的组合得出的总和为PAR2(0.5 + 1.5 + 3.0 = 5.0),但PAR1的总和为:110 + 105 + 130 = 345 <(350)-条件不好,不能使用结果
  • A + B + E的组合得出的总和为PAR2(0.5 + 1.5 + 5.5 = 7.5),但PAR1的总和为:110 + 105 + 115 = 330 <(350)-条件不好,不能使用结果
  • A + B + F的组合得出的总和为PAR2(0.5 + 1.5 + 6.5 = 8.5),但PAR1的总和为:110 + 105 + 130 = 345 <(350)-条件不好,不能使用结果 (...)
  • B + C + D的组合得出结果PAR2之和(1.5 + 2.0 + 3.0 = 6.5)和PAR1之和:105 + 120 + 130 = 355>(350)- 条件还可以!,所以我们有一个最佳成绩为6.5的获胜者

这是一个ASP.NET应用程序,因此我尝试从数据库中获取表并在后面使用VB代码来获取结果,但这是使用FOR..NEXT LOOP的“手动”工作,需要花费一些时间。因此,对于这样的计算而言,这不是一个很好的选择,而且也太慢了。

我想知道是否有更好的平滑智能SQL代码可以直接从SQL Query获取结果。也许一些高级数学功能?有什么想法吗?

预先感谢。


我使用forpas解决方案进行了一些测试,是的,它的效果非常好。但是当我添加很多WHERE条件时需要花费很多时间,因为原始表非常大。因此,我将尝试找到在函数(而非过程)中使用临时表的解决方案。谢谢大家的答案。

forpas,特别感谢您提供示例和解释,通过这种方式,您可以让我快速地了解您的想法-这是大师级别;)

3 个答案:

答案 0 :(得分:1)

您可以像这样使用双重内部自联接:

select top 1 * from tablename t1
inner join tablename t2 on t2.id > t1.id
inner join tablename t3 on t3.id > t2.id
where t1.par1 + t2.par1 + t3.par1 >= 350
order by t1.par2 + t2.par2 + t3.par2

请参见demo
结果:

> ID | PAR1 | PAR2 | ID | PAR1 | PAR2 | ID | PAR1 | PAR2
> :- | ---: | :--- | :- | ---: | :--- | :- | ---: | :---
> A  |  110 | 0.5  | C  |  120 | 2.0  | D  |  130 | 3.0 

所以获胜者是 A + C + D ,因为:

110 + 120 + 130 = 360 >= 350 

并且PAR2的总和是

0.5 + 2.0 + 3.0 = 5.5

这是最小值

答案 1 :(得分:0)

检查此。我觉得它准确或接近您的要求-

WITH CTE (ID,PAR1,PAR2)
AS
(
    SELECT 'A',110,0.5  UNION ALL
    SELECT 'B',105,1.5  UNION ALL
    SELECT 'C',120,2.0  UNION ALL
    SELECT 'D',130,3.0  UNION ALL
    SELECT 'E',115,5.5  UNION ALL
    SELECT 'F',130,6.5  UNION ALL
    SELECT 'G',120,7.0  UNION ALL
    SELECT 'H',110,7.5  UNION ALL
    SELECT 'I',105,8.0  UNION ALL
    SELECT 'J',120,9.0  UNION ALL
    SELECT 'K',110,9.5
)
SELECT B.AID,B.BID,B.CID,SUM_P2,SUM_P1
(
    SELECT * , ROW_NUMBER() OVER (PARTITION BY CHAR_SUM ORDER BY CHAR_SUM) CS
    FROM 
    (
        SELECT ASCII(A.ID) + ASCII(B.ID)+ASCII(C.ID) CHAR_SUM,
        A.ID AID,B.ID BID,C.ID CID,
        (A.PAR2+B.PAR2+C.PAR2) AS SUM_P2,
        (A.PAR1+B.PAR1+C.PAR1) AS SUM_P1
        FROM CTE A
        CROSS APPLY CTE B
        CROSS APPLY CTE C
        WHERE A.ID <> B.ID AND A.ID <> C.ID AND B.ID <> C.ID
        AND (A.PAR1+B.PAR1+C.PAR1) >= 350
    ) A
)B
WHERE CS = 1

答案 2 :(得分:0)

您可以尝试将表自身交叉连接三遍。这样,您就可以将三行的所有组合都旋转到单行上,从而使您能够应用所需的条件并选择最大值。

select  t1.ID, t2.ID, t3.ID, t1.PAR2 + t2.PAR2 + t3.PAR2
from    yourTable t1
cross join
        yourTable t2
cross join
        yourTable t3
where   t1.ID < t2.ID and t2.ID < t3.ID and
        t1.PAR1 + t2.PAR1 + t3.PAR1 >= 350
order by t1.PAR2 + t2.PAR2 + t3.PAR2 ASC

虽然此解决方案在技术上应该可行,但是交叉连接表并不是理想的性能,甚至多次执行时也是如此。如果表的大小将随着时间增长,并且您可以选择在代码级应用计算,我认为这样做是明智的。

修改

更改了where子句,包括塞尔格的建议