Самый быстрый способ генерировать комбинации строк - PullRequest
3 голосов
/ 19 января 2011

У нас есть таблица планов, и у каждого плана есть много услуг. Нам нужен быстрый способ нахождения комбинаций планов, которые не содержат дублирующих сервисов, но как комбинации содержат определенные сервисы.

например. таблица планов

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

например. допустимые комбинации, содержащие service_1 и service_2

UPDATE

Если бы было 2 службы, и мне потребовались обе из них, мы бы объединили до 2 строк (или планов), поскольку они могли бы содержать как минимум 1 услугу в каждой.

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

UPDATE

В настоящее время он работает самостоятельно, присоединяясь к самому себе с агрессивной обрезкой или строками. Запрос генерируется динамически в зависимости от количества сервисов. Он создает сочетания действительных условий соединения, что делает его непрактичным для публикации.

В настоящее время стоимость указана в порядке количества планов ^ количество услуг.

Меня больше всего интересуют другие способы решения этой проблемы, не обязательно улучшающие текущий способ.

Ответы [ 2 ]

1 голос
/ 19 января 2011

Как я уже упоминал в комментариях, правило, по которому определяется «дубликат», неясно. Тем не менее, по звукам, вы просто делаете побитовое И.

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, верно и обратное.

Если это не правильно, нам нужно гораздо больше ясности и несколько лучших выборочных данных, которые иллюстрируют крайние случаи того, что является и не является «дубликатом».

Обновление

Учитывая то, что кибервики указали в комментариях, если то, что ищется для каждого плана, - это другой план, который при объединении предоставляет все услуги ровно один раз, то то, что запрашивается, - это комплимент в двоичном виде, который произведет все 1. Мы можем сделать это, найдя все планы, которые, когда XOR будет соответствовать текущему плану, произведут все:

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, обратное также верно.

1 голос
/ 19 января 2011

Кажется, все в порядке

Данные настройки

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...