生成行组合的最快方法

时间:2011-01-19 01:47:28

标签: sql performance optimization postgresql

我们有一个计划表,每个计划都有很多服务。我们希望快速查找不包含重复服务的计划组合,但组合包含某些服务。

e.g。计划表

id | service_1 | service_2 | ...
---------------------------------
1  |   true    |  true     | ...
2  |   true    |  false    | ...
3  |   false   |  true     | ...

e.g。包含service_1和service_2的有效组合

更新

如果有两项服务且我需要两项服务,我们最多可以合并两行(或计​​划),因为它们可以包含至少1项服务。

id | service_1 | service_2 | id | service_1 | service_2 |
---------------------------------------------------------
1  |   true    |  true     |NULL|    NULL   |    NULL   |
2  |   true    |  false    | 3  |    false  |    true   |

更新

目前通过自我左侧加入进行积极的修剪或行。查询是基于服务的数量动态生成的。它创建了有效连接条件的排列,使其不适合发布。

目前,费用按计划数量^服务数量的顺序排列。

我最感兴趣的是解决这个问题的其他方法,不一定是对当前方式的改进。

2 个答案:

答案 0 :(得分:1)

这似乎工作正常

设置数据

DROP TABLE IF EXISTS plan;
CREATE TABLE plan (id int, service1 bool, service2 bool, service3 bool);
INSERT INTO `plan` (`id`, `service1`, `service2`, `service3`) VALUES (1, 1, 0, 0);
INSERT INTO `plan` (`id`, `service1`, `service2`, `service3`) VALUES (2, 0, 1, 0);
INSERT INTO `plan` (`id`, `service1`, `service2`, `service3`) VALUES (3, 1, 1, 1);
INSERT INTO `plan` (`id`, `service1`, `service2`, `service3`) VALUES (4, 1, 0, 1);
INSERT INTO `plan` (`id`, `service1`, `service2`, `service3`) VALUES (5, 0, 0, 1);

查询

select *
from plan A
left join (
    select id, service1, service2, service3 from plan
    union all
    select null, null, null, null) B on B.id > A.id or B.id is null
left join (
    select id, service1, service2, service3 from plan
    union all
    select null, null, null, null) C on C.id > B.id or C.id is null
WHERE (A.service1 + A.service2 + A.service3)
  AND (A.service1 + ifnull(B.service1,0) + ifnull(C.service1,0)) = 1
  AND (A.service2 + ifnull(B.service2,0) + ifnull(C.service2,0)) = 1
  AND (A.service3 + ifnull(B.service3,0) + ifnull(C.service3,0)) = 1

结果

id | service1 | service2 | service3 | id | service1 | service2 | service3 | id | service1 | service2 | service3
1 | 1 | 0 | 0 | 2 | 0 | 1 | 0 | 5 | 0 | 0 | 1
1 | 1 | 0 | 0 | 5 | 0 | 0 | 1 | 2 | 0 | 1 | 0
2 | 0 | 1 | 0 | 4 | 1 | 0 | 1 | NULL | NULL | NULL | NULL
2 | 0 | 1 | 0 | NULL | NULL | NULL | NULL | 4 | 1 | 0 | 1
3 | 1 | 1 | 1 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL

答案 1 :(得分:1)

正如我在评论中提到的那样,确定“重复”的规则尚不清楚。但是,从它的声音来看,你只是做一个按位AND。

With RawData As
    (
    Select 1 As id, 1 As service_1, 1 As service_2
    Union All Select 2, 1, 0
    Union All Select 3, 0, 1
    )
    , BinData As
    (
    Select A.id, A.service_1, A.service_2
        , A.service_1 * 2 + A.service_2 As Bin
    From RawData As A
    )
Select *
From BinData As F1
    Left Join BinData As F2
        On F2.id <> F1.id
            And F1.Bin & F2.Bin = 0
Order By F1.id

但是,您会在此解决方案中注意到我为id = 3获取了一行。出于同样的原因,id = 3是id = 2的“重复”,反之亦然。

如果这不正确,我们需要更多的清晰度和一些更好的样本数据,以说明什么是“重复”和不是“重复”的边缘情况。

<强>更新

鉴于cyberwiki在评论中所说的内容,如果每个计划正在寻求的是另一个计划,当合并时提供所有服务一次,那么正在寻求的是一个二元的赞美,它将产生所有的1。我们可以通过找到当前计划的异或产生所有计划的所有计划来做到这一点:

With RawData As
    (
    Select 1 As id, 1 As service_1, 1 As service_2
    Union All Select 2, 1, 0
    Union All Select 3, 0, 1
    )
    , BinData As
    (
    Select A.id, A.service_1, A.service_2
        , A.service_1 * 2 + A.service_2 As Bin
    From RawData As A
    )
Select *, F1.Bin ^ F2.Bin
From BinData As F1
    Left Join BinData As F2
        On F2.id <> F1.id
            And F1.Bin ^ F2.Bin = 3
Order By F1.id

再次注意,id = 3将显示在结果中,因为id = 3与id = 2完全匹配,反之亦然。