【问题标题】:SQL query for all possible combinations from tableSQL查询表中所有可能的组合
【发布时间】:2019-05-13 14:37:27
【问题描述】:

我有一个表,它是 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
  • A+B+D的组合给出和PAR2的结果(0.5+1.5+3.0=5.0),但是PAR1的和:110+105+130=345
  • A+B+E的组合给出和PAR2的结果(0.5+1.5+5.5=7.5),但是PAR1的和:110+105+115=330
  • A+B+F的组合给出和PAR2的结果(0.5+1.5+6.5=8.5),但是PAR1的和:110+105+130=345
  • 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,也特别感谢示例和解释,这样你让我快速理解你的想法 - 这是大师级;)

【问题讨论】:

  • 什么类型的SQL数据库? SQL 服务器?甲骨文? MySQL?还有什么?
  • 这是一个 MS SQL Server
  • 谢谢。我添加了一个合适的标签。在询问 SQL 问题时,您需要始终这样做,否则人们无法提出解决方案,以防它涉及特定于您的 SQL 版本的函数或关键字。并非所有数据库引擎都是平等的......
  • 谢谢。我是 stackoverflow 的新手。
  • 自加入表两次,应用条件,找到最大值。

标签: sql sql-server


【解决方案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

这是最小值

【讨论】:

  • forpas,非常感谢!这似乎是一个非常聪明的解决方案。让我在数据库中做一些测试,我会回复你。是的,在这种情况下,5.5 是最好的结果,我也会在其他表上尝试。
  • @jeremy5 你是否接受了一个比直接连接效率低的交叉连接的答案?
  • 是的,我误以为也可以标记两个或多个。你的解决方案是最好的选择,你帮了我很多,谢谢;)我应该把下一个问题放到关于 tempTable 的新主题中还是继续在这里讨论?
  • 如果涉及到新需求,那一定是新问题
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    您可能会尝试将表与自身交叉连接 3 次。通过这种方式,您可以将三行的所有组合以单行为轴,从而使您能够应用所需的条件并选择最大值。

    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 子句,包括 Serg 的建议

    【讨论】:

    • 可能where t1.ID &lt; t2.ID and t2.ID &lt; t3.ID 排除排列,如果它们被认为是相同的解决方案。
    • 感谢您的回答。这也适用,类似于 forpas 命题。
    猜你喜欢
    • 1970-01-01
    • 2011-05-20
    • 2015-06-21
    • 2013-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2015-11-10
    相关资源
    最近更新 更多