【问题标题】:Full Outer Join "with" table not working完全外部联接“与”表不起作用
【发布时间】:2013-07-11 22:47:33
【问题描述】:

我有一个由 sku、store、period 键入的销售表。由此,我需要一个返回包含 This YearLast Year's 信息的记录的查询。

下面查询的逻辑是这样的:

  1. 计算去年的销售额(在带表中)
  2. 在主体(WHERE CLAUSE)中计算今年的销售额
  3. 将“LAST YEAR”表加入主表。仅加入 sku 和商店(您不能按日期加入,因为它们不会重叠)

我的问题是去年的结果不是全部。我的结果表现得好像我正在执行 LEFT JOIN,而不是返回“LAST YEAR”表中的所有结果。

附加细节:

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

下面的代码已经简化了一些...我不太担心语法,但更多的是关于逻辑。如果有人有任何想法,或者知道我的逻辑中可能存在的缺陷,我会全神贯注!提前致谢!

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

【问题讨论】:

  • MySQL 不支持公用表表达式 (CTE) 和 FULL OUTER JOIN。我认为这个问题被错误标记为 MySQL,而不是 SQL Server。
  • 内连接可能更适合您所描述的内容。您是否只对两年都有销售的商店/sku 组合感兴趣?
  • 我对在这里发帖还是比较陌生。我试图删除 mysql 标记...我只是标记它,因为我对任何 sql 逻辑感兴趣来解释一些行为。

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


【解决方案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

【讨论】:

  • 您对 INNER JOIN 隐含发生的原因的解释提供了一些见解,使我了解了导致问题的原因。但是,WHERE 子句最终不是问题。问题是,为了提取所有 SKU(如果它存在于任一表中),我还必须选择 sku#,因此由于汇总了销售额,因此迫使我按 sku 分组。我需要能够独立于 SKU 来拉动销售,所以这条路线是不可行的。在了解了以上所有内容后,我能够快速构建下面的SQL。
  • @ejc:很高兴听到你想通了。
【解决方案2】:

似乎有多个问题。这个谓词:

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

这里的“技巧”是使用条件测试来确定是否应将 spsales 金额包含在 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

【讨论】:

    【解决方案3】:

    谢谢大家的建议。我确实重组了 SQL,使今年和去年都嵌套在 with 子句中。我忽略的致命缺陷是仅存在于去年数据集中的 sku 没有被包括在内,除非我在主子句中按 sku 选择/分组。

    为了解决这个问题,我使用了下面的代码。我分别为 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"    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-26
      • 2014-12-18
      • 1970-01-01
      • 2017-08-04
      • 1970-01-01
      相关资源
      最近更新 更多