SQL 服务器 return 加入的 table where 子句中没有条目时行的结果

SQL Server return result for row when no entry on joined table where clause

我遇到了一个问题,我试图从一个人的时期中提取时间表。如果他们有一个条目,我会得到一行,其中包含他们的姓名和时间表状态。但是,如果他们在一段时间内没有提交两个时间表中的任何一个,则他们根本不会出现在查询中(这意味着当有人跟进他们时他们会被遗漏)。

这是一个简化的示例查询,returns 没有任何内容,因为 resource_id 在 period_end_date 中没有数据大于 where 子句限制:

SELECT time_status_id, last_name
FROM dbo.wh_resource
LEFT JOIN wh_time_report ON wh_time_report.creator_resource_id = wh_resource.resource_id
WHERE wh_resource.resource_id = '31100670'
AND period_end_date >= '2017-01-29'
ORDER BY last_name

我发现制作它 (period_end_date >= '2017-01-29' OR period_end_date IS NULL) 是行不通的,因为他们过去曾输入过数据(如果他们从未输入过,它会把它们拉起来)。

如果由于 period_end_date 没有找到数据,有没有办法简单地放置一个 0?我正在查看 CASE WHERE,但没有成功找到任何地方。

如果他们没有比搜索的条目更新的条目,我宁愿得到一个 time_status_id 0 和这个人的名字。这可能相当简单吗?

我一直在寻求简化调用,但最糟糕的是我可以提取所有姓氏,然后提取所有时间表状态,然后找到不存在的任何条目。下面的这项工作很好(但缺少没有提交时间表的人)

