【问题标题】:SELECT multiple rows where date Is greater than X minutes of previous row选择多行,其中日期大于前一行的 X 分钟
【发布时间】:2013-01-23 18:55:15
【问题描述】:

我需要SELECT 表中的所有行,其中所选行比先前选择的行的日期时间大给定的恒定分钟数。举个例子可能是最好的。

下面是数据表——我们称之为myTable。

guid     fkGuid   myDate
-------  -------  ---------------------
1        100      2013-01-10 11:00:00.0
2        100      2013-01-10 11:05:00.0
3        100      2013-01-10 11:10:00.0
4        100      2013-01-10 11:15:00.0
5        100      2013-01-10 11:20:00.0
6        100      2013-01-10 11:25:00.0
7        100      2013-01-10 11:30:00.0
8        100      2013-01-10 11:35:00.0
9        100      2013-01-10 11:40:00.0
10       100      2013-01-10 11:50:00.0
11       100      2013-01-10 11:55:00.0

我想要做的是提供一个恒定的增量(比如 10 分钟),并从第一行取回距离前一行 10 分钟或更长时间的所有行。因此,10 分钟后,结果集应如下所示:

guid     myDate
-------  ---------------------
1        2013-01-10 11:00:00.0
3        2013-01-10 11:10:00.0
5        2013-01-10 11:20:00.0
7        2013-01-10 11:30:00.0
9        2013-01-10 11:40:00.0
11       2013-01-10 11:55:00.0

常量作为变量传入,所以它可以是任何东西。假设是 23 分钟,那么结果集应该是这样的:

guid     myDate
-------  ---------------------
1        2013-01-10 11:00:00.0
6        2013-01-10 11:25:00.0
10       2013-01-10 11:50:00.0

最后一个示例显示我从第 0 行的时间 (11:00:00) 开始添加 23 分钟并获得下一个 >= 行,即 11:25:00,将 23 分钟添加到新行的时间,然后获得下一个(11:50:00)等等。

我曾尝试使用 CTE 执行此操作,但尽管我可以很容易地找回所有时间或一个都没有,但我似乎无法弄清楚如何获得我需要的行。我当前的测试代码使用 23 分钟硬编码到 WHERE 子句中:

WITH myCTE AS
(
    SELECT guid,
           myDate,
           ROW_NUMBER() OVER (PARTITION BY guid ORDER BY myDate ASC) AS rowNum
    FROM myTable
    WHERE fkGuid = 100
)

SELECT currentRow.guid, currentRow.myDate
FROM myCTE AS currentRow
LEFT OUTER JOIN
    myCTE AS previousRow
    ON currentRow.guid = previousRow.guid
    AND currentRow.rowNum = previousRow.rowNum + 1
WHERE
    currentRow.myDate > DATEADD(minute, 23, previousRow.myDate)
ORDER BY
    currentRow.myDate ASC

这不返回任何内容。如果我省略 WHERE 子句,我会返回所有行(显然是因为我没有过滤)。

我错过了什么?

我们将一如既往地非常感谢任何和所有帮助!

【问题讨论】:

    标签: database sql-server-2008 tsql datetime select


    【解决方案1】:

    @gilly3,几乎没有 SQL 巫术

    WITH CTE
    AS
    (
      SELECT TOP 1
             guid 
            ,fkGuid
            ,myDate
            ,ROW_NUMBER() OVER (ORDER BY myDate) RowNum
      FROM MyTable
      UNION ALL
      SELECT mt.guid
            ,mt.fkGuid
            ,mt.myDate
            ,ROW_NUMBER() OVER (ORDER BY mt.myDate)
      FROM  MyTable mt
            INNER JOIN
            CTE ON mt.myDate>=DATEADD(minute,23,CTE.myDate)
      WHERE RowNum=1
    )
    SELECT guid
           ,fkGuid
           ,myDate
    FROM   CTE
    WHERE  RowNum=1
    

    SQL Fiddle 是 here

    【讨论】:

    • 戴尔 M - 才华横溢。谢谢你的巫毒教 :-) 也不知道 SQL Fiddle - 真是一个启示!也非常感谢您的快速响应!
    • @Hooligancat 感谢您的反馈 - 你让我度过了一个愉快的夜晚
    • @DaleM - 不,这绝对是 SQL 巫术! :) 您正在从它自己的定义中加入一个公用表表达式!我什至不明白它是如何工作的。这是一个非常酷的查询。 但是, 我必须指定 OPTION(MAXRECURSION 10000) 才能让它在我的数据库上运行,并且运行需要 13.5 分钟。相反,仅选择所有数据并在代码中对其进行过滤就需要 300 毫秒。很酷的查询,但不实用。
    • @gilly3 mydate 上有一个索引,不是吗?
    【解决方案2】:

    首先,无论where 子句如何,您的联接都不会返回任何行。 Guid 和 rowNum 都是每行的唯一键,因此如果 guid 相同,则 rowNum 也相同。您可以通过将previousRow 中的字段添加到您的选择列表并在没有where 子句的情况下运行查询来看到连接总是失败。

    接下来,加入rowNum + 1 可防止跳过行。您将只选择满足日期过滤器的相邻行。

    可能有一些带有递归查询的 SQL 巫术可以使这项工作发挥作用,但会对性能造成巨大影响。过滤应用程序代码中的数据。例如,在 C# 中:

    List<DataRow> FilterByInterval(IEnumerable<DataRow> rows, string dateColumn, int minutes)
    {
        List<DataRow> filteredRows = new List<DataRow>();
        DateTime lastDate = DateTime.MinValue;
        foreach (DataRow row in rows)
        {
            DateTime dt = row.Field<DateTime>(dateColumn);
            TimeSpan diff = dt - lastDate;
            if (diff.TotalMinutes >= minutes)
            {
                filteredRows.Add(row);
                lastDate = dt;
            }
        }
        return rows;
    }
    

    【讨论】:

    • gilly3 - 我已经开始了另一条路线(查看光标等) - 看起来 Dale M 今天启发了我们俩!不过感谢您这么快就加入 - 非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多