Соедините таблицу продуктов с таблицей цен, для продуктов с n действительными ценами - PullRequest
3 голосов
/ 09 ноября 2011

У меня есть следующие таблицы товаров и цен:

таблица: продукт

productID   
---------
1
2
3
4
5

стол: цена

priceID     productID   started     expires
-------------------------------------------
1           1           2011-11-05  NULL    
2           1           2011-11-05  2011-11-20
3           2           2011-11-05  NULL
4           3           2011-11-05  NULL
5           3           2011-11-06  2011-11-08

и я хочу присоединиться к ним таким образом, чтобы:

  1. Только одна цена за товар
  2. Цены действительны, если price.started <= NOW() AND ( price.expires >= NOW() || price.expires IS NULL )
  3. Если для одного продукта действует более одной цены, следует выбрать цену с более высоким значением price.priceID
  4. Если цена не действительна, по-прежнему показывать информацию о продукте

Эти четыре критерия определяют наиболее действительные (на удачу лучшего срока) действительных цен. Таким образом, для NOW() == 2011-11-09 конечный результат должен быть:

priceID     productID   started     expires
-------------------------------------------
2           1           2011-11-05  2011-11-20
3           2           2011-11-05  NULL
4           3           2011-11-05  NULL

Я застрял в

  • Если для одного продукта действует более одной цены, следует выбрать цену с более высоким price.priceID

требование, которое проистекает из странного делового требования для более чем одной действительной цены для любого данного продукта в течение периода времени. Мой sql fu очень плохой, я дошел до:

SELECT
    product.*,
    price.*
FROM
    product
LEFT JOIN
    price
ON 
    price.productID = product.productID
    AND price.started <= NOW() 
    AND (
        price.expires IS NULL
        OR price.expires >= NOW()
    )

Что, конечно, для NOW() == 2011-11-09 приводит к:

productID   priceID     productID   started     expires
-------------------------------------------------------
1           1           1           2011-11-05  NULL    
1           2           1           2011-11-05  2011-11-20
2           3           2           2011-11-05  NULL
3           4           3           2011-11-05  NULL

Разъяснения (на основе комментариев и ответов):

  • Требования уникальны, но действительны. Перекрывающиеся интервалы - это фактическое требование бизнеса, иногда мне нужно показывать все цены (просто), а иногда только одну, более свежую (о чем я спрашиваю).
  • Не могу многое сделать, чтобы изменить модель данных.
  • Я ищу всё соединение, если возможно. (или подсказки о том, как подзапросы могут быть интегрированы в окончательное объединение)
  • Я бы предпочел решения, основанные на стандартном SQL (ANSI / ISO) или MySQL.

Ответы [ 5 ]

3 голосов
/ 09 ноября 2011

Обновлено

Следующий (полный) запрос должен помочь:

SELECT
    product.*,
    price.*
FROM
    product, 
   (SELECT price.* FROM price 
        WHERE price.started <= NOW() 
        AND 
            (price.expires IS NULL
            OR price.expires >= NOW())
    GROUP BY price.productId
    BY price.priceId DESC LIMIT 1) AS `price`
WHERE price.productID = product.productID;
1 голос
/ 09 ноября 2011
SELECT
    pro.*
  , pri.*
FROM 
    product AS pro
  JOIN
    price AS pri
      ON pri.priceID =
         ( SELECT p.priceID
           FROM price p 
           WHERE p.productID = pro.productID
             AND p.started <= CURRENT_DATE() 
             AND ( p.expires IS NULL
                OR p.expires >= CURRENT_DATE()
                 )
           ORDER BY p.priceId DESC 
           LIMIT 1
         )
0 голосов
/ 09 ноября 2011
WITH pc AS (
    SELECT pc1.priceid, pc1.productid
    , pc1.started, pc1.expires
    FROM tmp.price pc1
    WHERE pc1.started <= now()
    AND (pc1.expires IS NULL OR pc1.expires > now() )
    AND NOT EXISTS ( SELECT *
        FROM tmp.price pcx
        WHERE pcx.productid = pc1.productid
        AND pcx.started <= now()
            AND (pcx.expires IS NULL OR pcx.expires > now() )
        AND pcx.priceid > pc1.priceid
        )
    )
SELECT pd.productid
    , pd.productname
    , pc.started
    , pc.expires
FROM pc
JOIN tmp.product pd ON pc.productid = pd.productid
    ;

CTE - это безымянное представление.Если ваша СУБД не поддерживает CTE, вы можете сделать первую (CTE) часть запроса в именованном представлении;субсинтаксис такой же.YMMV.

0 голосов
/ 09 ноября 2011

если вы хотите избежать подзапросов, вы можете попробовать следующее утверждение:

SELECT
   product.*,
   price.*,
   MAX(price.priceID)
FROM
   product
LEFT JOIN
   price
ON 
   price.productID = product.productID
   AND price.started <= NOW() 
   AND (
      price.expires IS NULL
      OR price.expires >= NOW()
   )
GROUP BY price.productID

GROUP BY будет принудительно использовать только отдельные продукты в результате с максимальным идентификатором цены. Надеюсь, это поможет!

0 голосов
/ 09 ноября 2011
SELECT
    p.*,
  ( SELECT 
        TOP 1 pr.price 
    FROM price pr 
    WHERE pr.productID = p.productID 
      AND pr.started <= NOW() 
      AND (  pr.expires IS NULL 
          OR pr.expires >= NOW()) 
    ORDER BY 
        pr.productID DESC
  ) AS price
FROM product p
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...