选择按(分析)总和构建组

时间:2013-12-16 18:33:31

标签: sql oracle select

请帮我构建一个sql select来分配(软件开发)任务到软件版本。实际上,这是解决我的实际业务特定问题的一个虚拟示例。

我有一个关系任务:

ID Effort_In_Days
3  3
1  2
6  2
2  1
4  1
5  1

我想将任务分发给最多2天的版本(超过2的任务仍然会被放入一个版本中)。在我真正的问题中,我有更多的“日子”可以分发“任务”。预期产出:

Release Task_ID
1       3
2       1
3       6
4       2
4       4
5       5

我认为我需要使用分析函数,使用sum(effort_in_days) over等来获取结果。但是我没有太多使用分析函数,也没有找到一个与我的具体问题足够接近的例子。如果达到总和(> = 2),我需要构建组(发布)。

3 个答案:

答案 0 :(得分:2)

这是一个bin-packing问题的例子(见here)。除了在某些边界情况下,我所知道的SQL中没有最佳解决方案。例如,如果所有任务具有相同的长度或者所有任务都是> = 2,那么就有一个易于找到的最佳解决方案。

贪婪算法效果很好。这是将给定记录放在它适合的第一个bin中,可能按照大小顺序递减列表。

如果您的问题确实如您所述,那么贪婪算法将会产生最佳解决方案。也就是说,如果最大值是2并且努力是整数。在这种情况下,甚至可能有一种方法可以在SQL中计算解决方案。

否则,您将需要pl / sql代码来实现近似解决方案。

答案 1 :(得分:2)

我会做类似的事情:

with data as (
    select 3 ID, 3 Effort_In_Days from dual union all
    select 1 ID, 2 Effort_In_Days from dual union all
    select 6 ID, 2 Effort_In_Days from dual union all
    select 2 ID, 1 Effort_In_Days from dual union all
    select 4 ID, 1 Effort_In_Days from dual union all
    select 5 ID, 1 Effort_In_Days from dual
) 
select id, effort_in_days, tmp, ceil(tmp/2) release
from (
    select id, effort_in_days, sum(least(effort_in_days, 2)) over (order by effort_in_days desc rows unbounded preceding) tmp
    from data
);

结果是:

        ID EFFORT_IN_DAYS        TMP    RELEASE
---------- -------------- ---------- ----------
         3              3          2          1
         1              2          4          2
         6              2          6          3
         2              1          7          4
         4              1          8          4
         5              1          9          5

基本上,我使用least()将2以上的所有内容转换为2.然后我将所有行按该值按降序排列并开始分配版本。由于它们按降序排列,最大值为2,因此我知道每次达到2的倍数时我都需要指定一个新版本。

请注意,如果您有小数值,则最终可能会发布未分配完整2天的版本(而不是分配超过2天),这可能会也可能不会满足您的需求。

另请注意,我只显示输出中的所有列,以便更容易查看代码实际执行的操作。

答案 2 :(得分:0)

SQL Fiddle

Oracle 11g R2架构设置

CREATE TABLE data AS
    select 3 ID, 3 Effort_In_Days from dual union all
    select 1 ID, 2 Effort_In_Days from dual union all
    select 6 ID, 2 Effort_In_Days from dual union all
    select 2 ID, 1 Effort_In_Days from dual union all
    select 4 ID, 1 Effort_In_Days from dual union all
    select 5 ID, 1 Effort_In_Days from dual union all
    select 9 ID, 2 Effort_In_Days from dual union all
    select 7 ID, 1 Effort_In_Days from dual union all
    select 8 ID, 1 Effort_In_Days from dual;

查询1

  1. 为行提供一个索引,以便它们可以轻松保存;
  2. 将组分配给Effort_In_Days1的行,以便Effort_In_Days 1 Effort_In_Days的所有相邻行位于相同的组中,行之间的值由较高的值分隔Effort_In_Days属于不同的群组;
  3. Effort_In_Days大于1或WITH indexes AS ( SELECT ID, Effort_In_Days, ROWNUM AS idx FROM Data ), groups AS ( SELECT ID, Effort_In_Days, idx, CASE Effort_In_Days WHEN 1 THEN idx - ROW_NUMBER() OVER ( PARTITION BY Effort_In_Days ORDER BY idx ) END AS grp FROM indexes ORDER BY idx ), costs AS ( SELECT ID, Effort_In_Days, idx, CASE Effort_In_Days WHEN 1 THEN MOD( ROW_NUMBER() OVER ( PARTITION BY grp ORDER BY idx ), 2 ) ELSE 1 END AS cost FROM groups ORDER BY idx ) SELECT ID, Effort_In_Days, SUM( cost ) OVER ( ORDER BY idx ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS Release FROM costs ORDER BY idx 为1且行内有奇数行的每一行分配成本1;然后
  4. 最后,版本是行和所有前面行的所有成本的总和。
  5. 像这样:

    | ID | EFFORT_IN_DAYS | RELEASE |
    |----|----------------|---------|
    |  3 |              3 |       1 |
    |  1 |              2 |       2 |
    |  6 |              2 |       3 |
    |  2 |              1 |       4 |
    |  4 |              1 |       4 |
    |  5 |              1 |       5 |
    |  9 |              2 |       6 |
    |  7 |              1 |       7 |
    |  8 |              1 |       7 |
    

    <强> Results

    {{1}}