按邻近时间戳划分的SQL查询组

按邻近时间戳划分的SQL查询组,sql,sql-server,group-by,Sql,Sql Server,Group By,我有一个带有时间戳列的表。我希望能够按标识符列(如cusip)分组,在另一列(如数量)上求和,但仅限于彼此相隔30秒的行,即不在固定的30秒桶间隔内。鉴于数据: cusip| quantity| timestamp ============|=========|============= BE0000310194| 100| 16:20:49.000 BE0000314238| 50| 16:38:38.110 BE0000314238| 50| 16:4

我有一个带有时间戳列的表。我希望能够按标识符列(如cusip)分组,在另一列(如数量)上求和,但仅限于彼此相隔30秒的行,即在固定的30秒桶间隔内。鉴于数据:

cusip| quantity| timestamp ============|=========|============= BE0000310194| 100| 16:20:49.000 BE0000314238| 50| 16:38:38.110 BE0000314238| 50| 16:46:21.323 BE0000314238| 50| 16:46:35.323 cusip |数量|时间戳 ============|=========|============= BE0000310194 | 100 | 16:20:49.000 BE0000314238 | 50 | 16:38:38.110 BE0000314238 | 50 | 16:46:21.323 BE0000314238 | 50 | 16:46:35.323 我想编写一个查询,返回:

cusip| quantity ============|========= BE0000310194| 100 BE0000314238| 50 BE0000314238| 100 cusip |数量 ============|========= BE0000310194 | 100 BE0000314238 | 50 BE0000314238 | 100 编辑:
此外,如果我还可以从查询中获取MIN(时间戳),这将大大简化工作。

以下内容可能会对您有所帮助

一组30秒的周期,表示给定时间的形式。这里是“2012-01-01 00:00:00”。DATEDIFF统计时间戳值和声明时间之间的秒数。然后将其除以30得到分组列

SELECT MIN(TimeColumn) AS TimeGroup, SUM(Quantity) AS TotalQuantity FROM YourTable
GROUP BY (DATEDIFF(ss, TimeColumn, '2012-01-01') / 30)

在这里,每个组的最小时间戳将作为时间组输出。但您可以使用最大值或甚至分组列值可以再次转换为时间来显示。

查看上述注释,我假设Chris的第一个场景就是您想要的场景(尽管值1和值3彼此之间的间隔不在30秒内,但每个值都在值2的间隔30秒内,但所有3个都会被分组)。还要假设表中的每一行都有一个称为“ID”的唯一ID。您可以执行以下操作:

  • 创建新分组,确定分区中的前一行是否比当前行落后30秒以上(例如,确定是否需要新的30秒分组,或继续上一个分组)。我们称之为家长id
  • 总数量超过父项id(加上任何其他聚合)
  • 代码可能是这样的

    select
        sub.parent_id,
        sub.cusip,
        min(sub.timestamp) min_timestamp,
        sum(sub.quantity) quantity
    from 
        (
            select
                base_sub.*,
                case
                    when base_sub.self_parent_id is not null
                    then base_sub.self_parent_id
                    else lag(base_sub.self_parent_id) ignore nulls over (
                        partition by
                            my_table.cusip
                        order by
                            my_table.timestamp,
                            my_table.id
                        ) parent_id
            from 
                (
                    select
                        my_table.id,
                        my_table.cusip,
                        my_table.timestamp,
                        my_table.quantity,
                        lag(my_table.timestamp) over (
                            partition by
                                my_table.cusip
                            order by
                                my_table.timestamp,
                                my_table.id
                            ) previous_timestamp,
                        case
                            when datediff(
                                second, 
                                nvl(previous_timestamp, to_date('1900/01/01', 'yyyy/mm/dd')),
                                my_table.timestamp) > 30
                            then my_table.id
                            else null
                        end self_parent_id
                    from
                        my_table
                ) base_sub
        ) sub
    group by
        sub.time_group_parent_id,
        sub.cusip
    

    从Sean G解决方案中,我删除了完整表上的Group By。事实上,为Oracle SQL重新调整了几个部分

    在找到上一次之后,首先分配自父id。如果在上一次中有空值,那么我们排除给它一个id

    现在基于通过避免空值获取最近的自父id,以便所有最近的30秒cusip都属于一个组

    由于有一个CUSIP列,我假设数据集将是大型市场交易数据。在完整的表上使用GROUPBY,而使用CUSIP分区和最终的组父ID,以获得更好的性能

    SELECT
    id,
    sub.parent_id,
    sub.cusip,
    timestamp,
    quantity,
    sum(sub.quantity) OVER(
        PARTITION BY cusip, parent_id
    ) sum_quantity,
    MIN(sub.timestamp) OVER(
        PARTITION BY cusip, parent_id
    ) min_timestamp
    FROM
    (
        SELECT
            base_sub.*,
            CASE
                WHEN base_sub.self_parent_id IS NOT NULL THEN
                    base_sub.self_parent_id
                ELSE
                    LAG(base_sub.self_parent_id) IGNORE NULLS OVER(
                        PARTITION BY cusip
                        ORDER BY
                            timestamp, id
                    )
            END parent_id
        FROM
            (
                SELECT
                    c.*,
                    CASE
                        WHEN nvl(abs(EXTRACT(SECOND FROM to_timestamp(previous_timestamp, 'yyyy/mm/dd hh24:mi:ss') - to_timestamp
                        (timestamp, 'yyyy/mm/dd hh24:mi:ss'))), 31) > 30 THEN
                            id
                        ELSE
                            NULL
                    END self_parent_id
                FROM
                    (
                        SELECT
                            my_table.id,
                            my_table.cusip,
                            my_table.timestamp,
                            my_table.quantity,
                            LAG(my_table.timestamp) OVER(
                                PARTITION BY my_table.cusip
                                ORDER BY
                                    my_table.timestamp, my_table.id
                            ) previous_timestamp
                        FROM
                            my_table
                    ) c
            ) base_sub
    ) sub
    
    下面是表中的行

    输入数据:

    下面是输出

    结果


    考虑值1:01、1:22、1:45。第一个值和最后一个值都在中间值的30秒内,但彼此之间的距离不在30秒内。这是多少组?一个-它们都被分组在一起(但是外部值之间的间隔不是30秒)?两个-中间值组和两个外部值在30秒内相同(因此计数两次)?在实践中,变量一总是正确的。找出第一个时间戳组的逻辑是什么?这些语句中哪一个是正确的?(a) 当时间戳不晚于组中的最小时间戳30秒(即,组最多可跨越30秒)时,对行进行分组;或者(b)当时间戳不超过组中前一个时间戳的30秒时,对行进行分组(也就是说,组中包含的行数与快速连续出现的行数相同,即使可能跨越30秒)。Chris,在我看来,这就像是对第一个问题的重新表述,答案仍然是一样的,实际上,一个小组从开始到结束的时间最多为30秒。在绝大多数情况下,这实际上只需要几秒钟。但老实说,我想我可以让任何一种变体都起作用。您有针对这两个方面的纯SQL解决方案吗?现在我不得不求助于获取所有条目,并在perl中进行操作以按程序进行分组。由于我在原始帖子中提到的“非30秒桶间隔”,这将不起作用。