SQL Query:如何只返回第一个和最后一个实例?

时间:2015-07-22 20:20:33

标签: sql sql-server sql-server-2014

我有一个表格,显示每个案例的状态,同时执行多个作业,我希望显示结果,以便它只显示第一个和最后一个实例。 (主要是我想知道什么时候开始工作,最后知道什么状态)。

我通过UNION函数加入的查询设法获得了2个类似的min,max和group的结果。但是有更简单的方法吗?

但是,是否可以在一行显示2个实例而不是2个单独的行?因为第一个实例的日期将是开始日期,最后一个实例将是结束日期,我并不关心第一个状态,因为它总是待定,我只想知道最后的已知状态

第一个表显示未过滤的结果,第二个表是期望的结果(但是如果我们可以将第一个和最后一个实例组合在一条线上甚至更好)

ID  Status      Date       Job  Note
1   pending     1-Jul       A   abc
1   pending     2-Jul       A   xyz
1   pending     2-Jul       A   abc
1   done        3-Jul       B   xyz
1   done        4-Jul       A   abc
2   pending     1-Jul       A   abc
2   done        2-Jul       A   xyz
2   done        2-Jul       A   abc
2   pending     3-Jul       C   xyz
2   pending     4-Jul       C   xyz
2   pending     5-Jul       C   xyz
2   pending     6-Jul       C   xyz
3   pending     2-Jul       D   xyz
3   done        3-Jul       D   abc
3   pending     4-Jul       D   abc
3   pending     1-Jul       E   xyz
3   done        3-Jul       E   xyz

ID  Status      Date       Job  Note
1   pending     1-Jul       A   abc
1   done        3-Jul       B   xyz
1   done        4-Jul       A   abc
2   pending     1-Jul       A   abc
2   done        2-Jul       A   abc
2   pending     3-Jul       C   xyz
2   pending     6-Jul       C   xyz
3   pending     2-Jul       D   xyz
3   pending     4-Jul       D   abc
3   pending     1-Jul       E   xyz
3   done        3-Jul       E   xyz

非常感谢您提前

3 个答案:

答案 0 :(得分:1)

一种方法是按升序和降序两次使用ROW_NUMBER函数来获取每组的第一行和最后一行。见SQL Fiddle

WITH
CTE
AS
(
  SELECT
    ID
    ,Status
    ,dt
    ,Job
    ,Note
    ,ROW_NUMBER() OVER (PARTITION BY ID, Job ORDER BY dt ASC) AS rnASC
    ,ROW_NUMBER() OVER (PARTITION BY ID, Job ORDER BY dt DESC) AS rnDESC
  FROM T
)
SELECT 
    ID
    ,Status
    ,dt
    ,Job
    ,Note
FROM CTE
WHERE rnAsc=1 OR rnDesc=1
ORDER BY ID, Job, dt

此变体将扫描整个表格,计算行数并丢弃那些不满足过滤器的行。

第二种变体是使用CROSS APPLY,如果(a)你的主表有数百万行,(b)你有一个包含所有{{1}列表的小表,这可能会更有效率。 } s和ID s,(c)主表有适当的索引。在这种情况下,您不必读取主表的所有行,而是可以为每个Job执行索引搜索(两个搜索,一个用于第一行,另一个用于最后一行)。

答案 1 :(得分:0)

试试这个:

SELECT A.ID, A.JOB, A.STATUS, B.START_DATE, CASE WHEN A.STATUS = 'done' THEN C.END_DATE ELSE NULL AS END_DATE
FROM <JOBS_TABLE> A
JOIN (SELECT ID, JOB, MIN(DATE) AS START_DATE FROM <JOBS_TABLE> GROUP BY ID, JOB) B
ON A.ID = B.ID
AND A.JOB = B.JOB
JOIN (SELECT ID, JOB, MAX(DATE) AS END_DATE FROM <JOBS_TABLE GROUP BY ID, JOB) C
ON A.ID= C.ID
AND A.JOB = C.JOB
AND A.DATE = C.END_DATE

您需要更换&lt; JOBS_TABLE&gt;无论你的名字是什么。理想情况下,这应该为每个不同的ID和JOB值组合来自第一行和最后一行的数据。如果作业未完成,则不会显示END_DATE。

答案 2 :(得分:0)

我不认为你的UNION想法有多大问题。这就是你拥有的吗?

选择ID,工作,状态,最长(日期),工作,注意,&#39; max&#39;作为来自test1 group by job UNION的类型 选择id,job,status,min(date),job,note,&#39; min&#39;作为test1 group by job的类型;