左边连接条件在右表上

时间:2012-05-06 12:32:16

标签: sql sql-server left-join

我正在尝试在正确的桌子上使用LEFT JOIN条件,而且我遇到了很多问题。

我有两张桌子:

  1. projects{project_id,start_date}
  2. projectForeCast{project_id,year_number,month_number,hours}
  3. 我正在尝试获取上周开放的所有项目以及上个月记录的小时数。

    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的原因,但它没有用。

6 个答案:

答案 0 :(得分: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中的函数调用会降低您的查询速度,因此请尽可能用变量替换它们。

答案 1 :(得分:2)

根据我之前的经验,我会将您的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

OR 使用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

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

答案 2 :(得分:0)

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

连接正在运行,但第二个表获取空值。使用COALESCE或IS NULL。

答案 3 :(得分:0)

这是预料之中的。如果你进行左连接,你将获得左表中的所有记录,即使你没有右表上的记录,但是因为你在where子句上说你想要在右表上记录WHERE YEAR_NUMBER(例如)满足一个条件,你几乎使它成为一个常规连接,因为,如果记录不存在,它们是null和null与任何结果null相比。

你需要做的是像YEAR_NUMBER = the_year_you_want或YEAR_NUMBER为空来处理你没有projectForeCast的情况

还记得在每个条件

上使用()

答案 4 :(得分:0)

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

通过示例查看此答案以获得深入的解释:
What is the difference in these two queries as getting two different result set?

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

答案 5 :(得分: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