SQL SERVER SELECTING ROW [关闭]

Posted

技术标签:

【中文标题】SQL SERVER SELECTING ROW [关闭]【英文标题】:SQL SERVER SELECTING ROW [closed] 【发布时间】:2013-01-29 07:04:51 【问题描述】:

如果我在下面有这张表

   id         time
   start      08.00 AM 
   stop       09.00 AM
   stop       09.30 AM
   start      09.30 AM
   start      11.00 AM
   start      11.30 AM
   stop       11.40 AM
   stop       12.00 PM

我希望输出只包含每个第一次启动停止后的每次启动最后一次停止

有什么办法吗?

这就是我想要的输出:

   id         time
   start      08.00 AM 
   stop       09.00 AM
   start      09.30 AM
   stop       11.40 AM
   stop       12.00 PM

【问题讨论】:

我完全是新手,这就是为什么我不知道我应该做什么。请给点建议。 请给出一些解决方案 从那个表中,没有像标识符id这样的其他列吗? 标识符id只有start和stop,它是从0 == stop和1 == start转换而来的 您如何确定stop, 09.30 AM 出现在start, 09.30 AM 之前而不是相反? start, 11.30 AMstop, 11.30 AM 也是如此。没有任何内容(至少,您没有披露任何内容)表明应该解释行的特定顺序。 【参考方案1】:

如果每一行都有任何主键或不同的 id ......我想到的唯一解决方案是:

select rn, id , time
from
(select ROW_NUMBER() over (order by time) as rn, id, time from test 
where id = 'start'
union
select ROW_NUMBER() over (order by time) as rn, id, time from test 
where id = 'stop'
) d
order by rn

基本上我在开始行和停止行之间建立了一个联合,如下所示:

 (select ROW_NUMBER() over (order by time) as rn, id, time from test 
    where id = 'start'
    union
    select ROW_NUMBER() over (order by time) as rn, id, time from test 
    where id = 'stop'
    ) d

返回:

    1   start   08.00
    2   start   11.00
    3   start   12.00
    4   start   13.00
    1   stop    09.00
    2   stop    10.00
    3   stop    14.00
    4   stop    15.00

来自原始输入:

id          time
start   08.00
stop    09.00
stop    10.00
start   11.00
start   12.00
start   13.00
stop    14.00
stop    15.00

现在您只需按它们自己的行号对它们进行排序...就是那个 rn。

最后你会得到:

1   start   08.00
1   stop    09.00
2   start   11.00
2   stop    10.00
3   start   12.00
3   stop    14.00
4   start   13.00
4   stop    15.00

@注意:我的示例值与您的接近...但是是虚构的...

【讨论】:

您能解释一下吗?对不起,我只是新手。谢谢 没有太多要解释的...我只是使用 ROW_NUMBER() 函数来创建某种 id...所以我可以安排这些行 请在处理之前给我看看你的桌子好吗?请看我上面的帖子,我认为这不是我想要的输出。 我已经编辑了帖子... 如何按自己的行号排序?再次抱歉,我只是新手【参考方案2】:
 ;WITH cte AS
 (
  SELECT id AS pr, [time], ROW_NUMBER() OVER(ORDER BY [time]) AS Id,
         COUNT(*) OVER() AS cnt        
  FROM dbo.test21 t1
  ), cte2 AS
 (
  SELECT Id, pr, [time], pr AS prStart, [time] AS StopTime, pr AS prStop, [time] AS StartTime
  FROM cte
  WHERE Id = 1
  UNION ALL
  SELECT c1.Id, 
         CASE WHEN c1.pr != c2.pr OR c1.Id = c1.cnt THEN c1.pr ELSE c2.pr END,
         CASE WHEN c1.pr != c2.pr OR c1.Id = c1.cnt THEN c1.[time] ELSE c2.[time] END,
         c1.pr, c1.[time], c2.pr, c2.[time]
  FROM cte c1 JOIN cte2 c2 ON c1.Id = c2.Id + 1
  )
  SELECT pr, [time], MIN(StartTime) AS StartTime,
         MAX(DATEDIFF(minute, StartTime, [time])) AS Interval
  FROM cte2
  GROUP BY pr, [time]

SQLFiddle上的演示

【讨论】:

能否请您添加一些间隔时间列?所以它看起来像下面的 Jacco 发布的内容。请。 谢谢你的帖子正是我想要的 很抱歉,您能否将时间间隔列做成这样? sqlfiddle.com/#!3/0079a/7 为什么第一站要等到09.30? 添加了时间间隔列。试试【参考方案3】:

