SQL从定义了偏差的每个集合中获取n个结果
使用Oracle 11g并具有如下表:SQL从定义了偏差的每个集合中获取n个结果,sql,oracle,oracle11g,Sql,Oracle,Oracle11g,使用Oracle 11g并具有如下表: USER | TIME ----- | -------- User1 | 08:15:50 User1 | 10:42:22 User1 | 10:42:24 User1 | 10:42:35 User1 | 10:50:01 User2 | 13:23:05 User2 | 13:23:34 User2 | 13:24:01 User2 | 13:24:02 对于每个用户,如果可用,我需要获得3条记录,第一次和最后一次之间的偏差不到一分钟。如果行数超
USER | TIME
----- | --------
User1 | 08:15:50
User1 | 10:42:22
User1 | 10:42:24
User1 | 10:42:35
User1 | 10:50:01
User2 | 13:23:05
User2 | 13:23:34
User2 | 13:24:01
User2 | 13:24:02
对于每个用户,如果可用,我需要获得3条记录,第一次和最后一次之间的偏差不到一分钟。如果行数超过3,则它们将不符合条件。你能给我一些线索吗
结果应该如下所示:
User1 | 10:42:22
User1 | 10:42:24
User1 | 10:42:35
这是我的想法。我没有实时Oracle,SQLFiddle无法工作,因此请告知结果:
CREATE TABLE t (
u VARCHAR(5),
t DATETIME
);
INSERT INTO t
(u, t)
VALUES
('User1', '2001-01-01 08:15:50'),
('User1', '2001-01-01 10:42:22'),
('User1', '2001-01-01 10:42:24'),
('User1', '2001-01-01 10:42:35'),
('User1', '2001-01-01 10:50:01'),
('User2', '2001-01-01 13:23:05'),
('User2', '2001-01-01 13:23:34'),
('User2', '2001-01-01 13:24:01'),
('User2', '2001-01-01 13:24:02');
SELECT
z.u,
min(z.t) evt_start,
max(z.t) evt_end
FROM
(
SELECT y.*, SUM(prev_or_2prev_not_within) OVER(PARTITION BY u ORDER BY t ROWS UNBOUNDED PRECEDING) as ctr
FROM
(
SELECT
t.*,
CASE WHEN
t - LAG(t) OVER(PARTITION BY u ORDER BY t) < 1.0/1440.0 OR
t - LAG(t, 2) OVER(PARTITION BY u ORDER BY t) < 1.0/1440.0
THEN 0 ELSE 1
END as prev_or_2prev_not_within
FROM
t
) y
) z
GROUP BY
z.u,
z.ctr
HAVING COUNT(*) = 3
我相信它将建立一个递增计数器,当前一行或前一行在当前行的一分钟内时,该计数器不会递增。它通过将行分类为0或1来实现这一点,当出现0时,“对所有前面的行求和”操作将生成一个不变的计数器。然后在该计数器上分组,正好出现3次。分区使计数器对每个用户有效
你可以在这里看到它的作用:
如前所述,这是SQL Server,我没有oracle,但SQL Server使用的术语和oracle的逻辑应该大致相似-oracle支持滞后、无界和等,并且它使用dateA-dateB->一个代表一天全部或部分的浮点数和每天1440分钟进行日期计算,1/1440应代表一分钟的浮动。sqlserver使用的数据类型可能与oracle略有不同,此查询取决于我称之为t的时间-不喜欢保留字/关键字的列名列是日期时间,而不是看起来像时间的字符串。如果数据是字符串,请对其进行排序,使其不使用内部子查询生成日期时间,或者更改数据存储,使其存储为日期时间类型
你说你想要一个告诉用户和事件时间的结果——最简单的方法是使用min和max来给出日期范围。如果您非常希望显示所有3行,则可以将此查询的输出连接回evt_start和evt_end之间的表,或者使用某种字符串聚合类型函数,为您提供最外层组操作的时间列表我将使用带范围子句的分析计数:
结果:
USER_ TIME_
----- --------
User1 10:42:22
User1 10:42:24
User1 10:42:35
编辑:
正如@CaiusJard所注意到的,当间隔为10:52:01、10:53:00、10:53:59时,第一个答案可能会显示不正确的值。有一些方法可以纠正这一点。首先是找到小组中的最小和最大时间,并检查条件numtodserval max-min,“day”对不起,这里有点新。我正在使用添加到描述中的oracle 11g。如果给定用户在一分钟内拥有多组3条记录,会发生什么情况?在这种情况下会发生什么?当一分钟内有4条记录时会发生什么?你想要a,b,c还是b,c,d或两者都要?所有的组都必须列出,即使它不止一个。如果我们有10:42:30和10:43:25和10:44:20会怎么样?时间超过1分钟,但间隔不到1分钟。谢谢。工作起来很有魅力。巧妙的技巧,我不知道Oracle可以根据当前行值设置范围。我想这会有几个bug。。请参见-根据Angel的规则,此结果集中不应有任何行。考虑把整个事情在一个组中通过计数=3?谢谢。你是对的。我修改了答案并在两个样本数据集上进行了测试,您的数据集没有返回,正如预期的那样,还有一个稍大一些。谢谢我需要一些时间来运行它,因为oracle和mssql之间的数据类型不同,正如您上面提到的,实际上需要一些时间来了解其背后的全部想法:但这是一个非常有趣和迅速的决定。如果答案有用,请使用^箭头进行投票。您可以对任意数量的答案执行此操作,但绿色勾号只能用于一个答案
USER_ TIME_
----- --------
User1 10:42:22
User1 10:42:24
User1 10:42:35
with t as (
select row_number() over (order by user_, time_) rn, tbl.*,
count(1) over (partition by user_ order by time_
range between interval '1' minute preceding
and interval '1' minute following) cnt
from (select user_, to_date(time_, 'hh24:mi:ss') time_ from tbl) tbl),
r as (select rn,
case when 3 = lag(cnt) over (partition by user_ order by time_)
and 3 = cnt
and 3 = lead(cnt) over (partition by user_ order by time_)
then 1
end flag
from t )
select * from t
join (select rn-1 r1, rn r2, rn+1 r3 from r where flag = 1) r
on rn in (r1, r2, r3)