【问题标题】:Yet another LEFT OUTER JOIN fail in SQL Server 2012 ExpressSQL Server 2012 Express 中的另一个 LEFT OUTER JOIN 失败
【发布时间】:2016-12-20 19:57:55
【问题描述】:

我想知道是否有人能够解决我的查询问题。我有一个简单的表格,每个月都有项目节省。总有连续 12 个月的价值或储蓄,但第一个月可能会有所不同(例如:从 1 月开始持续 12 个月,从 3 月开始持续 12 个月等)。

我需要一份报告,以获取给定年份的所有储蓄(按月)。这意味着,对于某些项目节约而言,如果开始月份不是 1 月,那么部分项目节约将落在不同的报告年度。

因此,我需要一个查询,该查询将返回当前报告年度的所有月份,并且对于项目在该月没有保存值的情况为零。

我有一些从 7 月开始的项目,而我只拿回这 6 个月的价值。也就是说,回到日期的左连接不是正确的外部连接。谁能告诉我哪里出错了?

见下面的代码:

DECLARE @MonthEndSnapshot SMALLDATETIME;
SELECT @MonthEndSnapshot =  getdate()

DECLARE @StartDate SMALLDATETIME, @EndDate SMALLDATETIME;
SELECT @StartDate = FORMAT(@MonthEndSnapshot, 'yyyy') + '0101', @EndDate = FORMAT(@MonthEndSnapshot, 'yyyy') + '1231';

;WITH d(d) AS 
(
    SELECT 
        DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0))
    FROM 
        (SELECT TOP 
             (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
             n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
         FROM 
             sys.all_objects 
         ORDER BY [object_id]) AS n
)
select  
    left(datename(month, d.d), 3) as xAxisValueMon,
    datepart(mm, d.d) as xAxisValue,
    a.ProjectId as ProjectId, 
    ISNULL(SUM(a.Saving), 0) as yAxisValue
from 
    d 
LEFT OUTER JOIN 
    (SELECT 
         mes.ProjectId, mes.Saving, mes.SavingMonth
     FROM 
         dbo.sf_SnapshotMonthEndSaving() mes) AS a ON d.d = a.SavingMonth
group by 
    a.ProjectId,  datename(month, d.d), datepart(mm, d.d)
order by   
    a.ProjectId, datepart(mm, d.d)  

WITH d(d) 部分有效,并返回 12 个月的日期(从 1 月到 12 月的第 1 个月)。

我还尝试了以下结构作为查询:

