Full Outer Join“with”表无效

时间:2013-07-11 22:47:34

标签: mysql sql with-statement full-outer-join

我有一个由 sku,store,期间键入的销售表。由此,我需要一个返回包含今年去年信息的记录的查询。

以下查询背后的逻辑是:

  1. 计算去年的销售额(在表格中)
  2. 计算主体(WHERE CLAUSE)中的今年销售额
  3. 将“LEAR YEAR”表加入主表。只加入sku和商店(你不能按日期加入,因为它们不会重叠)
  4. 我的问题是去年的结果不是全部金额。我的结果就好像我正在进行LEFT JOIN,而不是从“LAST YEAR”表中返回所有结果。

    其他细节:

    • 当我运行LEFT JOIN和FULL OUTER JOIN时,我得到相同的结果。
    • 当我独立执行“WITH”子句时,结果是正确的
    • 当我执行整个陈述时,去年的销售额不是全额

    下面的代码已经简化了一些...我不是很担心语法,而是更多关于LOGIC。如果有人有任何想法,或者知道我的逻辑中存在可能的缺陷,我全都耳朵!提前谢谢!

    WITH lastYear AS (                                                 
        SELECT 
            spsku "sku", 
            spstor "store", 
            sum(spales) "sales_ly"   
        FROM SALES                                              
        WHERE spyypp BETWEEN 201205 AND 201205 
        GROUP BY spstor, spsku
    )                                                                  
    SELECT 
        Sales_report.spstor "store", 
        sum(spales) "bom_retail", 
        sum(LY."sales_ly") "sales_ly"
    FROM SALES Sales_report                              
    FULL OUTER JOIN lastYear LY ON LY."sku" = spsku AND LY."store" = spstor
    WHERE spyypp BETWEEN 201305 AND 201305      
    GROUP BY spstor
    

3 个答案:

答案 0 :(得分:1)

条款WHERE spyypp BETWEEN 201305 AND 201305具有强制您加入INNER JOIN的结果,因为加入完成后执行。

为了达到您想要的效果,您必须将此子句移动到这样的ON条件中,以便在>连接之前应用该子句:

WITH lastYear AS (                                                 
    SELECT 
        spsku "sku", 
        spstor "store", 
        sum(spales) "sales_ly"   
    FROM SALES                                              
    WHERE spyypp BETWEEN 201205 AND 201205 
    GROUP BY spstor, spsku
)                                                                  
SELECT 
    Sales_report.spstor "store", 
    sum(spales) "bom_retail", 
    sum(LY."sales_ly") "sales_ly"
FROM SALES Sales_report                              
FULL OUTER JOIN lastYear LY
    ON LY."sku" = spsku
   AND LY."store" = spstor
   AND spyypp BETWEEN 201305 AND 201305      
GROUP BY spstor

或者,在某些情况下提供更清晰的代码,同时使LAST_YEAR和THIS_YEAR公用表表达式如下:

WITH 
lastYear AS (                                                 
    SELECT 
        spsku "sku", 
        spstor "store", 
        sum(spales) "sales_ly"   
    FROM SALES                                              
    WHERE spyypp BETWEEN 201205 AND 201205 
    GROUP BY spstor, spsku
),
this year as (
    SELECT 
        spsku "sku", 
        spstor "store", 
        sum(spales) "sales_ly"   
    FROM SALES                                              
    WHERE spyypp BETWEEN 201305 AND 201305 
    GROUP BY spstor, spsku
)                                                                  
SELECT 
    TY.spstor "store", 
    sum(TY.spales) "bom_retail", 
    sum(LY."sales_ly") "sales_ly"
FROM this year TY
FULL OUTER JOIN lastYear LY
    ON LY."sku"   = TY.sku
   AND LY."store" = TY.stor

答案 1 :(得分:0)

似乎存在多个问题。这个谓词:

WHERE spyypp BETWEEN 201305 AND 201305 

