加入哪些行不存在或哪里标准匹配......?

时间:2012-04-04 20:10:18

标签: sql sql-server sql-server-2008 database-design

我正在尝试编写一个查询,告诉我哪些订单有有效的promocodes。 Promocodes仅在特定日期和可选某些软件包之间有效。

我甚至无法解释它是如何工作的(请参阅下面的psudo-ish代码),但基本上如果有与promocode相关联的包,那么订单必须包含其中一个包并且在有效的日期范围内,否则它只需要处于有效的日期范围内。

整个“如果PrmoPackage行存在”的事情真的让我失望了,我觉得我应该能够在没有一大堆Union的情况下做到这一点。 (我甚至不确定在这一点上是否会让它更容易......)

有人对查询有任何想法吗?

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

订单:

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

促销:

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

期望的结果:

OrderID | IsPromoCodeValid
1       | 1
2       | 0
3       | 1
4       | 0

5 个答案:

答案 0 :(得分:3)

Agh ....我认为有几种方法可以做到:

使用一些左外连接,一组by和一个case语句,这是一个有点混乱的方法

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

您可以使用CTE以更干净的方式执行此操作。

编辑:使用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)
;

编辑:还有一个解决方案。这个通过组合Promo和PromoPackage表创建一个“促销”表。没有相关PromoPackage记录的促销记录有效地将PackageID设为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

答案 1 :(得分:3)

;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;

非CTE版本

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;

答案 2 :(得分:0)

您可以通过相关子查询来完成此操作。我没有测试过,但是:

SELECT  
    a.OrderID,
    CASE WHEN 0 <= (
            SELECT COUNT(*)
            FROM PromoCode x
            JOIN PromoPackage y
                ON y.PromoCode = x.PromoCOde
                AND a.OrderTimeStamp BETWEEN y.PromoSTartTimestamp AND y.PromoEndTimestamp
            WHERE
                x.PromoCode = a.OrderPromoCode              
        ) THEN 0
    ELSE 1
    END AS 'IsPromoCodeValid'

FROM    
    Order a     

答案 3 :(得分:0)

我没有尝试重新创建表格,但此查询应该接近

SELECT o.OrderID, case ISNULL(pck.PackageID, 0) when 0 then 0 else 1 end as IsPromoCodeValid
FROM [Order] as o 
LEFT OUTER JOIN [Promo] as p ON
o.OrderPromoCode = p.PromoCode AND o.OrderTimestamp >= p.PromoStartTimestamp AND o.OrderTimestamp <= p.PromoStartTimestamp
LEFT OUTER JOIN [PromoPackage] pck ON  o.PackageID = pck.PackageID AND p.PromoCode = pck.PromoCode

答案 4 :(得分:-1)

这样的事可能有用(未经测试):

select o.OrderID,
  isPromoCodeValid = 
    isnull((select 1 from Promo p where o.OrderTimestamp >= p.PromoStartTimestamp and o.OrderTimestamp <= p.PromoEndTimestamp), 0)
    or isnull((select 1 from PromoPackage pp where o.OrderTimestamp >= pp.PromoStartTimestamp and o.OrderTimestamp <= pp.PromoEndTimestamp and o.PackageID = pp.PackageID), 0)
from orders o