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 Union
s. (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
;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;
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With