;WITH d(d) AS 
(
  SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0))
  FROM ( SELECT TOP (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
    FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select  left(datename(month, d.d), 3) as xAxisValueMon,
    datepart(mm, d.d) as xAxisValue,
    mes.ProjectId as ProjectId, 
    ISNULL(SUM(mes.Saving), 0) as yAxisValue
from d LEFT OUTER JOIN 
    dbo.sf_SnapshotMonthEndSaving() mes 
    ON d.d = mes.SavingMonth
group by mes.ProjectId,  datename(month, d.d), datepart(mm, d.d)
order by   mes.ProjectId, datepart(mm, d.d) 

但结果相同。 MonthEndSaving表如下:

CREATE TABLE [dbo].[MonthEndSaving]
(
    [MonthEndSavingId] [int] IDENTITY(1,1) NOT NULL,
    [MonthEndSnapshot] [datetime] NOT NULL,   
    [ProjectId] [int] NOT NULL,
    [SavingMonth] [datetime] NOT NULL,
    [Saving] [money] NOT NULL,
    [DateCreated] [datetime] NOT NULL,

    PRIMARY KEY CLUSTERED (MonthEndSavingId)
)
GO

ALTER TABLE MonthEndSaving
    ADD CONSTRAINT [ProjectMonthEndSaving] 
    FOREIGN KEY (ProjectId) REFERENCES [dbo].[Project](ProjectId) 
GO

【问题讨论】:

  • mes.SavingMonth 带来了什么价值??
  • projectID 只会在该项目的 SavingMonth 值与您的 d 集合中的月份匹配的月份显示。如果您希望每个项目 ID 都显示出来,则必须无条件地加入它们。如果我理解正确(例如ISNULL(SUM(CASE WHEN mes.SavingMonth = MONTH(d.d) THEN mes.Saving ELSE 0 END),0)),您的情况需要在SUM 内的CASE 语句中。
  • 只关注一个 ProjectId(从 7 月开始保存),您预计会获得 12 条记录:一条用于 1 月,一条用于 2 月,一条用于 3 月,等等……您希望这些记录是第一个6 条记录的金额为零,第 7 条及以后的记录有来自表函数的相关金额。对?问题是您只能获得 6 条记录:7 月的一条,8 月的一条,9 月的一条等。每条记录都有各自的金额。我理解正确吗?
  • 感谢您的回复,但这似乎不起作用。所以 SavingMonth 是一个日期,所以我将您的代码行固定为:
  • @marc_s ...啊你一定是风格的警察!是的,我确实坚持一种风格,但在这种情况下,我将代码传递给了决定不这样做的人。目前,我的首要任务是让它工作,而不是担心整理它。但感谢您的宝贵意见

标签: sql sql-server join outer-join


【解决方案1】:

Dang,Laughing Vergil 似乎是一个更快的打字员 =) 无论如何,这个想法几乎是一样的。您的“错误”是您每个月都加入dbo.sf_SnapshotMonthEndSaving() 中的所有项目。如果一个合适,它只返回那个一个,如果两个合适,它会显示这两个等等......但它不会在每个项目中重复。这应该。

DECLARE @StartDate datetime = '1 jan 2016',
        @EndDate datetime = '1 dec 2016'

;WITH d(FirstDayOfMonth) AS 
(
  SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0))
  FROM ( SELECT TOP (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
    FROM sys.all_objects ORDER BY [object_id] ) AS n
),
RelevantProjects AS
(
   SELECT DISTINCT ProjectId
     FROM dbo.sf_SnapshotMonthEndSaving() mes
    WHERE mes.SavingMonth BETWEEN @StartDate AND @EndDate -- you could also join to d but I think this is faster
),
ProjectsAndDates AS
(
    SELECT ProjectID,
           FirstDayOfMonth
      FROM d
     CROSS JOIN RelevantProjects
)
select  left(datename(month, d.FirstDayOfMonth), 3) as xAxisValueMon,
        datepart(mm, d.FirstDayOfMonth) as xAxisValue,
        d.ProjectId as ProjectId, 
        ISNULL(SUM(mes.Saving), 0) as yAxisValue
   from ProjectsAndDates d 
    LEFT OUTER JOIN [MonthEndSaving] mes  -- dbo.sf_SnapshotMonthEndSaving() mes 
                 ON mes.SavingMonth = d.FirstDayOfMonth
                AND mes.Project_id  = d.ProjectID
   group by d.ProjectId, datename(month, d.FirstDayOfMonth), datepart(mm, d.FirstDayOfMonth)
   order by d.ProjectId, datepart(mm, d.FirstDayOfMonth) 

【讨论】:

  • 太棒了!感谢@deroby 和@LaughingVergil!实际上,我仍然必须将它加入到项目表中才能获取 ProjectTypeId 而不仅仅是 ProjectId。但是您帮助我解决了 JOIN 的问题。让我们看看我怎么能从这里把它填满! ;p
【解决方案2】:

这段代码应该做你需要的:

DECLARE @MonthEndSnapshot SMALLDATETIME;
SELECT @MonthEndSnapshot =  getdate()



DECLARE @StartDate SMALLDATETIME, @EndDate SMALLDATETIME;
SELECT @StartDate = FORMAT(@MonthEndSnapshot, 'yyyy') + '0101', @EndDate = FORMAT(@MonthEndSnapshot, 'yyyy') + '1231';

;WITH d(d) AS 
(
  SELECT DATEADD(MONTH, n, DATEADD(MONTH, DATEDIFF(MONTH, 0, @StartDate), 0))
  FROM ( SELECT TOP (DATEDIFF(MONTH, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
    FROM sys.all_objects ORDER BY [object_id] ) AS n
)
select  left(datename(month, d.d), 3) as xAxisValueMon,
    datepart(mm, d.d) as xAxisValue,
    prj.ProjectId as ProjectId, 
    ISNULL(SUM(a.Saving), 0) as yAxisValue
from d 
CROSS JOIN 
    (
        SELECT DISTINCT mes.ProjectId
        FROM dbo.sf_SnapshotMonthEndSaving() mes
    ) as prj
LEFT OUTER JOIN 
    (
        SELECT mes.ProjectId, mes.Saving, mes.SavingMonth
        FROM dbo.sf_SnapshotMonthEndSaving() mes 
    ) as a
    ON d.d = a.SavingMonth
    AND prj.ProjectID = a.ProjectID
group by prj.ProjectId,  datename(month, d.d), datepart(mm, d.d)
order by prj.ProjectId,  datepart(mm, d.d)   

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-29
    • 2014-08-10
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    相关资源
    最近更新 更多