可能正在消除一些“外连接”行。对于spyypp,这些行将具有NULL。 (spsku的分组有点奇怪,但实际上这可能不是问题,你只需要获得单独的行...总共有一个匹配的spsku,另一行它们不是,但那些所有人都会崩溃购买GROUP BY,所以我不明白这一点。

如果你想使用公用表表达式,我想你想使用两个,并对这些结果集进行全外连接。我使用的函数为非匹配选取非NULL值,ISNULL函数对此很方便。

WITH lastYear AS
(
    SELECT 
        spsku,
        spstor,
        sum(spales) AS sales_ly
    FROM SALES
    WHERE spyypp BETWEEN 201205 AND 201205
    GROUP BY spstor, spsku
)
, thisYear AS (
    SELECT 
        spsku,
        spstor,
        SUM(spales) AS sales_ty
    FROM SALES
    WHERE spyypp BETWEEN 201305 AND 201305
    GROUP BY spstor, spsku
)
SELECT ISNULL(thisYear.spstor,lastYear.spstor) AS "store"
     , SUM(TY.sales_ty) AS "bom_retail"
     , SUM(LY.sales_ly) AS "sales_ly"
  FROM thisYear TY
  FULL
 OUTER
  JOIN lastYear LY
    ON LY.spsku = TY.spsku 
   AND LY.store = TY.store
 GROUP
    BY ISNULL(thisYear.spstor,lastYear.spstor)

如果这是你所追求的结果集,那似乎是一大堆不必要的噪音。如果您不关心返回的spsku及其完整的外连接,那么此查询将返回等效的结果集:

SELECT r.spstor AS "store"
     , SUM(CASE WHEN r.spyypp BETWEEN 201305 AND 201305 THEN r.spsales END) AS "bom_retail"
     , SUM(CASE WHEN r.spyypp BETWEEN 201205 AND 201205 THEN r.spsales END) AS "sales_ly"
  FROM SALES r
 WHERE r.spyypp BETWEEN 201305 AND 201305
    OR r.spyypp BETWEEN 201205 AND 201205
 GROUP
    BY r.spstor

这里的“技巧”是使用条件测试,以确定是否应该在SUM中包含销售额。


如果这实际上是针对MySQL(而不是SQL Server),那么我会这样写:

SELECT r.spstor AS `store`
     , SUM(IF(r.spyypp BETWEEN 201305 AND 201305,r.spsales,NULL)) AS `bom_retail`
     , SUM(IF(r.spyypp BETWEEN 201205 AND 201205,r.spsales,NULL)) AS `sales_ly`
  FROM SALES r
 WHERE r.spyypp BETWEEN 201305 AND 201305
    OR r.spyypp BETWEEN 201205 AND 201205
 GROUP
    BY r.spstor

答案 2 :(得分:0)

谢谢大家的建议。我确实重组了SQL,以使今年和去年都嵌套在一个with子句中。除了我在主要条款中选择/分组为sku之外,我所看到的致命缺陷是仅存在于去年数据集中的sku's不被包括在内。

为了解决这个问题,我使用了下面的代码。我用TY / LY销售的占位符分别构建了数据集。然后我执行了一个UNION来组合表(TY / LY存储在不同的列和不同的行中)。我把所有这些都塞进了一个子查询中。因为我在对数据进行求和(按非求和字段分组),这会折叠所有行,以便以正确的格式正确反映。

WITH lastYear AS (                                                
   SELECT sku, store, sum(sales) "sales_ly"  
   FROM DWHLIB.SLSSUMPD                                             
   WHERE spyypp BETWEEN 201205 AND 201205    
   GROUP BY store, sku
),                                                                
thisYear AS (                                                     
   SELECT spsku sku, store, sum(sales) "sales"     
   FROM DWHLIB.SLSSUMPD                                             
   WHERE spyypp BETWEEN 201305 AND 201305    
   GROUP BY store, sku                                        
)
SELECT sum(AY."sales"), sum(AY."sales_ly"), AY."store"                                                                 
FROM (
    SELECT sum(TY."sales") "sales", 0 "sales_ly", TY."store"
    FROM thisYear TY GROUP BY TY."store"                              
    UNION ALL                                                         
    SELECT  0 "sales", sum(LY."sales_ly") "sales_ly", LY."store"
    FROM lastYear LY
    GROUP BY LY."store"
) AY
GROUP BY "store"