PL/pgSQL:比较连续的行

PL/pgSQL: Comparing Successive Rows

我有下面的函数 get_untracked_moves。我的目标是,对于两个日期范围之间的所有数据,找到相距超过 p_separation_distance 的连续事件。

例如:

如果 p_separation_distance 为 100 米时事件 1 和事件 2 相距 40 米,则将返回一条记录,其中事件 1 的关联 cont_name 作为 source_name 和事件 2 的 cont_name 作为 target_name.

CREATE FUNCTION get_untracked_moves(IN p_since_date TIMESTAMP WITHOUT TIME ZONE, IN p_before_date TIMESTAMP WITHOUT TIME ZONE, IN p_separation_distance INTEGER)
    RETURNS TABLE ( id INTEGER,
            asset_name CHARACTER VARYING,
            source_name CHARACTER VARYING,
            target_name CHARACTER VARYING,
            source_time TIMESTAMP WITHOUT TIME ZONE,
            target_time TIMESTAMP WITHOUT TIME ZONE,            
            source_lat DOUBLE PRECISION,
            source_lon DOUBLE PRECISION,            
            target_lat DOUBLE PRECISION,
            target_lon DOUBLE PRECISION ) AS $$

    DECLARE     
        d_previous_location GEOMETRY;
        d_previous_name CHARACTER VARYING;
        d_previous_time TIMESTAMP WITHOUT TIME ZONE;

        d_cur record;
    BEGIN
         -- Begin @ 0,0
         d_previous_location := st_setsrid(st_makepoint(0,0), 4326);
        d_previous_name := '';
        d_previous_time := NULL;

        FOR    d_cur
        IN
            SELECT
                rank() OVER (PARTITION BY events.asset_id ORDER BY events.event_time) AS idx,
                tags.id asset_id,
                tags.name asset_name,
                d_previous_name,
                conts.name cont_name,
                events.position,
                events.event_time evt_time

            FROM 
                events
            JOIN 
                assets tags ON tags.id = events.asset_id
            JOIN 
                assets conts ON conts.id = events.container_asset_id
            WHERE
                events.event_time >= p_since_date
            AND
                events.event_time <= p_before_date
        LOOP
                 IF (d_previous_time = NULL) THEN
                    d_previous_time :=  events.event_time;
                 END IF;

                IF (st_distancesphere(events.position, d_previous_location)>=p_separation_distance) THEN
                    RETURN NEXT;
                END IF;

                d_previous_location := events.position;
                d_previous_name := conts.name;
                d_previous_time :=  events.event_time;

        END LOOP;   
    END;
    $$
LANGUAGE plpgsql VOLATILE;

该函数创建得很好,但是当我转到 运行 它时:

select * from get_untracked_moves('2015-11-1', '2015-12-1', 10000);

我得到:

ERROR:  missing FROM-clause entry for table "events"
LINE 1: SELECT (st_distancesphere(events.position, d_previous_locati...
                                  ^
QUERY:  SELECT (st_distancesphere(events.position, d_previous_location)>=p_separation_distance)
CONTEXT:  PL/pgSQL function "get_untracked_moves" line 41 at IF

********** Error **********

ERROR: missing FROM-clause entry for table "events"
SQL state: 42P01
Context: PL/pgSQL function "get_untracked_moves" line 41 at IF

我在这里错过了什么?我认为在我的 SELECT 语句中包含 FROM events 就足够了。

循环的每次传递都被赋予包含 select 结果集的相应行的记录的值。所以 events 在循环内是不可见的。相反,使用 d_cur.position 来引用该列。

顺便说一句,正如对你的问题的评论,你真的应该使用 lag window 函数并摆脱混乱的循环。

作为建议检查此查询:

select idx, asset_id, asset_name, previous_name, cont_name, position, evt_time
from (
    select
        rank() over (partition by e.asset_id order by e.event_time) as idx,
        st_distancesphere(
            e.position,
            lag(e.position, 1, e.position) over (order by e.event_time)
        ) >= p_separation_distance as b,
        t.id as asset_id,
        t.name as asset_name,
        lag(c.name, 1) as previous_name,
        c.name as cont_name,
        e.position,
        e.event_time as evt_time
    from 
        events e
        inner join 
        assets tags on t.id = e.asset_id
        inner join 
        assets c on c.id = e.container_asset_id
    where
        e.event_time >= p_since_date
        and
        e.event_time <= p_before_date
) s
where b