对非唯一值进行分区

Posted

技术标签:

【中文标题】对非唯一值进行分区【英文标题】:Partitioning on non-unique values 【发布时间】:2020-09-02 21:48:38 【问题描述】:

我有一个表格,列出了事件、事件中的操作以及每个操作的时间。事件 ID 不是唯一的,因为它是同一个事件,只是发生在不同的时间。对于相同类型的事件,操作可能会有所不同。同一个事件永远不会连续运行两次)。

我想根据给定的示例填充三个新列。这将允许我对单独的事件进行分析,因为我将能够生成一个唯一的“事件”ID。

编辑: 我已经尝试过基于事件的 PARTITION 函数,但它没有工作,因为 SQL 服务器假定两个事件(A 和 B),因此为所有“A”事件提供相同的开始日期,即使实际上我需要将它们显示为单独的不同开始日期的活动。

谢谢!

【问题讨论】:

“我已经尝试过 PARTITION 功能” - 告诉我们! 刚刚滚动我的结果表并意识到即使我的行号提供功能也不能像上面评论的那样工作。由于非唯一事件 ID,SQL 服务器将其分为两组,而我需要识别 3 个单独的事件。 【参考方案1】:

这只是窗口函数:

select t.*,
       min(operationtime) over (partition by event) as event_start_time,
       max(operationtime) over (partition by event) as event_end_time,
       concat(event, '-', min(operationtime) over (partition by event)) as event_id
from t;

实际上,对于事件 id,您可能想要这样的内容:

       concat(event, '-', convert(varchar(255), min(operationtime) over (partition by event), 101)) as event_id

或任何您真正想要的日期格式。我推荐 YYYY-MM-DD 作为日期格式。

【讨论】:

您好,谢谢您的回答。不幸的是,它不起作用。已经试过了。由于非唯一事件 ID - SQL 服务器将其划分为两组数据(A 和 B)。但是,我需要查看三个事件(A、B、A)以及单独的开始日期。建议的解决方案将第一个事件的开始日期提供给事件 1 和 3,因为它们共享相同的 ID。【参考方案2】:

我将此理解为一个差距和孤岛问题,您希望在其中构建连续的日常事件组。

一个选项使用行号之间的差异来识别组:

select 
    t.*,
    min(operation_time) over(partition by event, rn1 - rn2) event_start_time,
    max(operation_time) over(partition by event, rn1 - rn2) event_end_time,
    concat(event, '-', min(operation_time) over(partition by event, rn1 - rn2)) event_id
from (
    select 
        t.*, 
        row_number() over(order by operation_time) rn1,
        row_number() over(partition by event order by operation_time) rn2
    from mytable t
) t
order by operation_time

如果每天总是只有一个事件,如您的示例数据所示,那么一个 row_number() 以及日期算术就足够了:

select
    t.*,
    min(operation_time) over(partition by event, grp) event_start_time,
    max(operation_time) over(partition by event, grp) event_end_time,
    concat(event, '-', min(operation_time) over(partition by event, grp)) event_id
from (
    select 
        t.*, 
        dateadd(
            day,
            - row_number() over(partition by event order by operation_time),
            operation_time
        ) grp
from mytable t
) t

【讨论】:

谢谢 - 将尝试第一个解决方案。第二个不起作用,不幸的是,同样的事件可能在同一天发生。只是试图给出一个更简化的实际问题示例。唯一的可取之处是它们不能一个接一个地运行。 谢谢。我在尝试实现代码时确实遇到了问题。但这可能只是我。非常感谢你给我的问题一个“行业标准名称”——gaps-and-island。没有意识到这一点。【参考方案3】:

此方法显式创建事件组,然后使用与其他答案非常相似的窗口查询。我创建了一个简单的示例表来显示结果。

数据

drop table if exists #tTEST;
go
select * INTO #tTEST from (values 
('A', 'X', '2020-01-08'),
('A', 'Z', '2020-02-08'),
('B', 'X', '2020-03-08'),
('B', 'Z', '2020-04-08'),
('A', 'X', '2020-05-08'),
('A', 'Z', '2020-06-08')) V([Event], [Operation], operation_time);

查询

;with
grp_cte as (
    select t.*, case when lag([Event], 1, 0) over (order by operation_time) != [Event] then 1 else 0 end grp_ind
    from #tTEST t),
event_grp_cte as (
    select gc.*, sum(grp_ind) over (order by operation_time) EventGroup
    from grp_cte gc)
select 
    t.*,
    min(operation_time) over(partition by EventGroup) event_start_time,
    max(operation_time) over(partition by EventGroup) event_end_time,
    concat(event, '-', min(operation_time) over(partition by EventGroup)) event_id
from event_grp_cte t
order by operation_time;

结果

Event   Operation   operation_time  grp_ind EventGroup  rn1 rn2 event_start_time    event_end_time  event_id
A       X       2020-01-08  1   1   1   1   2020-01-08  2020-02-08  A-2020-01-08
A       Z       2020-02-08  0   1   2   2   2020-01-08  2020-02-08  A-2020-01-08
B       X       2020-03-08  1   2   3   1   2020-03-08  2020-04-08  B-2020-03-08
B       Z       2020-04-08  0   2   4   2   2020-03-08  2020-04-08  B-2020-03-08
A       X       2020-05-08  1   3   5   3   2020-05-08  2020-06-08  A-2020-05-08
A       Z       2020-06-08  0   3   6   4   2020-05-08  2020-06-08  A-2020-05-08

【讨论】:

效果很好 - 非常感谢!我确实遇到过代码损坏的情况,但这是由于额外的复杂性。超级整洁的解决方案。谢谢!

以上是关于对非唯一值进行分区的主要内容,如果未能解决你的问题,请参考以下文章

VMware下Linux根分区磁盘扩容

Oracle 分区和本地 Indexex (12c)

用DiskGenius怎么对硬盘分区

Oracle:更新非唯一字段

开机状态下对磁盘进行分区,分区工具disktooldiskgenuis.cn

在Linux 环境上对磁盘进行分区操作