Если вы разрешите изменить способ хранения определения групп go в кампании, тогда возможно следующее решение:
- Кампании определяются набором групп.
- Группы содержат идентификаторы или названия продуктов.
- Любая группа может рассматриваться как группа
OR
, что означает, что для соответствия этой группе необходимо купить хотя бы один продукт из этой группы. Кроме того, группы могут быть AND
группами, что означает все продуктов, которые необходимо купить для соответствия группе.
Это может выглядеть так:
create column table camp_grps
(camp NVARCHAR(20) not null
, grp integer not null
, grp_type NVARCHAR(3) not null
, product NVARCHAR(20) not null);
insert into camp_grps values
('Campaign 1', '1', 'AND', 'P1');
insert into camp_grps values
('Campaign 1', '1', 'AND', 'P2');
insert into camp_grps values
('Campaign 1', '1', 'AND', 'P3');
insert into camp_grps values
('Campaign 2', '1', 'OR', 'G2');
insert into camp_grps values
('Campaign 2', '1', 'OR', 'G4');
insert into camp_grps values
('Campaign 2', '2', 'AND', 'G6');
CAMP | GRP | GRP_TYPE| PRODUCT
-----------|--------|---------|-------
Campaign 1 | 1 | AND | P1
Campaign 1 | 1 | AND | P2
Campaign 1 | 1 | AND | P3
Campaign 2 | 1 | OR | G2
Campaign 2 | 1 | OR | G4
Campaign 2 | 2 | AND | G6
Теперь мы можем получить простой обзор
- кампаний,
- групп в каждой из них, включая
- тип группы,
- сколько групп составляют кампанию и
- сколько продуктов составляют каждую группу.
SQL для этого выглядит следующим образом:
select
camp
, grp
, grp_type
, count(distinct grp) over
(partition by camp) camp_grp_cnt
, count(*) prd_cnt
from
camp_grps
group by
camp, grp, grp_type;
CAMP |GRP |GRP_TYPE |CAMP_GRP_CNT |PRD_CNT
------------|-------|-----------|---------------|-------
Campaign 1 |1 |AND |1 |3
Campaign 2 |1 |OR |2 |2
Campaign 2 |2 |AND |2 |1
Остальное немного утомительно, но не слишком сложно. Нам нужно
- подсчитать, сколько совпадающих покупок у каждого клиента для каждой группы - в зависимости от типа группы (AND
/ OR
) нам нужно считать группу как совпавшей , когда количество совпадений с группой больше 0 (OR
группа) или, по крайней мере, количество товаров в группе (AND
группа).
Объединенный SQL выглядит следующим образом:
with grp_ref as
(select
camp
, grp
, grp_type
, count(distinct grp) over
(partition by camp) camp_grp_cnt
, count(*) prd_cnt
from
camp_grps
group by
camp, grp, grp_type),
grp_match as
(select
p.customer, p.product
, gr.camp camp_ref, gr.grp grp_ref, gr.grp_type grp_type_ref, gr.prd_cnt prd_cnt_ref
, gr.camp_grp_cnt
, count(*) over
(partition by p.customer, cg.camp, gr.grp) camp_total_matches
from
purchases p
left outer join camp_grps cg
on p.product = cg.product
inner join grp_ref gr
on (cg.camp, cg.grp) = (gr.camp, gr.grp)),
match_cnter as
(select
customer, product
, camp_ref, grp_ref, grp_type_ref, prd_cnt_ref, camp_total_matches
, camp_grp_cnt
, case
when grp_type_ref = 'AND'
and ((camp_total_matches - prd_cnt_ref) >= 0) then
'AND grp matched'
when grp_type_ref = 'OR'
and (camp_total_matches > 1) then
'OR grp matched'
end matched_grp_info
, case
when grp_type_ref = 'AND'
and ((camp_total_matches - prd_cnt_ref) >= 0) then
1
when grp_type_ref = 'OR'
and (camp_total_matches > 1) then
1
end matched_grp
from
grp_match)
select
customer, camp_ref, SUM(matched_grp), MIN(camp_grp_cnt)
from
match_cnter
group by
customer, camp_ref;
Результат выглядит следующим образом:
CUSTOMER |CAMP_REF |SUM(MATCHED_GRP) | MIN(CAMP_GRP_CNT)
------------|-----------|-------------------|---------------------------
ABC |Campaign 1 |3 |1
ABC |Campaign 2 |3 |2
XYZ |Campaign 1 |? |1
За CUSTOMER
и CAMP_REF
(ссылка на кампанию) мы видим, сколько групп кампании было найдено (SUM(MATCHED_GRP)
) ) и сколько необходимо сопоставить, чтобы иметь право на участие в кампании (MIN(CAMP_GRP_CNT)
).
Клиент ABC
имеет право участвовать в обеих кампаниях 1 и 2, в то время как клиент XYZ
не соответствует ни одной группе ( см. ?
как NULL
в результате).