【问题标题】:SQL Query: how to return only the first and last instance?SQL 查询:如何只返回第一个和最后一个实例?
【发布时间】:2015-07-22 20:20:32
【问题描述】:

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

我已经设法通过 UNION 函数连接的 2 个类似的 min、max 和 group by 查询获得结果。但是有没有更简单的方法?

但是,是否可以将 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

非常感谢您

【问题讨论】:

  • 你能更清楚地解释你需要什么吗? “只显示第一个和最后一个实例”?按什么? ID?工作?如果您的第二个数据样本是期望的结果,我不明白您将如何从第一个样本中的数据中获得这些结果。
  • 每个作业和 ID。例如,作业 A 和 B 都已分配给 ID1,作业 A 于 7 月 1 日开始,我们对 ID 1 下的 A 的最后记录是 7 月 4 日。所以我们要显示第一个表的第一行和第 5 行。我希望这更清楚

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


【解决方案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)您有一个小表,其中包含所有IDs 和@ 987654326@s, (c) 主表有合适的索引。在这种情况下,您可以为每个 (ID, Job) 进行索引查找,而不是读取主表的所有行(两次查找,第一行一次,最后一行一次)。

【讨论】:

  • 就像一个魅力,它比我原来的联合查询快得多。谢谢!
【解决方案2】:

试试这个:

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

您需要将 替换为您的表名。理想情况下,这应该为每组不同的 ID 和 JOB 值组合第一行和最后一行的数据。如果作业未完成,则不会显示 END_DATE。

【讨论】:

  • hmm,它给了我 3 个错误,1) 第 1 行关键字 'as' 附近的语法不正确,2) 第 3 行的 'B' 附近的语法不正确,以及 3) 'C' 附近的语法不正确在第 6 行
  • 第一行的NULL后面需要有一个END。你能告诉我你试图运行的确切结构吗?我发布的答案是伪代码,因为我不知道您的表的确切名称等。
  • 根据您使用的数据库,您可能需要在 ) 之后、B 之前和 ) 之后以及 C 之前有一个 AS
【解决方案3】:

我认为您的 UNION 想法没有太大问题。这是你的吗?

从 test1 group by job UNION 中选择 id、job、status、max(date)、job、note、'max' 作为类型 选择 id, job, status, min(date), job, note, 'min' as type from test1 group by job;

【讨论】:

  • 是的,但我正在寻找一种更有效的方法:)
  • 确保在日期列上有索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-12
  • 1970-01-01
  • 1970-01-01
  • 2018-10-20
  • 2021-10-07
相关资源
最近更新 更多