【问题标题】:How to ORDER BY based on condition? SQL Server如何根据条件订购? SQL 服务器
【发布时间】:2020-12-22 18:57:43
【问题描述】:

例如,下面有一个表格:

ID          NAME           Company          StartDate        EndDate        Status     
--------------------------------------------------------------------------------------
50          Bob            ABC              11-15-2016       02-05-2017     Former  
50          Bob            XYZ              05-06-2014       05-06-2015     Former
50          Bob            EFG              06-10-2019       NULL           Active
50          Bob            LMN              07-05-2019       NULL           Active

我试图按降序列出他们最近的 3 个工作,因此他们的“活动”工作将排在第一位,但如果他们有 2 个“活动”工作,如上面的示例,我只想拉“活动”具有最近开始日期的工作,或者如果两个“活动”工作的开始日期相同,则只需随机选择其中一个,跳过另一个活动,然后继续他们最近的以前的工作。 “活动”作业的 EndDate 值始终为 NULL。

所以我正在寻找的结果集如下:

ID          NAME           Company          StartDate        EndDate        Status        rn     
----------------------------------------------------------------------------------------------
50          Bob            LMN              07-05-2019       NULL           Active         1 
50          Bob            ABC              11-15-2016       02-05-2017     Former         2
50          Bob            XYZ              05-06-2014       05-06-2015     Former         3

我已经尝试了下面的代码,但它无法检查两个活动作业状态并正确地给它们一个行号。根据上述条件,我希望只有一个 Active 工作是 rn = 1,而 rn = 2 和 rn = 3 将是最后 2 个“前”工作:

  select *, row_number() over (partition by ID, NAME order by CASE WHEN Status = 'Active' AND 
  EndDate is NULL THEN Status END desc, EndDate desc
  ) rn
  from employment

【问题讨论】:

  • 他们能有更多那两个在职的工作吗?如果是这样,大概仍然只有一个出现在结果中?

标签: sql-server sql-order-by case greatest-n-per-group window-functions


【解决方案1】:

您可以使用窗口函数来做到这一点。假设“Active”和“Former”是仅有的两个可能的状态值,您可以这样做:

select *
from (
    select t.*, 
        row_number() over (partition by id, status order by startDate desc) rn
    from mytable t
) t
where 
    (status = 'Active' and rn = 1)
    or (status <> 'Active' and rn <= 2)

如果可能还有其他状态,则将窗口函数更改为:

row_number() over (
    partition by id, case when status = 'Active' then 1 else 0 end 
    order by startDate desc
) rn

最后,如果您想使用startDate 订购“活动”记录并使用endDate 订购其他人:

row_number() over (
    partition by id, case when status = 'Active' then 1 else 0 end 
    order by case when status = 'Active' then startDate else endDate end desc
) rn

【讨论】:

  • 是的,还有一个“退休”状态,我也希望将其包含在与“前任”相同的标准中。此外,一些“前”或“退休”的工作也可能有一个 NULL StartDate,但总是有一个 EndDate
  • @jwalls91:最后一个表达式似乎做了你想做的事。
  • 当我尝试最后一个表达式时,以前的作业似乎存在问题,其 StartDate 为 NULL。它给 Active as rn = 1 这是正确的,但它也给了一个具有 NULL StartDate a rn = 1 的“前”工作。所以我想我需要找到一种方法将“Active”解释为 rn = 1,并解释“Former”或“Retired”,它们总是有一个 EndDate,但可能有一个 NULL StartDate,并对这些“Former”进行排序”或“退休”最近。如果他们没有“活跃”,那么只需按顺序按他们的前/退休工作排序
  • 看起来在我将“状态 = 'Active' 然后 1 else 0 的情况”部分编辑为“状态 = 'Active' 然后 0 else 1 的情况”部分之后,这似乎是现在订购它们正确地,即使前者之一仍然是 rn = 1,对于前者/退休,我需要它是 rn = 2 和 rn = 3。
  • 这似乎有效,基本上创建了另一个具有正确编号的列。谢谢!
【解决方案2】:

您可以使用 CTE 来获取最新的 Active 工作,并使用 rn 来获取以前的工作,然后 UNION 两者:

;with cte1 as
(
select Id, Name, C.Company, C.StartDate, C.EndDate, C.Status, rn = 1
From employment E
Outer Apply
(
    Select top 1    E2.Company,
                    E2.StartDate,
                    E2.EndDate,
                    E2.Status
    From employment E2
    where E.Id = E2.Id and E.Name = E2.Name
    and E2.Status = 'Active'
    order by E2.StartDate desc
) C
group by E.Id, E.Name, C.Company, C.StartDate, C.EndDate, C.Status
),
cte2 as
(
  select *, 1 + row_number() over (partition by ID, NAME order by StartDate desc
  ) rn
  from employment
  where Status = 'Former'
)   
    select * from cte1
   union
    select * from cte2
    where rn <= 3
   order by Id asc, Name asc, StartDate desc

输出:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    • 1970-01-01
    • 2021-09-18
    • 2010-12-23
    • 1970-01-01
    相关资源
    最近更新 更多