3

我正在尝试使用LEFT JOIN右表上的条件,但这样做会遇到很多问题。

我有两张桌子:

  1. projects{project_id,start_date}
  2. projectForeCast{project_id,year_number,month_number,hours}

我正在尝试获取上周打开的所有项目以及上个月记录的时间。

SELECT      dbo.Project.PROJECT_ID, dbo.ProjectForeCast.HOURS AS F0
FROM         dbo.Project LEFT JOIN  dbo.ProjectForeCast ON dbo.Project.PROJECT_ID = dbo.ProjectForeCast.PROJECT_ID
WHERE   (dbo.ProjectForeCast.YEAR_NUMBER = DATEPART(YYYY, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())))) AND 
                      (dbo.ProjectForeCast.MONTH_NUMBER = DATEPART(MM, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())))) AND 
                      (DATEPART(WK,dbo.Project.START_DATE) = DATEPART(WK, DATEADD(WK, - 1, GETDATE())))AND
                      (DATEPART(YYYY,dbo.Project.START_DATE) = DATEPART(YYYY, DATEADD(WK, - 1, GETDATE())))

它工作得很好,但如果项目projectForeCast最后没有记录,month_number我根本就没有得到这个项目。F0在这种情况下,我想在列中获取一个空单元格或 null 。这就是我尝试过LEFT JOIN但没有成功的原因。

4

6 回答 6

3

根据我以前的经验,我会这样编写您的 SQL 查询:

SELECT p.PROJECT_ID, pfc.HOURS AS F0
FROM 
(   SELECT dbo.Project.PROJECT_ID FROM dbo.Project q 
    WHERE (DATEPART(WK,dbo.Project.START_DATE) = DATEPART(WK, DATEADD(WK, - 1, GETDATE())))AND
    (DATEPART(YYYY,dbo.Project.START_DATE) = DATEPART(YYYY, DATEADD(WK, - 1, GETDATE())))
) p
LEFT JOIN  
(   SELECT dbo.ProjectForeCast.HOURS FROM dbo.ProjectForeCast 
    WHERE (dbo.ProjectForeCast.YEAR_NUMBER = DATEPART(YYYY, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())))) AND 
    (dbo.ProjectForeCast.MONTH_NUMBER = DATEPART(MM, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE()))))
) pfc
ON p.PROJECT_ID = pfc.PROJECT_ID

或者使用 SQL 语句将更具可读性的表别名:

SELECT p.PROJECT_ID, pfc.HOURS AS F0
FROM 
(   SELECT pr.PROJECT_ID FROM dbo.Project pr 
    WHERE (DATEPART(WK,pr.START_DATE) = DATEPART(WK, DATEADD(WK, - 1, GETDATE())))AND
    (DATEPART(YYYY,pr.START_DATE) = DATEPART(YYYY, DATEADD(WK, - 1, GETDATE())))
) p
LEFT JOIN  
(   SELECT pf.HOURS FROM dbo.ProjectForeCast pf
    WHERE (pf.YEAR_NUMBER = DATEPART(YYYY, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())))) AND 
    (pf.MONTH_NUMBER = DATEPART(MM, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE()))))
) pfc
ON p.PROJECT_ID = pfc.PROJECT_ID

我认为您将通过上述查询获得正确的结果。

于 2012-05-06T19:44:02.690 回答
2

尝试将您的WHERE条款移动到您的LEFT JOIN.

DECLARE @YEAR_NUMBER1 INT; SET @YEAR_NUMBER1=DATEPART(YYYY, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())));
DECLARE @MONTH_NUMBER1 INT; SET @MONTH_NUMBER1=DATEPART(MM, DATEADD(MM, 0, DATEADD(WK, - 1, GETDATE())));
DECLARE @YEAR_NUMBER2 INT; SET @YEAR_NUMBER2=DATEPART(YYYY, DATEADD(WK, - 1, GETDATE()));
DECLARE @WEEK_NUMBER1 INT; SET @WEEK_NUMBER1=DATEPART(WK, DATEADD(WK, - 1, GETDATE()));

SELECT p.PROJECT_ID
, pfc.HOURS AS F0
FROM  project p
LEFT JOIN  projectForeCast pfc 
    ON p.PROJECT_ID = pfc.PROJECT_ID
    AND pfc.YEAR_NUMBER = @YEAR_NUMBER1
    AND pfc.MONTH_NUMBER = @MONTH_NUMBER1
    AND DATEPART(YYYY,p.[START_DATE]) = @YEAR_NUMBER2
    AND DATEPART(WK,p.[START_DATE]) = @WEEK_NUMBER1;
GO

一些额外的快速提示:

  • 表别名将使您的代码更具可读性
  • 函数调用WHEREJOIN减慢您的查询速度,因此请尽可能用变量替换它们。
