Скажем, у меня есть таблица цен;
+------------+----------+---+-----+----+-------+-----+
| product_id | price_id | a | b | c | price | fee |
+------------+----------+---+-----+----+-------+-----+
| 1 | 1 | 1 | 100 | 10 | 500 | 60 |
| 1 | 2 | 1 | 100 | 20 | 505 | 50 |
| 1 | 3 | 1 | 200 | 10 | 510 | 30 |
| 1 | 4 | 1 | 200 | 20 | 515 | 25 |
| 1 | 5 | 1 | 300 | 10 | 520 | 15 |
| 1 | 6 | 1 | 300 | 20 | 525 | 50 |
| 1 | 7 | 2 | 100 | 10 | 530 | 40 |
| 1 | 8 | 2 | 100 | 20 | 535 | 35 |
| 1 | 9 | 2 | 200 | 10 | 540 | 60 |
+------------+----------+---+-----+----+-------+-----+
В действительности эта таблица будет иметь сотни продуктов, и каждый из столбцов a, b и c может принимать около 10 значений, и для каждой комбинации этих столбцов для каждого продукта будет цена.
Я хочу показать только одну цену за продукт, поэтому у меня есть GROUP BY
на product_id.
Предположим, что изначально я хочу отображать самую низкую цену для каждого продукта, я могу добиться этого с помощью SELECT
min (цена), без проблем. Теперь, когда я хочу показать плату, связанную с минимальной ценой, я не могу просто показать min (fee), потому что цены и сборы не коррелируют, и минимальная цена не обязательно имеет самую низкую плату. Поэтому я присоединяюсь к подзапросу, например;
SELECT
t.product_id,
t.price_id,
t.a,
t.b,
t.c,
min(t.price) as 'min_price',
t.fee,
t2.fee AS 'min_price_fee'
FROM
prices as t
JOIN so_q as t2 on t.product_id = t2.product_id
AND t.a = t2.a
AND t.b = t2.b
AND t.c = t2.c
AND t2.price = (
SELECT min(price)
FROM so_q as t3
WHERE t3.product_id = t.product_id
-- AND t3.b = 300
)
-- WHERE
-- t.b = 300
GROUP BY
t.product_id;
Но, как вы, возможно, догадались из строк, которые я прокомментировал, моя проблема возникает, когда пользователи добавили фильтры и теперь есть предложение where in play. Я не могу выполнить эту работу, не помещая предложение where в подзапрос (если я не получаю никаких строк, которые, как я думаю, я понимаю), и мой вопрос в том, есть ли способ, которым я могу это сделать что мне нужно иметь только одно предложение?
Спасибо за ваш совет - дайте мне знать, если я буду включать любую другую информацию. Попытка перенаправить MCVE из фактического кода, с которым я работаю, была сложной, поэтому я, возможно, забыл что-то очевидное.
EDIT, как версия MySQL, которая равна 5.5.56
EDIT 2
используя предложение @Gordon Linoff;
SELECT
p.*
FROM
prices p
WHERE
p.price = (
SELECT min( p2.price )
FROM prices p2
WHERE p2.product_id = p.product_id
)
AND b = 300;
Я все еще получаю 0 строк, возвращенных, когда я добавляю условие b = 300
к предложению where в последней строке.
ИЗМЕНИТЬ 3
Чтобы попытаться прояснить, что я пытаюсь сделать: до добавления каких-либо фильтров, для продукта 1, я хочу отобразить минимальную цену (500) и плату (60) из этой записи (price_id = 1). Если пользователь добавляет фильтр c = 20
, я хочу отобразить минимальную цену, которая имеет значение c
20 (505) и плату (50) из этой записи (price_id = 2). Я не думаю, что могу использовать min(price)
и min(fee)
потому что в конечном итоге вы получите цены и сборы из разных записей, и они должны быть из одной записи. Итак, мне нужно найти минимальную цену, которая удовлетворяет всем введенным пользователем критериям (которые в конечном итоге являются частью основного предложения where), а затем найдите плату, связанную с этой ценой.
Принимая @GordonLinoff ответ и расширяя требование включить минимизацию количества повторения кода, чтобы сделать динамическую генерацию SQL проще...
Изменение коррелированного подзапроса для возврата идентификатора строки вместо минимальной цены имеет два последствия
p.id = (SELECT id
FROM prices p2
WHERE p2.product_id = p.product_id
AND <filters>
ORDER BY price DESC,
some_tie_break_field(s)
LIMIT 1
)
С такой структурой вам, вероятно, будет полезно начать с таблицы product
чтобы свести к минимуму работу, выполняемую коррелированным подзапросом.
SELECT
prices.*
FROM
product
INNER JOIN
prices
ON prices.id = (SELECT id
FROM prices p
WHERE p.product_id = product.id
AND <filters>
ORDER BY price DESC,
some_tie_break_field(s)
LIMIT 1
)
Как я уже упоминал в комментариях, есть решения, использующие функцию окна и CTE, однако они недоступны в более низких версиях MySQL. Единственное решение, которое я могу предложить, где константа не повторяется, выглядит следующим образом:
SELECT
t.product_id,
t.price_id,
t.a,
t.b,
t.c,
min(t.price) as 'min_price',
t.fee,
t2.fee AS 'min_price_fee'
FROM
prices as t
JOIN so_q as t2 on t.product_id = t2.product_id
AND t.a = t2.a
AND t.b = t2.b
AND t.c = t2.c
AND t.b = 300
AND t2.price = (
SELECT min(price)
FROM so_q as t3
WHERE t3.product_id = t.product_id AND
t3.b = t.b
)
b = 300
то t3.b = tb
также ДОЛЖЕН быть удален. Проще всего заявить, что любые пользовательские фильтры должны применяться как внутри подзапроса, так и за его пределами.
Не используйте group by
. Вы хотите отфильтровать все остальные строки, поэтому используйте where
:
select p.*
from prices p
where p.price = (select min(p2.price)
from prices p2
where p2.product_id = p.product_id
);
Если вам нужно отфильтровать на b
, тогда вам нужно учитывать это как в подзапросе, так и в внешнем запросе:
select p.*
from prices p
where p.b = 300 and
p.price = (select min(p2.price)
from prices p2
where p2.product_id = p.product_id and p2.b = p.b
);
AND b = 300
к where
, то я все равно получу 0 возвращенных строк, так что, если я что-то упустил, я не знаю, решит ли это мою проблему?