【问题标题】:SQL Left vs Right Outer Join. Return all valuesSQL 左与右外连接。返回所有值
【发布时间】:2023-03-09 17:49:01
【问题描述】:

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

以下结果:应返回“MONTH 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

查询:

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

【问题讨论】:

  • 使用完全联接来确保您的数据中确实有 a.month 1,因为外部联接应该可以解决您的问题。

标签: sql inner-join outer-join


【解决方案1】:

总结

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

问题定义

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

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

  1. 相同月份的重复行;
  2. NULLmonths 表示最近一年的月份不可用。

所以我决定继续破译查询。

表格和数据设置

不幸的是,@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%。

【讨论】:

    【解决方案2】:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-10
      • 2012-03-20
      • 1970-01-01
      • 2021-09-02
      • 2023-03-07
      • 1970-01-01
      相关资源
      最近更新 更多