在SQL中,如何基于当前行值选择上一行?

在SQL中,如何基于当前行值选择上一行?,sql,postgresql,window-functions,Sql,Postgresql,Window Functions,我有一个简单的SQL表,如下所示- CREATE TABLE msg ( from_person character varying(10), from_location character varying(10), to_person character varying(10), to_location character varying(10), msglength integer, ts timestamp without time zone

我有一个简单的SQL表,如下所示-

CREATE TABLE msg (
    from_person character varying(10),
    from_location character varying(10),
    to_person character varying(10),
    to_location character varying(10),
    msglength integer,
    ts timestamp without time zone
);
我想找出表中每一行在过去3分钟内是否有不同的“from_person”和“from_location”与当前行中的“to_person”进行了交互

例如,在上表中,对于第4行,除孟买当前行的玛丽外,纽约市的南希和巴塞罗那的鲍勃也在最后3分钟向查理发送了一条消息,因此计数为2

类似地,对于第2行,除了巴塞罗那当前行的bob之外,只有纽约市的nancy向ca当前行的charlie发送了消息,因此计数为1

期望输出示例-

0
1
0
2

我尝试使用窗口函数,但在frame子句中,我可以指定前后的行数,但不能指定时间本身

这应该差不多能做到。根据您的要求,您可能需要修改where子句中的中间两个条件:

select *,
   (select count(*) from msg m2
    where m2.to_person = m1.to_person
        and m2.from_person != m1.from_person
        and m2.from_location != m1.from_location
        and abs(EXTRACT(EPOCH FROM (m1.ts - m2.ts))) <= 3*60)
from msg m1

众所周知,Postgres中的每个表都有一个主键。或者至少应该有。如果您有一个主键来定义预期的行顺序,那就太好了

示例数据:

create table msg (
    id int primary key,
    from_person text,
    to_person text,
    ts timestamp without time zone
);

insert into msg values
(1, 'nancy',   'charlie', '2016-02-01 01:00:00'),
(2, 'bob',     'charlie', '2016-02-01 01:00:00'),
(3, 'charlie', 'nancy',   '2016-02-01 01:00:01'),
(4, 'mary',    'charlie', '2016-02-01 01:02:00');
查询:

select m1.id, count(m2)
from msg m1
left join msg m2
on m2.id < m1.id
and m2.to_person = m1.to_person
and m2.ts >= m1.ts- '3m'::interval
group by 1
order by 1;

 id | count 
----+-------
  1 |     0
  2 |     1
  3 |     0
  4 |     2
(4 rows)
请注意,我已经使用了row_number over order by ts,from_person desc来获得您在问题中所展示的行序列。当然,您应该自己决定如何解决由前两行中相同的列ts值引起的歧义。

根据您的实际问题,这将是一个正确的答案:

SELECT count(m2.to_person) AS ct_3min
FROM   msg m1
LEFT   JOIN msg m2
   ON   m2.to_person = m1.to_person
   AND (m2.from_person, m2.from_location) <> (m1.from_person, m1.from_location)
   AND  m2.ts <= m1.ts   -- including same timestamp (?)
   AND  m2.ts >= m1.ts - interval '3 min'
GROUP  BY m1.ctid
ORDER  BY m1.ctid;
请注意,如果没有额外的列、任何列的唯一组合(理想情况下是PK),结果基本上没有意义。我返回当前物理顺序中的行-可以在没有警告的情况下随时更改。关系表中没有自然的行顺序。如果没有明确的ORDERBY子句,结果行的顺序是不可靠的

根据您的定义,根据显示顺序的前两行需要具有相同的结果:如果不计算相同的时间戳,则1-或0-根据您的定义,一行为0,另一行为1是不正确的

在没有任何唯一密钥的情况下,我将使用作为穷人的代理密钥。更多:

您仍然应该在表中定义一个主键,但这绝不是必须的。这不是表格布局中唯一可疑的细节。您可能应该使用带有时区的时间戳进行操作,具有一些非空约束,并且在正确规范化的设计中,只有person_id列引用person表。比如:

CREATE TABLE msg (
   msg_id         serial PRIMARY KEY
 , from_person_id integer NOT NULL REFERENCES person
 , to_person_id   integer NOT NULL REFERENCES person
 , msglength      integer
 , ts             timestamp with time zone
);

无论哪种方式,依赖代理PK进行查询都是完全错误的。下一个msg_id甚至不必有更晚的时间戳。在多用户数据库中,序列不能保证任何排序。

如果以插入的形式提供样本数据,会更容易。当您说“最后3分钟”时,是指当前时间的最后3分钟,还是记录上的时间戳?@TimJasko我的意思是记录的最后3分钟。在这种情况下,你应该从我的答案中删除abs,目前在两个方向上都是3分钟。将“nancy”算作“bob”是没有意义的,反之亦然。结果的前两行应该是0,0或1,1,但不是0,1。请以文本形式提供数据,切勿以图像形式提供。始终是您的Postgres版本。我从第7行:from msg;^中得到错误-错误:语法错误查询失败PostgreSQL表示:From处或附近的语法错误错过了结束语谢谢。我现在得到一个错误-错误:函数sum不存在第2行:从msg m2中选择sum*。像这样依赖代理PK的排序顺序是不正确的。解决方案中没有表示不同的“from_person”和“from_location”。谢谢,我添加了一些解释。
1   -- !!
1
0
2
CREATE TABLE msg (
   msg_id         serial PRIMARY KEY
 , from_person_id integer NOT NULL REFERENCES person
 , to_person_id   integer NOT NULL REFERENCES person
 , msglength      integer
 , ts             timestamp with time zone
);