MAX(),但前提是所有值都不为空

Posted

技术标签:

【中文标题】MAX(),但前提是所有值都不为空【英文标题】:MAX(), but only if all values are not null 【发布时间】:2021-07-13 00:31:53 【问题描述】:

在 SQL Server 2016 中,我有一个如下所示的表:

dbo.jobs

id name
0 First Job
1 Second Job

dbo.tasks

id start_date end_date
0 2017-04-01 2017-04-3
1 2017-04-02 2017-04-4
2 2017-04-03 null

dbo.job_tasks

id job_id task_id
0 0 0
1 1 1
2 1 2

我正在尝试做的是创建一个包含作业的视图,其中任务的最低开始日期和最高结束日期反映了作业的开始和结束日期。这相对容易,在视图中查询如下:

SELECT
   jobs.id,
   jobs.name,
   MIN(tasks.start_date) as job_start_date,
   MAX(task.end_date) as job_end_date,
FROM
   jobs
   LEFT OUTER JOIN job_tasks on jobs.id = job_tasks.job_id
   LEFT OUTER JOIN tasks on job_tasks.task_id = tasks.task_id
GROUP BY jobs.id, jobs.name, job_tasks.job_id, job_tasks.task_id, tasks.task_id

结果如下:

id name job_start_date job_end_date
0 First Job 2017-04-01 2017-04-3
1 Second Job 2017-04-02 2017-04-4

但是,如果有一个任务的 end_date 为空,例如“第二个作业”,则该任务未完成 - 所以 job_end_date 实际上应该为空,正确的输出应该是这样的:

id name job_start_date job_end_date
0 First Job 2017-04-01 2017-04-3
1 Second Job 2017-04-02 null

所有聚合函数都忽略 null,但 COUNT() 除外。所以,我现在的想法是使用表值函数,并传入job_id。这将限制正在处理的数据的范围,并允许我检查空结束日期并使用 CASE 语句进行相应调整。像这样的:

DECLARE @completed bit
SET @completed = 1

IF EXISTS(
   SELECT * FROM job_tasks 
   INNER JOIN tasks on tasks.task_id = job_tasks.task_id AND tasks.end_date IS NULL
   WHERE job_tasks.task_id = @taskId
)
BEGIN
   SET @completed = 0
END

SELECT 
  jobs.id,
  jobs.name,
  MIN(tasks.start_date) as job_start_date,
  CASE WHEN @completed = 0 THEN NULL
  ELSE (MAX(tasks.end_date)) END AS job_end_date
FROM
   jobs
   ... joins and group by clause
WHERE jobs.id = @jobId

是否有更好的方法来获取这种数据视图?也许是一种在不完全影响性能的情况下保留视图的方法?

任何建议将不胜感激。

(省略了视图和函数样板)

【问题讨论】:

【参考方案1】:

您可以使用 COALESCE 和一个大日期和一个案例将其翻译回 null,类似于

SELECT id, name, job_start_date,
    (CASE WHEN job_end_date <> d '2100-01-01'
         THEN job_end_date
    END) as job_end_date
FROM
(SELECT
   jobs.id,
   jobs.name,
   MIN(tasks.start_date) as job_start_date,
   MAX(COALESCE(task.end_date,d '2100-01-01' )) as job_end_date,
FROM
   jobs
   LEFT OUTER JOIN job_tasks on jobs.id = job_tasks.job_id
   LEFT OUTER JOIN tasks on job_tasks.task_id = tasks.task_id
GROUP BY jobs.id, jobs.name, job_tasks.job_id, job_tasks.task_id, tasks.task_id) A 

这也可以不使用子选择来完成,但它更难读写...

【讨论】:

这是一种有趣的方法,它的好处是在视图中工作,不需要函数。我可能会选择 '9999-12-31' 作为高日期值,因为那是 SQL Server 中的最大日期。谢谢!【参考方案2】:

您可以使用case 表达式:

SELECT jobs.id, jobs.name,
       MIN(tasks.start_date) as job_start_date,
       (CASE WHEN COUNT(task.end_date) = COUNT(*)
             THEN MAX(task.end_date)
        END) as job_end_date

【讨论】:

我认为这只会在函数中起作用 - 查询仅限于单个 jobs.id - 否则 count(*) 不会考虑到我们'重新处理所有记录。必须将其与变量进行基准测试,以查看使用我的实际数据实际上会更快。 @Mark COUNT(*) 将反对单个分组,在您的情况下是单个 job

以上是关于MAX(),但前提是所有值都不为空的主要内容,如果未能解决你的问题,请参考以下文章

Oracle:使用分隔符连接,但前提是两个操作数都不为空

请问在html中怎么让select的默认值为空,且select里面的option值都不为空。

c#winforms中所有文本框都不为空后如何启用按钮?

C#判断字符串为空有哪几种方法

通过SQL创建一个有主键自动递增有默认值不为空有注释的表

已解决list.size() = 1 但显示 All elements are null