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(*) 不会考虑到我们'重新处理所有记录。必须将其与变量进行基准测试,以查看使用我的实际数据实际上会更快。 @MarkCOUNT(*)
将反对单个分组,在您的情况下是单个 job
以上是关于MAX(),但前提是所有值都不为空的主要内容,如果未能解决你的问题,请参考以下文章