Прежде всего, мои извинения за название этого вопроса, у меня нет лучшей идеи на данный момент. Сделайте хорошее предложение, и я исправлю название. (Если у меня есть разрешение на это, я действительно не знаю.)
Ситуация:
Мне трудно выполнить правильный SQL-запрос. У меня есть установка, где люди могут размещать заказы, продукты и т. Д., И при определенных обстоятельствах получают скидки.
Рассмотрим следующую схему:
Product:
[...]
Price:
product_id: integer
description: string
[...]
Order:
[...]
OrderItem:
order_id: integer
price_id: integer
amount: integer
И рассмотрим следующие правила:
- Товаров: 9.
- Все товары имеют 2 цены, одна с описанием Цена A и одна с описанием Цена B .
- Все эти цены одинаковы для каждого продукта. (То есть все цены PriceA одинаковы, а все цены PriceB одинаковы.)
Проблема:
Для каждого набора из 5 различных продуктов с одинаковым уровнем цены (т. Е. PriceA против PriceB) общая стоимость заказа снижается на определенную сумму. Я пытаюсь написать запрос, который говорит мне, сколько раз это происходит.
Примеры:
Пример 1:
Пользователь размещает заказ:
- 5 раз product1,
- 5 раз product2,
- 5 раз product3,
- 3 раза product4,
- 3 раза product5.
Все на PriceA, клиент получает 3 раза скидку, так как есть 3 комплекта по 5
Пример 2:
Пользователь размещает заказ:
- 5 раз product1,
- 5 раз product2,
- 5 раз product3,
- 5 раз product4,
- 2 раза product5,
- 2 раза product6,
- 2 раза product7
Все цены. Теперь клиент получает 5-кратную скидку, поскольку существует 4 набора по 5, два из которых связаны с продуктом 5, два - с продуктом 6, а другой - с продуктом7.
Борьба:
Я пробовал этот SQL:
SELECT min(amount) as amount from
(SELECT oi.amount from `order` o
inner join orderitem oi on oi.order_id = o.id
inner join price p on oi.price_id = p.id AND p.description = "PriceA"
inner join product pr on p.product_id = pr.id
order by oi.amount desc
limit 5) as z
having count(amount) = 5;
Это прекрасно работает для примера 1, но в примере 2 он даст неправильный результат, так как выберет первый набор из 5 элементов, а затем проигнорирует
Вопрос в том: это разрешимо в SQL? Или мне лучше расширить выбор и заняться математикой с помощью сценариев? (Мое веб-приложение написано на PHP, поэтому у меня есть место для серверной математики.)
Решение:
Я реализовал решение Нейла; теперь это выглядит так:
/** @var $oid integer The order ID. */
/* Select all amounts ordered per item, only for a given price title. */
$sql = <<<SQL
SELECT oi.amount as amount FROM orderitems oi
INNER JOIN orders o ON oi.order_id = o.id AND o.id = $oid
INNER JOIN prices p ON oi.price_id = p.id AND p.title = '%s'
INNER JOIN products pr ON p.product_id = pr.id
ORDER BY oi.amount DESC
SQL;
$discountInfo = array(
array(
'price' => 'PriceA',
'discounts' => array(
9 => 49.50, /* Key is 'size of set', value is 'discount'. */
5 => 23.50
),
),
array(
'price' => 'PriceB',
'discounts' => array(
9 => 22,
5 => 10,
),
),
);
foreach($discountInfo as $info)
{
/* Store all ordered amounts per item in Array. */
$arr = array();
$result = $this->dbQuery(sprintf($sql,$info['price']));
while ($row = mysql_fetch_assoc($result)) $arr[] = $row['amount'];
foreach ($info['discounts'] as $am => $di)
{
/* For each highest set of $am items, fetch the smallest member. */
while (count($arr) >= $am)
{
/* Add discount for each complete set */
$discount += $di * $arr[$am - 1];
/* Substract the amounts from all members of the set */
for ($i=0; $i<=$am - 1; $i++) $arr[$i] -= $arr[$am - 1];
/* Remove all elements that are 0 */
foreach ($arr as $k=>$v) if ($v == 0) unset ($arr[$k]);
/* Array is messed up now, re-sort and re-index it. */
rsort($arr);
}
}
}