【发布时间】:2021-10-20 21:49:28
【问题描述】:
我希望简化一个函数,我必须计算两个事件之间的持续时间。我的数据库有三个在函数中使用的表。
-
事件表 - 此表重新组合所有事件。我们感兴趣的事件是具有 device_type 1 (= entry) 和 device_type 2 (= exit) 的事件
-
DURATIONS TABLE - 这是函数运行时填充的表。它将在事件表中找到具有相同卡号和设备类型 1 和设备类型 2 的前两个事件,计算两个事件之间的持续时间并将其保存在持续时间表中。
-
PROPERTIES - 此表用作参考,并将更新并用于将来的插入。
DDL 脚本
-- events
CREATE TABLE IF NOT EXISTS events (
id bigint NOT NULL autoincrement start 1 increment 1 PRIMARY KEY,
odb_created_at timestamp without time zone NOT NULL,
event_time timestamp without time zone NOT NULL,
device_type integer NOT NULL,
event_type integer NOT NULL,
ticket_type integer NOT NULL,
card_nr character varying(100),
count integer DEFAULT 1 NOT NULL,
manufacturer character varying(200),
carpark_id bigint
);
-- durations
CREATE TABLE IF NOT EXISTS durations (
id bigint NOT NULL autoincrement start 1 increment 1 PRIMARY KEY,
odb_created_at timestamp without time zone NOT NULL,
event_id_arrival bigint,
event_id_departure bigint,
event_time_arrival timestamp without time zone,
event_time_departure timestamp without time zone,
card_nr character varying(100),
ticket_type integer,
duration integer,
manufacturer character varying(200),
carpark_id bigint
);
--properties
create or replace TABLE PROPERTIES (
PROP_KEY VARCHAR(80) NOT NULL,
PROP_VALUE VARCHAR(250),
primary key (PROP_KEY)
);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188160996, '2021-10-02 04:28:26.338', '2021-10-01 09:14:41.32', 1, 2, 11, '03998988030897300007782', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188160790, '2021-10-02 04:28:26.248', '2021-10-01 09:31:10.94', 2, 2, 11, '03998988030897300007782', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188146489, '2021-10-02 04:26:55.069', '2021-10-01 10:03:01.57', 1, 2, 500, '01479804030429500089598', 1, 'XX', 1563);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188146069, '2021-10-02 04:26:54.852', '2021-10-01 11:49:58.45', 2, 2, 500, '01479804030429500089598', 1, 'XX', 1563);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188161161, '2021-10-02 04:28:26.372', '2021-10-01 18:44:33.62', 1, 2, 11, '03998988030897300007782', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188160950, '2021-10-02 04:28:26.329', '2021-10-01 18:45:51.903', 2, 2, 11, '03998988030897300007782', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188161227, '2021-10-02 04:28:26.374', '2021-10-01 23:21:18.58', 1, 2, 11, '04139733030897300003136', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188160974, '2021-10-02 04:28:26.334', '2021-10-01 23:24:03.29', 2, 2, 11, '04139733030897300003136', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188239864, '2021-10-03 04:24:43.345', '2021-10-02 06:49:55.97', 1, 2, 11, '01719400030897300061410', 1, 'XX', 1852);
INSERT INTO public.events (id, odb_created_at, event_time, device_type, event_type, ticket_type, card_nr, count, manufacturer, carpark_id) VALUES(188239649, '2021-10-03 04:24:43.308', '2021-10-02 07:02:08.72', 2, 2, 11, '01719400030897300061410', 1, 'XX', 1852);
这是一个例子:
这是我的功能
CREATE OR REPLACE FUNCTION public.calculateduration()
RETURNS void
LANGUAGE plpgsql
AS $function$
DECLARE
arrived_entry RECORD;
departed_entry RECORD;
durationLimitDays INTEGER := -1;
durationLimitDate TIMESTAMP := '1970-01-01 00:00:00';
cursorQuery text;
cursorEvent refcursor;
maxEventTime TIMESTAMP;
BEGIN
-- start date = DURATION.LIMIT.DATE
-- end date = now - DURATION.LIMIT.DAYS
SELECT PROP_VALUE INTO durationLimitDays FROM properties WHERE prop_key = 'DURATION.LIMIT.DAYS';
SELECT PROP_VALUE INTO durationLimitDate FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE';
RAISE NOTICE 'Parameter duration limit days ''%'' , duration limit date ''%''', durationLimitDays, durationLimitDate;
-- AND e.event_time < to_char(now(), ''YYYY-MM-DD'')::date - (''42 month''::interval)
cursorQuery:='SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.device_type, e.manufacturer, e.carpark_id
FROM events e
WHERE e.event_time >= ''' || durationLimitDate || '''
AND e.device_type IN (1,2) AND event_type=2
AND e.manufacturer like ''DESIGNA_ABACUS%''
AND NOT EXISTS (SELECT d.event_id_arrival FROM durations d WHERE d.event_id_arrival = e.id)
AND NOT EXISTS (SELECT d.event_id_departure FROM durations d WHERE d.event_id_departure = e.id)
ORDER BY e.card_nr, e.event_time, e.carpark_id';
-- AND e.event_time < ''2016-01-05 00:00:00''
OPEN cursorEvent SCROLL FOR EXECUTE cursorQuery;
LOOP
FETCH cursorEvent INTO arrived_entry;
EXIT WHEN NOT FOUND;
IF arrived_entry.device_type=1 THEN
FETCH cursorEvent INTO departed_entry;
EXIT WHEN NOT FOUND;
-- same card number and same car park
IF arrived_entry.card_nr=departed_entry.card_nr AND arrived_entry.carpark_id=departed_entry.carpark_id THEN
IF departed_entry.device_type=2 THEN
EXECUTE 'INSERT INTO durations VALUES (nextval(''durations_id_seq''), ''' || current_timestamp || ''',' || arrived_entry.id || ',' || departed_entry.id || ',''' || arrived_entry.event_time || ''',''' || departed_entry.event_time
|| ''',''' || arrived_entry.card_nr || ''',' || arrived_entry.ticket_type || ','
|| date_part('epoch', departed_entry.event_time::timestamp - arrived_entry.event_time::timestamp) || ', ''' || arrived_entry.manufacturer || ''','
|| arrived_entry.carpark_id || ')';
ELSE
-- repeated entry found - refresh entry with the repeated one
-- RAISE NOTICE 'Unexpected entry after entry found at event id ''%'' and card number''%''', arrived_entry.id, arrived_entry.card_nr;
FETCH PRIOR FROM cursorEvent INTO arrived_entry;
END IF;
ELSE
-- card number or car park changed - refresh to changed card number / car park
-- RAISE NOTICE 'Unexpected card number or car park change found at event id ''%'' and card number''%''', arrived_entry.id, arrived_entry.card_nr;
FETCH PRIOR FROM cursorEvent INTO arrived_entry;
END IF;
END IF;
END LOOP;
CLOSE cursorEvent;
-- update duration limit date ( = MAX(event_time) - durationLimitDays)
EXECUTE 'SELECT MAX(event_time) FROM events WHERE event_time >= ''' || durationLimitDate || '''' INTO maxEventTime;
SELECT (maxEventTime - (durationLimitDays ||' day')::interval) INTO durationLimitDate;
EXECUTE 'UPDATE properties SET PROP_VALUE=''' || durationLimitDate || ''' WHERE prop_key =''DURATION.LIMIT.DATE''';
RAISE NOTICE 'Add new DURATION.LIMIT.DATE to ''%''', durationLimitDate;
END;
$function$
;
我知道如何以更简单的方式重写它:
- 在我们的选择中,我们应该只计算来自属性的 events.event_time >) durationLimitDate 的持续时间
- 我需要确保 event_id_arrival 和 event_id_departure 不在目标表中以避免重复
- 将计算结果插入持续时间表后,我必须更新属性中的 durationLimitDate。知道
durationLimitDate = (Max(event_time) - durationLimitDays))
第一次尝试
INSERT INTO durations (id, odb_created_at, event_id_arrival, event_id_departure, event_time_arrival, event_time_departure, card_nr, ticket_type, duration, manufacturer, carpark_id)
SELECT nextval('durations_id_seq'),
current_timestamp,
arrived_entry.id,
departed_entry.id,
arrived_entry.event_time,
departed_entry.event_time,
arrived_entry.card_nr,
arrived_entry.ticket_type,
date_part('epoch', departed_entry.event_time::timestamp - arrived_entry.event_time::timestamp),
arrived_entry.manufacturer,
arrived_entry.carpark_id
FROM (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id
WHERE e.event_time >= TO_TIMESTAMP((SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE'),'YYYY-MM-DD HH24:MI:SS.MS')
AND e.device_type = 1
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL) AS arrived_entry
INNER JOIN (SELECT e.id, e.card_nr, e.carpark_id, e.event_time
FROM events e
LEFT JOIN durations d ON d.event_id_departure = e.id
WHERE e.event_time >= TO_TIMESTAMP((SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE'),'YYYY-MM-DD HH24:MI:SS.MS')
AND e.device_type = 2
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL) AS departed_entry ON arrived_entry.card_nr = departed_entry.card_nr AND arrived_entry.carpark_id = departed_entry.carpark_id;
问题:
- event_id_arrival > 许多重复,最多 600 个。我需要确保 event_id_arrival 和 event_id_departure 不在目标表中以避免重复
- 将计算结果插入持续时间表后,我必须更新属性中的 durationLimitDate。知道durationLimitDate = (Max(event_time) - durationLimitDays))
【问题讨论】:
-
尝试将表格结构共享为文本,因为没有它们我们无法尝试该功能。您也应该分享一些示例数据(
INSERT语句)。 -
@nachospiu 我已经修改了问题并添加了所有 ddls 和一些示例数据!
标签: postgresql plpgsql