我有一些替代方案,它会以不同的格式给出结果。但它确实返回了请求的信息。

SELECT
  MIN([main].[Start]) AS [Start],
  [main].[End] AS [Stop],
  DATEDIFF(minute, MIN([main].[Start]), [main].[End]) AS [Minutes]
FROM
(
  SELECT
    [sub].[Start],
    MIN([sub].[End]) AS [End]
  FROM
  (
    SELECT
      [start].[time] AS [Start],
      [start].[id] AS [StartingStatus],
      [end].[time] AS [End],
      [end].[id] AS [EndingStatus]
    FROM [Log] [start],  [Log] [end]
    WHERE [start].[id] = 'start' 
      AND [start].[time] < [end].[time]
      AND [start].[id] <> [end].[id]
  ) AS [sub]
  GROUP BY
    [sub].[Start],
    [sub].[StartingStatus]
) AS [main]
GROUP BY
  [main].[End]

基本上,它首先选择所有记录where [id] = 'start'。然后它会查找所有具有较晚时间和where [id] &lt;&gt; 'start' 的记录。

在这些记录中,它按每个“开始”分组并确定第一个“停止”时间。然后将这些记录再次分组,以找到每个“停止”时间的第一个“开始”时间。

结果如下所示:

+--------------------------------+--------------------------------+---------+
| START                          | STOP                           | MINUTES |
+================================+================================+=========+
| January, 29 2013 08:00:00+0000 | January, 29 2013 09:00:00+0000 | 60      |
+--------------------------------+--------------------------------+---------+
| January, 29 2013 09:30:00+0000 | January, 29 2013 11:30:00+0000 | 120     |
+--------------------------------+--------------------------------+---------+
| January, 29 2013 11:30:00+0000 | January, 29 2013 12:00:00+0000 | 30      |
+--------------------------------+--------------------------------+---------+

找到了小提琴here。

PS:这个答案是基于我的答案here。

【讨论】:

首站时间应为 90 分钟或直到 09.30。你能改变你的代码,让它给出我想要的结果吗?谢谢 @user2020598:根据你的例子,8:00 之后的第一站是9:00。为什么你希望它成为9:30?此外,您从未说过您想要这样的结果。这只是 Jacco 的建议,您可能会发现这种输出更方便。如果您喜欢这种方式,请接受此答案并提出一个 问题,了解如何更改它以显示不同的结束时间。我说的是一个新问题,因为很多人已经为解决您的原始问题做出了贡献,因此编辑它只是为了适应这种输出是不公平的。 对不起,我想要的是下一次开始时间最后一次停止时间之前的时间下一次开始 我同意@AndriyM 的观点,但我对 OP 目前真正想要的东西感到迷茫。 不不不,我没有迷路!我完全理解,因为我的回答被接受了!【参考方案4】:

经过 cmets 的澄清,这是一个经典的gaps-n-islands 问题。

假设这是 SQL Server 2005+,您可以使用以下方法:

WITH partitioned AS (
  SELECT
    *,
    grp = ROW_NUMBER() OVER (ORDER BY time, CASE id WHEN 'stop' THEN 0 ELSE 1 END)
        - ROW_NUMBER() OVER (PARTITION BY id ORDER BY time)
  FROM atable
)
SELECT
  id,
  time = MIN(time)
FROM partitioned
GROUP BY
  id,
  grp
ORDER BY
  time
;

【讨论】:

能否请您在 sqlfiddle.com 上给我看一下 @user2020598:你能不能把它应用到你的数据上?另一方面,你也可以在 SQL Fiddle 为我准备一个数据样本,然后我会根据你的数据样本调整这个脚本。 sqlfiddle.com/#!3/a268a/1 sqlfiddle.com/#!3/97181/1 @user2020598:感谢您允许我使用其他人的 Fiddles。 :) 好的,给你:#1,#2。你真的不喜欢自己做作业,是吗。 :)

以上是关于SQL SERVER SELECTING ROW [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

计算 Row Wise Sum - Sql server

SQL Server 2005 ROW_NUMBER() 没有 ORDER BY

SQL Server中row_number函数用法介绍

SQL Server - 带有 PARTITION 的 ROW_NUMBER(),如何获取多条记录?

SQL Server:row_number 分区不重置计数器

ROW_NUMBER SQL Server 2005的LIMIT功能实现(ROW_NUMBER()排序函数)