FULL OUTER JOIN不生效:需返回当月所有日期无论对应活动是否为空
问题根因
- 逻辑顺序错误:需要保留全部记录的
Dates表被放在了JOIN的最末端做全外连接,前面的INNER JOIN链只要有一个关联失败,整行数据就会被丢弃,根本留不到和Dates表关联的步骤 - 过滤条件位置错误:你当前的筛选条件写在WHERE子句中,当
Dates表对应的日期没有工时数据时,关联的项目、用户字段全部为NULL,如果你选择了特定的项目/用户参数,这些没有数据的日期行就会被过滤 - 全外连接冗余:你不需要全外连接,需求是保留全部符合条件的
Dates表数据,用左连接即可
修改方案
- 先构造需要保留的维度基表:将你需要筛选的日期、项目、用户先过滤出来做笛卡尔积,保证所有需要展示的(日期+项目+用户)组合都存在
- 所有业务表都对这个基表做左连接,避免关联失败丢数据
- 所有维度筛选条件都提前到基表的过滤逻辑中,不要放在最终的WHERE子句
- 聚合时对空值的工时做兼容处理
修改后代码
-- 先构造需要保留的所有维度组合:选中的日期 + 选中的项目 + 选中的用户 WITH BaseDim AS ( SELECT d.Date, d.MonthNameYear, p.ProjectID, p.ProjectName, p.ClientID, u.id AS UserID, u.Name AS UserName FROM Dates d CROSS JOIN tProjects p CROSS JOIN tUsers u WHERE d.MonthNameYear IN (@parmDate) AND p.ProjectName IN (@parmProjects) AND u.Name IN (@parmUser) ) SELECT bd.ProjectName, bd.ProjectID, bd.Date, bd.MonthNameYear, bd.UserID AS id, bd.UserName AS Name, ISNULL(SUM(ts.ActivityTime), 0) AS Time, e.ExpenseID, c.ClientName FROM BaseDim bd -- 左连接工时表,关联不上的话工时返回空,聚合后为0 LEFT JOIN tTimesheets ts ON bd.ProjectID = ts.ProjectID AND bd.UserID = ts.CreatedBy AND CAST(ts.ActivityDate AS DATE) = CAST(bd.Date AS DATE) -- 左连接客户表 LEFT JOIN tClients c ON bd.ClientID = c.ClientID -- 左连接费用相关表 LEFT JOIN tExpenses e ON ts.CreatedBy = e.CreatedBy AND CAST(ts.ActivityDate AS DATE) = CAST(e.ExpenseDate AS DATETIME) AND ts.ProjectID = e.ProjectID LEFT JOIN tExpenseType et ON e.CategoryId = et.Id GROUP BY bd.UserName, bd.UserID, bd.Date, bd.MonthNameYear, bd.ProjectName, bd.ProjectID, e.ExpenseID, c.ClientName ORDER BY bd.Date
补充说明
如果你不需要每个选中的项目+用户组合都返回行,只需要有过工时的项目和用户与日期的组合,把BaseDim里的CROSS JOIN改成根据已有工时数据去重关联即可,不需要笛卡尔积。
内容的提问来源于stack exchange,提问作者IamReallyStuck
相关产品推荐
相关产品推荐

