Найти значение, связанное с минимальным значением другого столбца с предложением where

0

Скажем, у меня есть таблица цен;

+------------+----------+---+-----+----+-------+-----+
| 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), а затем найдите плату, связанную с этой ценой.

  • 1
    Я считаю, что нет способа переписать его без оконных функций или CTE так, как вы запрашиваете. К сожалению, более низкие версии MySQL (менее 8) не имеют его. Следовательно, какую версию MySQL вы используете?
  • 0
    извините, отредактировано, чтобы включить, MySQL версии 5.5.56. ах, так что мне может быть не повезло здесь ...
Показать ещё 11 комментариев
Теги:
greatest-n-per-group
correlated-subquery

3 ответа

1
Лучший ответ

Принимая @GordonLinoff ответ и расширяя требование включить минимизацию количества повторения кода, чтобы сделать динамическую генерацию SQL проще...

Изменение коррелированного подзапроса для возврата идентификатора строки вместо минимальной цены имеет два последствия

  1. Вам нужно только поместить фильтр в подзапрос
  2. Он никогда не вернет несколько строк в случае равенства


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
                      )
  • 0
    Большое спасибо @MatBailie, это было действительно полезно.
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
    )
  • 0
    Исходя из комментариев ОП, это может быть неправильно понято. Если пользователь удаляет фильтр b = 300 то t3.b = tb также ДОЛЖЕН быть удален. Проще всего заявить, что любые пользовательские фильтры должны применяться как внутри подзапроса, так и за его пределами.
1

Не используйте 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
                );
  • 1
    Спасибо, но если я добавлю AND b = 300 к where , то я все равно получу 0 возвращенных строк, так что, если я что-то упустил, я не знаю, решит ли это мою проблему?
  • 1
    @sauntimo Обновите ваш вопрос, чтобы включить вашу фактическую попытку.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню