SQL Server 2012 Express 中的另一个 LEFT OUTER JOIN 失败

Yet another LEFT OUTER JOIN fail in SQL Server 2012 Express

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

我需要一份报告,了解给定年份的所有储蓄(按月)。这意味着对于某些项目的节省,如果开始月份不是 1 月,则该项目的部分节省将落入不同的报告年。

所以我需要一个查询,该查询将 return 当前报告年的所有月份,并且对于项目在该月没有储蓄值的地方有零。

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

查看下面的代码:

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) 部分有效,returns 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) 

但结果相同。 MonthEndSavingtable如下:

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

这段代码应该可以满足您的需求:

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)   

Dang,Laughing Vergil 似乎是一个更快的打字员 =) 无论如何,这个想法几乎是一样的。您的 'error' 是您每个月都加入 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)