Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Join Where Rows Don't Exist or Where Criteria Matches...?

I'm trying to write a query to tell me which orders have valid promocodes. Promocodes are only valid between certain dates and optionally certain packages.

I'm having trouble even explaining how this works (see psudo-ish code below) but basically if there are packages associated with a promocode then the order has to have one of those packages and be within a valid date range otherwise it just has to be in a valid date range.

The whole "if PrmoPackage rows exist" thing is really throwing me off and I feel like I should be able to do this without a whole bunch of Unions. (I'm not even sure if that would make it easier at this point...)

Anybody have any ideas for the query?

if `OrderPromoCode` = `PromoCode`

    then if `OrderTimestamp` is between `PromoStartTimestamp` and `PromoEndTimestamp`

        then if `PromoCode` has packages associated with it
            //yes
                then if `PackageID` is one of the specified packages
                    //yes
                        code is valid
                    //no
                        invalid
            //no
                code is valid

Order:

OrderID* | OrderTimestamp | PackageID | OrderPromoCode
1        | 1/2/11         | 1         | ABC
2        | 1/3/11         | 2         | ABC
3        | 3/2/11         | 2         | DEF
4        | 4/2/11         | 3         | GHI

Promo:

PromoCode* | PromoStartTimestamp* | PromoEndTimestamp*
ABC        | 1/1/11               | 2/1/11
ABC        | 3/1/11               | 4/1/11
DEF        | 1/1/11               | 1/11/13
GHI        | 1/1/11               | 1/11/13

PromoPackage:

PromoCode* | PromoStartTimestamp* | PromoEndTimestamp* | PackageID*
ABC        | 1/1/11               | 2/1/11             | 1
ABC        | 1/1/11               | 2/1/11             | 3
GHI        | 1/1/11               | 1/11/13            | 1

Desired Result:

OrderID | IsPromoCodeValid
1       | 1
2       | 0
3       | 1
4       | 0
like image 568
Greg Avatar asked Apr 04 '12 20:04

Greg


2 Answers

;WITH PromoCTE AS
(   SELECT  promo.*, CASE WHEN p.PromoCode IS NULL THEN 0 ELSE 1 END [HasPackage]
    FROM    Promo
            LEFT JOIN
            (   SELECT  DISTINCT PromoCode
                FROM    PromoPackage
            ) p
                ON promo.PromoCode = p.PromoCode
)
SELECT  [Order].OrderID, 
        CASE WHEN COUNT(CASE WHEN HasPackage = 1 THEN PromoPackage.PromoCode ELSE Promo.PromoCode END) >= 1 THEN 1 ELSE 0 END [IsPromoCodeValid]
FROM    [Order]
        LEFT JOIN PromoCTE promo
            ON Promo.PromoCode = [Order].OrderPromoCode
            AND [Order].OrderTimeStamp BETWEEN Promo.PromoStartTimestamp AND Promo.PromoEndTimestamp
        LEFT JOIN PromoPackage
            ON PromoPackage.PromoCode = OrderPromoCode
            AND PromoPackage.PackageID = [Order].PackageID
            AND [Order].OrderTimeStamp BETWEEN PromoPackage.PromoStartTimestamp AND PromoPackage.PromoEndTimestamp
GROUP BY [Order].OrderID;

NON CTE VERSION

SELECT  [Order].OrderID, 
        CASE WHEN COUNT(CASE WHEN HasPackage = 1 THEN PromoPackage.PromoCode ELSE Promo.PromoCode END) >= 1 THEN 1 ELSE 0 END [IsPromoCodeValid]
FROM    [Order]
        LEFT JOIN 
        (   SELECT  promo.*, CASE WHEN p.PromoCode IS NULL THEN 0 ELSE 1 END [HasPackage]
            FROM    Promo
                    LEFT JOIN
                    (   SELECT  DISTINCT PromoCode
                        FROM    PromoPackage
                    ) p
                        ON promo.PromoCode = p.PromoCode
        ) promo
            ON Promo.PromoCode = [Order].OrderPromoCode
            AND [Order].OrderTimeStamp BETWEEN Promo.PromoStartTimestamp AND Promo.PromoEndTimestamp
        LEFT JOIN PromoPackage
            ON PromoPackage.PromoCode = OrderPromoCode
            AND PromoPackage.PackageID = [Order].PackageID
            AND [Order].OrderTimeStamp BETWEEN PromoPackage.PromoStartTimestamp AND PromoPackage.PromoEndTimestamp
GROUP BY [Order].OrderID;
like image 148
GarethD Avatar answered Sep 21 '22 21:09

GarethD


Agh....I think there are a few ways to do it:

Here's a somewhat messy approach using a couple of left outer joins, a group by, and a case statement

SELECT [Order].OrderID, CASE count(isnull(Promo.PromoCode, PromoPackage.PromoCode)) WHEN 0 THEN 0 ELSE 1 END
FROM [Order]
LEFT OUTER JOIN Promo ON
    Promo.PromoCode = [Order].OrderPromoCode
    AND [Order].OrderTimestamp BETWEEN Promo.PromoStartTimestamp and Promo.PromoEndTimestamp
    AND NOT EXISTS (SELECT 1 FROM PromoPackage WHERE PromoPackage.PromoCode = Promo.PromoCode)
LEFT OUTER JOIN PromoPackage ON
    PromoPackage.PromoCode = [Order].OrderPromoCode
    AND PromoPackage.PackageID = [Order].PackageID
    AND [Order].OrderTimestamp BETWEEN PromoPackage.PromoStartTimestamp and PromoPackage.PromoEndTimestamp
GROUP BY
    [Order].OrderID

You might be able to do this in a cleaner manner using a CTE.

EDIT: updated with a query using a CTE

WITH OrderPromo (OrderID, PromoCode, PackageID)
AS
(
SELECT [Order].OrderID, Promo.PromoCode, null
FROM [Order]
INNER JOIN Promo ON
    [Order].OrderPromoCode = Promo.PromoCode
    AND [Order].OrderTimestamp BETWEEN Promo.PromoStartTimestamp AND Promo.PromoEndTimestamp
    AND NOT EXISTS (SELECT 1 FROM PromoPackage WHERE PromoPackage.PromoCode = Promo.PromoCode)

UNION ALL

SELECT [Order].OrderID, PromoPackage.PromoCode, PromoPackage.PackageID
FROM [Order]
INNER JOIN PromoPackage ON
    [Order].OrderPromoCode = PromoPackage.PromoCode
    AND [Order].PackageID = PromoPackage.PackageID
    AND [Order].OrderTimestamp BETWEEN PromoPackage.PromoStartTimestamp AND PromoPackage.PromoEndTimestamp
)
SELECT [Order].OrderID, 1
FROM [Order]
WHERE
    EXISTS (SELECT 1 FROM OrderPromo WHERE OrderPromo.OrderID = [Order].OrderID)

UNION ALL

SELECT [Order].OrderID, 0
FROM [Order]
WHERE
    NOT EXISTS (SELECT 1 FROM OrderPromo WHERE OrderPromo.OrderID = [Order].OrderID)
;

EDIT: one more solution. This one creates a "Promotion" table by combining the Promo and PromoPackage tables. Promo records that have no associated PromoPackage record effectively have a PackageID of null.

SELECT
  [Order].OrderID,
  CASE count(Promotion.PromoCode) WHEN 0 THEN 0 ELSE 1 END
FROM [Order]
LEFT OUTER JOIN (
    SELECT
      Promo.PromoCode,
      PromoPackage.PackageID,
      isnull(PromoPackage.PromoStartTimestamp, Promo.PromoStartTimestamp) as PromoStartTimestamp,
      isnull(PromoPackage.PromoEndTimestamp, Promo.PromoEndTimestamp) as PromoEndTimestamp
  FROM Promo
  LEFT OUTER JOIN PromoPackage ON
      Promo.PromoCode = PromoPackage.PromoCode
) Promotion ON
    Promotion.PromoCode = [Order].OrderPromoCode
    AND (Promotion.PackageID is null OR Promotion.PackageID = [Order].PackageID)
    AND [Order].OrderTimestamp BETWEEN Promotion.PromoStartTimestamp AND Promotion.PromoEndTimestamp
GROUP BY
  [Order].OrderID
like image 23
Dr. Wily's Apprentice Avatar answered Sep 23 '22 21:09

Dr. Wily's Apprentice