Как отфильтровать неагрегированные результаты запроса по агрегированному столбцу? - PullRequest
0 голосов
/ 19 февраля 2020

Я создаю сервис сравнения цен. Продукты из одного Site сравниваются с продуктами из одного или нескольких Sites. Продукты сопоставляются из одного Site с другим с использованием таблицы ProductMatch:

UML Diagram

С учетом следующего запроса для извлечения продуктов вместе с их соответствиями :

SELECT
    p1.id AS p1_id, p1.name AS p1_name, p1.price AS p1_price,
    p2.id AS p2_id, p2.name AS p2_name, p2.price AS p2_price,
    m.time
FROM Product p1
LEFT JOIN ProductMatch m ON m.fromProduct_id = p1.id
LEFT JOIN Product p2 ON m.toProduct_id = p2.id

WHERE p1.site_id = 1;

Как отфильтровать товары, цена которых (p1.price) ниже минимальной цены конкурента (MIN(p2.price))?

Используя подзапросы, вот как бы я это сделал:

SELECT
    p1.id AS p1_id, p1.name AS p1_name, p1.price AS p1_price,
    p2.id AS p2_id, p2.name AS p2_name, p2.price AS p2_price,
    m.time
FROM Product p1
LEFT JOIN ProductMatch m ON m.fromProduct_id = p1.id
LEFT JOIN Product p2 ON m.toProduct_id = p2.id

WHERE p1.id IN (
    SELECT x.id FROM (
        SELECT _p1.id, _p1.price
        FROM Product _p1
        JOIN ProductMatch _m ON _m.fromProduct_id = _p1.id
        JOIN Product _p2 ON _m.toProduct_id = _p2.id
        WHERE _p1.site_id = 1
        GROUP BY _p1.id
        HAVING _p1.price < MIN(_p2.price)
    ) x
);

Можно ли упростить этот запрос, чтобы не использовать подзапросы?

Мои проблемы:

  • кажется странным повторять те же самые объединения в подзапросе
  • У меня есть опасения по поводу производительности подзапросов для больших наборов данных
  • подзапросы не очень хорошо работают с моим ORM

1 Ответ

1 голос
/ 19 февраля 2020

С MIN() оконной функцией внутри CTE, которая будет отфильтрована:

WITH cte AS (
  SELECT
    p1.id AS p1_id, p1.name AS p1_name, p1.price AS p1_price,
    p2.id AS p2_id, p2.name AS p2_name, p2.price AS p2_price,
    m.time,
    MIN(p2.price) OVER (PARTITION BY p1.id) AS min_price
  FROM Product p1
  LEFT JOIN ProductMatch m ON m.fromProduct_id = p1.id
  LEFT JOIN Product p2 ON m.toProduct_id = p2.id
  WHERE p1.site_id = 1
)
SELECT 
  p1_id, p1_name, p1_price,
  p2_id, p2_name, p2_price,
  time
FROM cte
WHERE p1_price < min_price
...