SQL Left vs Right Outer Join。返回所有值

时间:2012-12-07 19:23:06

标签: sql inner-join outer-join

问题:需要查询才能返回每年的MONTH和YIELD。出于某种原因,如果在月份a.Month中找不到数据,则查询将不返回b.Month的Yield。我需要查询返回所有月度数据,无论a.Month是否包含与“b”相同月份的数据的月份。

以下结果:应该返回“月份1 YIELD_1”的值。但它没有...因为“MONTH 1 YIELD_0”不包含第1个月的值。

**DATA RESULTS WITH: LEFT OUTER JOIN:**
Month   Yield_1    Yield_0
2        11.44      14
3         NULL     3.21
4         NULL     14.24
7         NULL     10.36
8         NULL       0
9         NULL     -9.6
10        NULL     10.35
11        NULL      1.4
12        11.44    -1.18


**DATA RESULTS WITH RIGHT OUTER JOIN:**
Month   Yield_1    Yield_0
NULL     11.44      NULL
2        11.44       14
12       11.44     -1.18

QUERY:

SET @ID_CARTERA = 8;

select     
        a.Month Month,
        b.Monthly_Yield Yield_Year_1,
        a.Monthly_Yield Yield_Year_0

from
    ( select  
          LEFT(A.F_ANOMES, 4) Year,
          RIGHT(A.F_ANOMES, 2) Month,
          ROUND(A.POR_RENTABILIDAD, 2) Monthly_Yield

from      dr_rent_carteras_meses A
where     A.ID_CARTERA = @ID_CARTERA
And       A.IND_RENTABILIDAD = 1

And       LEFT(A.F_ANOMES, 4) = ( select MAX(left(F_ANOMES, 4 ) ) - 0 from dr_rent_carteras_meses where ID_CARTERA = @ID_CARTERA ) ) a


LEFT outer join 
        ( select  
          LEFT(A.F_ANOMES, 4) Year,
          RIGHT(A.F_ANOMES, 2) Month,
          ROUND(A.POR_RENTABILIDAD, 2) Monthly_Yield

from      dr_rent_carteras_meses A
where     A.ID_CARTERA = @ID_CARTERA
And       A.IND_RENTABILIDAD = 1
And       LEFT(A.F_ANOMES, 4) = ( select MAX(left(F_ANOMES, 4 ) ) - 1 from dr_rent_carteras_meses where ID_CARTERA = @ID_CARTERA ) ) b on ( a.Month = b.Month )

order by  month asc

2 个答案:

答案 0 :(得分:0)

答案 1 :(得分:0)

摘要

我认为您不需要对此特定查询进行左,右或完全联接,因为基于GROUP BY / CASE的解决方案应该可以正常工作并且速度至少快两倍。

问题定义

我发现这个问题相当有趣,因为它似乎来自现实生活中的情况,我相信在现实生活中FULL JOIN很少有必要。所以对我来说真正的问题不是如何组合来自左右连接的数据,而是为什么首先需要完全连接?

此外,简单地用LEFT替换FULL是不够的,因为这会导致

  1. 相同月份的重复行;
  2. NULL个月,因为最近一年没有月份。
  3. 所以我决定继续解密查询。

    表格和数据设置

    不幸的是,@ smileyseven没有提供表定义或插入语句,所以我不得不从查询和示例数据中解决它们。根据查询语法,我假设使用了SQL Server,这也是我使用的。

    这是我对表格外观的猜测:

    CREATE TABLE dr_rent_carteras_meses 
    (
        F_ANOMES varchar(6) NOT NULL PRIMARY KEY,
        POR_RENTABILIDAD float NOT NULL,
        -- These columns are  irrelevant 
        ID_CARTERA INT NOT NULL DEFAULT(8),
        IND_RENTABILIDAD INT DEFAULT(1) NOT NULL
    )   
    

    重要的一点是F_ANOMES可能是关键列,否则我们不得不期望查询输出中每个月有多行,这似乎不太可能。

    以下插入语句应生成示例数据:

    INSERT dr_rent_carteras_meses (F_ANOMES, POR_RENTABILIDAD) VALUES 
    ('201202', 14),
    ('201203', 3.21),
    ('201204', 14.24),
    ('201207', 10.36),
    ('201208', 0),
    ('201209', -9.6),
    ('201210', 10.35),
    ('201211', 1.4),
    ('201212', -1.18),
    ('201101', 11.44),
    ('201102', 11.44),
    ('201112', 11.44)
    

    解决方案

    首先要注意的是,我们并不需要两次计算最大年份,所以我们将从这开始:

    declare @Max_Year int
    select 
        @Max_Year = MAX(left(F_ANOMES, 4))
    from dr_rent_carteras_meses 
    where ID_CARTERA = @ID_CARTERA
    

    ab表几乎相同,因此我们可能会重复使用它们:

    ;with 
    Monthly_Yield_CTE as
    ( 
        select  
            LEFT(F_ANOMES, 4) Year,
            RIGHT(F_ANOMES, 2) Month,
            ROUND(POR_RENTABILIDAD, 2) Monthly_Yield
        from      dr_rent_carteras_meses
        where     ID_CARTERA = @ID_CARTERA
        and       IND_RENTABILIDAD = 1
        and       LEFT(F_ANOMES, 4) in (@Max_Year, @Max_Year - 1)
    )
    

    并使用FULL JOIN,但我认为更好的选择是按月分组:

    select
        [Month],
        SUM(CASE WHEN [Year] = @Max_Year - 1 THEN Monthly_Yield ELSE 0 END) Yield_Year_1,
        SUM(CASE WHEN [Year] = @Max_Year THEN Monthly_Yield ELSE 0 END) Yield_Year_0
    from Monthly_Yield_CTE
    group by [Month]
    order by [Month]
    

    可以使用CTE版本或在没有CTE的情况下重写它,因为查询很简单:

    SET @ID_CARTERA = 8
    
    declare @Max_Year int
    select 
        @Max_Year = MAX(left(F_ANOMES, 4))
    from dr_rent_carteras_meses 
    where ID_CARTERA = @ID_CARTERA
    
    select  
        RIGHT(F_ANOMES, 2) Month,
        SUM(CASE 
                WHEN LEFT(F_ANOMES, 4) = @Max_Year - 1 
                THEN ROUND(POR_RENTABILIDAD, 2) 
                ELSE 0 
            END) Yield_Year_1,
        SUM(CASE 
                WHEN LEFT(F_ANOMES, 4) = @Max_Year 
                THEN ROUND(POR_RENTABILIDAD, 2) 
                ELSE 0 
            END) Yield_Year_0
    from      dr_rent_carteras_meses
    where     ID_CARTERA = @ID_CARTERA
    and       IND_RENTABILIDAD = 1
    and       LEFT(F_ANOMES, 4) in (@Max_Year, @Max_Year - 1)
    group by RIGHT(F_ANOMES, 2)
    order by 1
    

    我所知道的唯一缺点是,在数据丢失的几个月内,我们得到的是0而不是NULL,我不确定这是否重要。

    <强>性能

    这个查询的表现似乎好一点;在我的设置中,GROUP BY / CASE查询与分隔MAX的相对合计费用约为30%,而FULL JOIN解决方案则为70%。