SELECT wh_resource.resource_id, last_name, first_name, narrative_full_name, department_name,
ISNULL(MAX(CASE WHEN wh_time_report.period_end_date = '2017-02-04' THEN time_status_id END),0) as timesheet_status1,
SUM(CASE WHEN date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN ROUND(wh_time_subitem.hours_worked,2) END) as hours_total1,
SUM(CASE WHEN date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as hours_total_old1, 
SUM(CASE WHEN allocation_code_id = '91207' AND date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as sick_leave1,
SUM(CASE WHEN allocation_code_id = '91206' AND date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as vacation1, 
SUM(CASE WHEN allocation_code_id = '91209' AND date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as personal_leave1,
SUM(CASE WHEN allocation_code_id = '91208' AND date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as holiday1,
SUM(CASE WHEN allocation_code_id = '30091631' AND date_worked >= '2017-01-29' AND date_worked <= '2017-02-04' THEN wh_time_subitem.hours_worked END) as funeral_leave1, 
ISNULL(MAX(CASE WHEN wh_time_report.period_end_date = '2017-02-11' THEN time_status_id END),0) as timesheet_status2,
SUM(CASE WHEN date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN ROUND(wh_time_subitem.hours_worked,2) END) as hours_total2,
SUM(CASE WHEN date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as hours_total_old2, 
SUM(CASE WHEN allocation_code_id = '91207' AND date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as sick_leave2,
SUM(CASE WHEN allocation_code_id = '91206' AND date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as vacation2, 
SUM(CASE WHEN allocation_code_id = '91209' AND date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as personal_leave2,
SUM(CASE WHEN allocation_code_id = '91208' AND date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as holiday2,
SUM(CASE WHEN allocation_code_id = '30091631' AND date_worked >= '2017-02-05' AND date_worked <= '2017-02-11' THEN wh_time_subitem.hours_worked END) as funeral_leave2
FROM dbo.wh_resource
JOIN dbo.wh_department_resource ON wh_resource.resource_id = wh_department_resource.resource_id 
JOIN dbo.wh_department ON wh_department_resource.department_id = wh_department.department_id
LEFT JOIN wh_time_item ON wh_time_item.user_id =  wh_resource.resource_id
LEFT JOIN wh_time_subitem ON wh_time_subitem.time_item_id = wh_time_item.time_item_id
LEFT JOIN wh_time_report ON wh_time_report.creator_resource_id = wh_department_resource.resource_id 
WHERE wh_department_resource.is_default_department = 1 
AND wh_resource.is_active = 1 
AND last_name != 'API.User' 
AND wh_department.department_id = '30091606'
AND department_name NOT IN ('Sales', 'Marketing', 'Operations', '') 
AND (wh_time_report.period_end_date IN ('2017-02-04', '2017-02-11') OR wh_time_report.period_end_date IS NULL)
GROUP BY wh_resource.resource_id, last_name, first_name, narrative_full_name, hire_date, department_name
ORDER BY last_name

那个查询 returns 73 个结果,有 76 人,所以如果我用

查询来拉人
SELECT * FROM dbo.wh_resource 
JOIN wh_department_resource ON wh_resource.resource_id = wh_department_resource.resource_id
WHERE wh_resource.is_active = 1
AND department_id = '30091606' 
AND is_default_department ='1'
ORDER BY last_name, first_name

通过检查名称是否存在,我可以在 PHP 的两个查询中获得我需要的所有数据。但我更愿意在一次查询中获得所有信息!

编辑: 使用 SqlZim 的推送,以下查询对我来说运行良好。它可能很混乱,但运行速度很快(比这个正在替换的 1000 个查询快得多,不是开玩笑)。日期将通过 PHP 提供,如果需要,部门 ID 也会提供。

SELECT wh_resource.resource_id, last_name, first_name, narrative_full_name, department_name,
ISNULL(MAX(CASE WHEN T1.period_end_date = '2017-02-04' THEN T1.time_status_id END),0) as timesheet_status1,
SUM(CASE WHEN S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN ROUND(S1.hours_worked,2) END) as hours_total1,
SUM(CASE WHEN S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as hours_total_old1, 
SUM(CASE WHEN S1.allocation_code_id = '91207' AND S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as sick_leave1,
SUM(CASE WHEN S1.allocation_code_id = '91206' AND S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as vacation1, 
SUM(CASE WHEN S1.allocation_code_id = '91209' AND S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as personal_leave1,
SUM(CASE WHEN S1.allocation_code_id = '91208' AND S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as holiday1,
SUM(CASE WHEN S1.allocation_code_id = '30091631' AND S1.date_worked >= '2017-01-29' AND S1.date_worked <= '2017-02-04' THEN S1.hours_worked END) as funeral_leave1, 
ISNULL(MAX(CASE WHEN T2.period_end_date = '2017-02-11' THEN T2.time_status_id END),0) as timesheet_status2,
SUM(CASE WHEN S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN ROUND(S2.hours_worked,2) END) as hours_total2,
SUM(CASE WHEN S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as hours_total_old2, 
SUM(CASE WHEN S2.allocation_code_id = '91207' AND S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as sick_leave2,
SUM(CASE WHEN S2.allocation_code_id = '91206' AND S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as vacation2, 
SUM(CASE WHEN S2.allocation_code_id = '91209' AND S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as personal_leave2,
SUM(CASE WHEN S2.allocation_code_id = '91208' AND S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as holiday2,
SUM(CASE WHEN S2.allocation_code_id = '30091631' AND S2.date_worked >= '2017-02-05' AND S2.date_worked <= '2017-02-11' THEN S2.hours_worked END) as funeral_leave2
FROM dbo.wh_resource
JOIN dbo.wh_department_resource ON wh_resource.resource_id = wh_department_resource.resource_id 
JOIN dbo.wh_department ON wh_department_resource.department_id = wh_department.department_id
LEFT JOIN wh_time_item ON wh_time_item.user_id =  wh_resource.resource_id
LEFT JOIN wh_time_report T1 ON T1.creator_resource_id = wh_resource.resource_id AND T1.period_end_date = '2017-02-04'
LEFT JOIN wh_time_subitem S1 ON S1.time_item_id = wh_time_item.time_item_id AND S1.date_worked >= '2017-01-29' AND S1.date_worked < '2017-02-05'
LEFT JOIN wh_time_report T2 ON T2.creator_resource_id = wh_resource.resource_id AND T2.period_end_date = '2017-02-11'
LEFT JOIN wh_time_subitem S2 ON S2.time_item_id = wh_time_item.time_item_id AND S2.date_worked >= '2017-02-05' AND S2.date_worked < '2017-02-12'
WHERE wh_department_resource.is_default_department = 1 
AND wh_resource.is_active = 1 
AND last_name != 'API.User' 
AND wh_department.department_id = '30091606'
AND department_name NOT IN ('Sales', 'Marketing', 'Operations', '') 
GROUP BY wh_resource.resource_id, last_name, first_name, narrative_full_name, hire_date, department_name
ORDER BY last_name

如果您在 where 中引用 outer join table(在本例中为左侧)的列,而不允许 null,您将删除行.

如果将 where 条件移动到加入条件,则可以使用 isnull() and/or [=] 处理 select 中的 nulls 18=].

您的查询看起来像这样:

select 
    wr.resource_id
  , last_name
  , first_name
  , narrative_full_name
  , department_name
  , timesheet_status1 = isnull(max(case when wtr.period_end_date = '2017-02-04' then time_status_id end),0)
  , hours_total1      = sum(case when date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then round(wts.hours_worked,2) end)
  , hours_total_old1  = sum(case when date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , sick_leave1       = sum(case when allocation_code_id = '91207'    and date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , vacation1         = sum(case when allocation_code_id = '91206'    and date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , personal_leave1   = sum(case when allocation_code_id = '91209'    and date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , holiday1          = sum(case when allocation_code_id = '91208'    and date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , funeral_leave1    = sum(case when allocation_code_id = '30091631' and date_worked >= '2017-01-29' and date_worked <= '2017-02-04' then wts.hours_worked end)
  , timesheet_status2 = isnull(max(case when wtr.period_end_date = '2017-02-11' then time_status_id end),0)
  , hours_total2      = sum(case when date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then round(wts.hours_worked,2) end)
  , hours_total_old2  = sum(case when date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end)
  , sick_leave2       = sum(case when allocation_code_id = '91207'    and date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end)
  , vacation2         = sum(case when allocation_code_id = '91206'    and date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end)
  , personal_leave2   = sum(case when allocation_code_id = '91209'    and date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end)
  , holiday2          = sum(case when allocation_code_id = '91208'    and date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end)
  , funeral_leave2    = sum(case when allocation_code_id = '30091631' and date_worked >= '2017-02-05' and date_worked <= '2017-02-11' then wts.hours_worked end) 
from dbo.wh_resource as wr
  inner join dbo.wh_department_resource as wdr 
     on wr.resource_id = wdr.resource_id
    and wr.is_active = 1
    and wr.last_name != 'api.user'
    and wdr.is_default_department = 1
  inner join dbo.wh_department as wd 
     on wdr.department_id = wd.department_id
    and wd.department_id = '30091606'
    and department_name not in ('sales', 'marketing', 'operations', '')
  left join wh_time_item as wti 
     on wti.user_id =  wr.resource_id
  left join wh_time_subitem as wts 
     on wts.time_item_id = wti.time_item_id
  left join wh_time_report as wtr 
     on wtr.creator_resource_id = wdr.resource_id
    and wtr.period_end_date in ('2017-02-04', '2017-02-11') 
group by wr.resource_id, last_name, first_name, narrative_full_name, hire_date, department_name
order by last_name

由于您没有为 period_end_date 提供 table 并且 time_status_id 不确定
但是试试这个

SELECT time_status_id, last_name
FROM dbo.wh_resource
LEFT JOIN wh_time_report 
       ON wh_time_report.creator_resource_id = wh_resource.resource_id
      AND period_end_date >= '2017-01-29'
WHERE wh_resource.resource_id = '31100670'

ORDER BY last_name

对不起,我没有足够的时间来操作您提供的完整查询。 您可以尝试 OUTER APPLY 来获得期望的结果 - 在 table 中没有记录匹配标准的情况下得到零。简短的例子,如何做到这一点:

SELECT 
     [time_status_id] = a.time_status_id
    ,[last_name] = a.last_name
    ,[count] = isnull(b.cnt, 0)
FROM 
    dbo.wh_resource as a
outer apply
    (
        select
            cnt = count(*)
        from
            wh_time_report
        where
             creator_resource_id = a.resource_id             
    ) as b     
WHERE a.resource_id = '31100670'
AND a.period_end_date >= '2017-01-29'
ORDER BY a.last_name