Структура таблицы
У меня есть таблица, подобная следующей:
* 1006 мест *
В следующей таблице приведен список предприятий
id name
50 Nando's
60 KFC
награды
В таблице указано количество наград, место, которому оно соответствует, и количество очков, необходимых для получения награды.
id venue_id name points
1 50 5% off 10
2 50 10% off 20
3 50 11% off 30
4 50 15% off 40
5 50 20% off 50
6 50 30% off 50
7 60 30% off 70
8 60 60% off 100
9 60 65% off 120
10 60 70% off 130
11 60 80% off 140
points_data
В таблице указано количество баллов, оставшихся у пользователя за каждое место.
venue_id points_remaining
50 30
60 90
Обратите внимание, что этот запрос фактически вычисляется в SQL следующим образом:
select * from (
select venue_id, (total_points - points_redeemed) as points_remaining
from (
select venue_id, sum(total_points) as total_points, sum(points_redeemed) as points_redeemed
from (
(
select venue_id, sum(points) as total_points, 0 as points_redeemed
from check_ins
group by venue_id
)
UNION
(
select venue_id, 0 as total_points, sum(points) as points_redeemed
from reward_redemptions rr
join rewards r on rr.reward_id = r.id
group by venue_id
)
) a
group by venue_id
) b
GROUP BY venue_id
) points_data
но для этого вопроса вы, вероятно, можете просто проигнорировать этот массивный запрос и предположить, что таблица просто называется points_data
.
Желаемый выход
Я хочу получить один запрос, который получает:
- Лучшие 2 награды, которые пользователь имеет право на каждое место проведения
- Наименьшие 2 награды, на которые пользователь еще не имеет права для каждого места
Таким образом, для приведенных выше данных результат будет:
id venue_id name points
2 50 10% off 20
3 50 11% off 30
4 50 15% off 40
5 50 20% off 50
7 60 30% off 70
8 60 60% off 100
9 60 65% off 120
Что я получил до сих пор
Лучшее решение, которое я нашел до сих пор, - это сначала получить points_data, а затем использовать код (например, PHP) для динамической записи следующего:
(
select * from rewards
where venue_id = 50
and points > 30
ORDER BY points desc
LIMIT 2
)
union all
(
select * from rewards
where venue_id = 50
and points <= 30
ORDER BY points desc
LIMIT 2
)
UNION ALL
(
select * from rewards
where venue_id = 60
and points <= 90
ORDER BY points desc
LIMIT 2
)
UNION ALL
(
select * from rewards
where venue_id = 60
and points > 90
ORDER BY points desc
LIMIT 2
)
ORDER BY venue_id, points asc;
Однако я чувствую, что запрос может быть слишком длинным и неэффективным. Например, если у пользователя есть очки в 400 местах, то это 800 подзапросов.
Я тоже пытался сделать соединение, но не могу лучше, чем:
select * from points_data
INNER JOIN rewards on rewards.venue_id = points_data.venue_id
where points > points_remaining;
что далеко от того, что я хочу.