于 2012-05-06T12:57:09.937 回答
0

您需要检查 NULl 在 where 语句中引用项目预测的位置。

联接正在工作,但第二个表正在获取空值。使用 COALESCE 或 IS NULL。

于 2012-05-06T12:41:53.407 回答
0

这是意料之中的。如果您进行左连接,即使您在右表上没有记录,您也会从左表中获取所有记录,但是由于您在 where 子句中说您希望右表上的记录 WHERE YEAR_NUMBER(例如)满足一个条件,你几乎使它成为一个常规连接,因为如果记录不存在,它们是空的,并且与任何结果相比都是空的。

您需要做的是 YEAR_NUMBER = the_year_you_want OR YEAR_NUMBER is null 来处理没有 projectForeCast 的情况

还记得()在每个条件下使用

于 2012-05-06T12:55:22.493 回答
0

当您执行LEFT JOINorRIGHT JOIN时,将条件放在JOIN子句中还是放在WHERE子句中会有所不同。

检查此答案以通过示例进行深入解释:
这两个查询在获得两个不同的结果集时有什么区别?

--> 如果您还想获得没有一行的项目projectForeCast,那么您必须将整个条件放在JOIN子句而不是WHERE子句中。

于 2012-05-06T12:58:05.907 回答
0

LEFT JOIN 将返回项目表中的所有记录,因此您的问题在于您的 WHERE 子句,我相信问题在于您上周使用的是 DATEPART 'WEEK'。相反,您应该使用“ISOWEEK”或在 SQL2008 之前自己计算它。

DECLARE @TodayDayOfWeek INT
DECLARE @EndOfPrevWeek DateTime
DECLARE @StartOfPrevWeek DateTime

--get number of a current day (1-Monday, 2-Tuesday... 7-Sunday)
SET @TodayDayOfWeek = datepart(dw, GetDate())
--get the last day of the previous week (last Sunday)
SET @EndOfPrevWeek = DATEADD(dd, -@TodayDayOfWeek, GetDate())
--get the first day of the previous week (the Monday before last)
SET @StartOfPrevWeek = DATEADD(dd, -(@TodayDayOfWeek+6), GetDate())

为了测试这个理论,只需删除 LEFT JOIN 只留下项目表和相关的 where 子句。您应该看到相同的项目列表。

另一个练习是选择各个日期部分以确保它们正确匹配。我想你会发现这DATEPART(WK...)并没有给你预期的结果。

另一件需要注意的事情是,您将减去 1 周,这将在周一和周二运行时给出不同的结果。当您说“上周”时,您的意思是字面意义上的今天 - 7 还是您的意思是上周,如周日至周六?

试试这个查询,看看它是否能解决您的问题。

DECLARE @Today DATETIME,
        @TodayDayOfWeek INT, 
        @EndOfPrevWeek DATETIME, 
        @StartOfPrevWeek DATETIME, 
        @MonthPart1 INT,
        @MonthPart2 INT,
        @YearPart1 INT,
        @YearPart2 INT 

-- get a date range consisting of 'last week'
SET @Today = GETDATE()
--get number of a current day (1-Monday, 2-Tuesday... 7-Sunday) 
SET @TodayDayOfWeek = datepart(dw, @Today) 
--get the last day of the previous week (last Sunday) 
SET @EndOfPrevWeek = DATEADD(dd, -@TodayDayOfWeek, @Today) 
--get the first day of the previous week (the Monday before last) 
SET @StartOfPrevWeek = DATEADD(dd, -(@TodayDayOfWeek+6), @Today) 

--last week could span months or even years (i.e. Dec/Jan)
SET @MonthPart1 = DATEPART(MM, @StartOfPrevWeek)
SET @MonthPart2 = DATEPART(MM, @EndOfPrevWeek)

SET @YearPart1 = DATEPART(YYYY, @StartOfPrevWeek)
SET @YearPart2 = DATEPART(YYYY, @EndOfPrevWeek)

SELECT      
            dbo.Project.PROJECT_ID, 
            dbo.ProjectForeCast.HOURS AS F0
FROM         
            dbo.Project 
LEFT JOIN   dbo.ProjectForeCast ON dbo.Project.PROJECT_ID = dbo.ProjectForeCast.PROJECT_ID
WHERE       dbo.ProjectForeCast.YEAR_NUMBER IN (@YearPart1, @YearPart2) 
AND         dbo.ProjectForeCast.MONTH_NUMBER IN (@MonthPart1, @MonthPart2) 
AND         dbo.Project.START_DATE BETWEEN @StartOfPrevWeek AND @EndOfPrevWeek
于 2012-05-06T13:02:10.783 回答