我正在尝试在正确的桌子上使用LEFT JOIN
条件,而且我遇到了很多问题。
我有两张桌子:
projects{project_id,start_date}
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
的原因,但它没有用。
答案 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
其他一些快速提示:
WHERE
和JOIN
中的函数调用会降低您的查询速度,因此请尽可能用变量替换它们。答案 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 JOIN
或RIGHT 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