重写一个 pl/pgSQL 函数
Rewriting a pl/pgSQL function
我想简化一个函数,我必须计算两个事件之间的持续时间。我的数据库有三个 table 用于函数。
EVENTS TABLE - table 重新组合所有事件。我们感兴趣的事件是 device_type 1(= 进入)和 device_type 2(= 退出)的事件
DURATIONS TABLE - 这是函数运行时填充的 table。它会在eventstable中找到前两个具有相同card nr和device type 1和device type 2的事件,计算两个事件之间的持续时间并保存在duration table中。
PROPERTIES - 此 table 用作参考,并将更新并用于将来的插入。
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$
;
我怎样才能以更简单的方式重写它:
- 在我们的 select 中,我们应该只计算 events.event_time >) durationLimitDate 来自 properties
的持续时间
- 我需要确保 event_id_arrival 和 event_id_departure 不在目标 table 中以避免重复
- 将计算插入持续时间 table 后,我必须更新属性中的 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 不在目标 table 中以避免重复
- 将计算插入持续时间 table 后,我必须更新属性中的 durationLimitDate。知道 durationLimitDate = (Max(event_time) - durationLimitDays))
您可以编写如下查询来将行插入 durations
:
,而不是遍历记录
检查查询,因为我无法尝试,因为您将表结构共享为图像。
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL
),
arrived_entry AS (SELECT *
FROM cte
WHERE device_type = 1
),
departed_entry AS (SELECT *
FROM cte
WHERE device_type = 2
)
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 arrived_entry
INNER JOIN departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
您可以使用 durationLimitDate
变量的值替换此查询 (SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
。
Pl/PgSQL 函数计算时间():
CREATE OR REPLACE FUNCTION calculateduration() RETURNS void AS $function$
DECLARE
durationLimitDays INTEGER := -1;
durationLimitDate TIMESTAMP := '1970-01-01 00:00:00';
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::timestamp INTO durationLimitDate FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE';
RAISE NOTICE 'Parameter duration limit days ''%'' , duration limit date ''%''', durationLimitDays, durationLimitDate;
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= durationLimitDate
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL)
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 * FROM cte WHERE cte.device_type = 1) AS arrived_entry
INNER JOIN (SELECT * FROM cte WHERE cte.device_type = 2) AS departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
-- update duration limit date ( = MAX(event_time) - durationLimitDays)
SELECT (MAX(event_time) - (durationLimitDays ||' day')::interval) INTO durationLimitDate FROM events WHERE event_time >= durationLimitDate;
UPDATE properties SET PROP_VALUE = durationLimitDate WHERE PROP_KEY ='DURATION.LIMIT.DATE';
RAISE NOTICE 'Add new DURATION.LIMIT.DATE to ''%''', durationLimitDate;
END;
$function$
LANGUAGE plpgsql;
SQL 函数计算时间():
CREATE OR REPLACE FUNCTION calculateduration() RETURNS void AS $function$
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL)
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 * FROM cte WHERE cte.device_type = 1) AS arrived_entry
INNER JOIN (SELECT * FROM cte WHERE cte.device_type = 2) AS departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
UPDATE properties
SET PROP_VALUE = (SELECT (MAX(event_time) - ((SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DAYS') ||' day')::interval) FROM events WHERE event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE'))
WHERE PROP_KEY ='DURATION.LIMIT.DATE';
$function$
LANGUAGE sql;
我想简化一个函数,我必须计算两个事件之间的持续时间。我的数据库有三个 table 用于函数。
EVENTS TABLE - table 重新组合所有事件。我们感兴趣的事件是 device_type 1(= 进入)和 device_type 2(= 退出)的事件
DURATIONS TABLE - 这是函数运行时填充的 table。它会在eventstable中找到前两个具有相同card nr和device type 1和device type 2的事件,计算两个事件之间的持续时间并保存在duration table中。
PROPERTIES - 此 table 用作参考,并将更新并用于将来的插入。
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$
;
我怎样才能以更简单的方式重写它:
- 在我们的 select 中,我们应该只计算 events.event_time >) durationLimitDate 来自 properties 的持续时间
- 我需要确保 event_id_arrival 和 event_id_departure 不在目标 table 中以避免重复
- 将计算插入持续时间 table 后,我必须更新属性中的 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 不在目标 table 中以避免重复
- 将计算插入持续时间 table 后,我必须更新属性中的 durationLimitDate。知道 durationLimitDate = (Max(event_time) - durationLimitDays))
您可以编写如下查询来将行插入 durations
:
检查查询,因为我无法尝试,因为您将表结构共享为图像。
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL
),
arrived_entry AS (SELECT *
FROM cte
WHERE device_type = 1
),
departed_entry AS (SELECT *
FROM cte
WHERE device_type = 2
)
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 arrived_entry
INNER JOIN departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
您可以使用 durationLimitDate
变量的值替换此查询 (SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
。
Pl/PgSQL 函数计算时间():
CREATE OR REPLACE FUNCTION calculateduration() RETURNS void AS $function$
DECLARE
durationLimitDays INTEGER := -1;
durationLimitDate TIMESTAMP := '1970-01-01 00:00:00';
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::timestamp INTO durationLimitDate FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE';
RAISE NOTICE 'Parameter duration limit days ''%'' , duration limit date ''%''', durationLimitDays, durationLimitDate;
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= durationLimitDate
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL)
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 * FROM cte WHERE cte.device_type = 1) AS arrived_entry
INNER JOIN (SELECT * FROM cte WHERE cte.device_type = 2) AS departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
-- update duration limit date ( = MAX(event_time) - durationLimitDays)
SELECT (MAX(event_time) - (durationLimitDays ||' day')::interval) INTO durationLimitDate FROM events WHERE event_time >= durationLimitDate;
UPDATE properties SET PROP_VALUE = durationLimitDate WHERE PROP_KEY ='DURATION.LIMIT.DATE';
RAISE NOTICE 'Add new DURATION.LIMIT.DATE to ''%''', durationLimitDate;
END;
$function$
LANGUAGE plpgsql;
SQL 函数计算时间():
CREATE OR REPLACE FUNCTION calculateduration() RETURNS void AS $function$
WITH cte AS (SELECT e.id, e.card_nr, e.event_time, e.ticket_type, e.manufacturer, e.carpark_id, e.device_type,
ROW_NUMBER() OVER (ORDER BY e.card_nr, e.carpark_id, e.event_time, e.device_type) AS rn
FROM events e
LEFT JOIN durations d ON d.event_id_arrival = e.id OR d.event_id_departure = e.id
WHERE e.event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE')
AND e.device_type IN (1, 2)
AND event_type = 2
AND e.manufacturer LIKE 'DESIGNA_ABACUS%'
AND d.id IS NULL)
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 * FROM cte WHERE cte.device_type = 1) AS arrived_entry
INNER JOIN (SELECT * FROM cte WHERE cte.device_type = 2) AS departed_entry ON arrived_entry.card_nr = departed_entry.card_nr
AND arrived_entry.carpark_id = departed_entry.carpark_id
AND arrived_entry.rn + 1 = departed_entry.rn;
UPDATE properties
SET PROP_VALUE = (SELECT (MAX(event_time) - ((SELECT PROP_VALUE FROM properties WHERE prop_key = 'DURATION.LIMIT.DAYS') ||' day')::interval) FROM events WHERE event_time >= (SELECT PROP_VALUE::timestamp FROM properties WHERE prop_key = 'DURATION.LIMIT.DATE'))
WHERE PROP_KEY ='DURATION.LIMIT.DATE';
$function$
LANGUAGE sql;