You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL函数问题:忽略同一卡号连续出现的首个入场记录

PostgreSQL函数public.desi3匹配入场离场逻辑错误问题

函数功能

  • 从properties表获取时长限制参数(天数和日期)
  • 构建动态SQL查询fact_entriesexits表,过滤日期和设备类型
  • 匹配入场(devicetype=1)和离场(devicetype=2)事件,插入到fact_parkingtransaction表
  • 针对不同场景输出提示信息
  • 更新properties表的DURATION.LIMIT.DATE参数

核心问题

同一cardnumber的连续入场记录中,首个入场记录应被忽略,但现有函数未实现该逻辑。例如卡号00D56029在2023-05-02有两条连续入场记录,预期忽略第一条(2023-05-02 15:32:26.000),但实际该记录仍被匹配离场记录。

样本数据

devicetype|cardnumber|eventdate              |
----------+----------+-----------------------+
         1|00D56029  |2023-05-02 13:47:06.000|
         2|00D56029  |2023-05-02 15:31:00.000|
         1|00D56029  |2023-05-02 15:32:26.000|
         1|00D56029  |2023-05-02 15:33:33.000|
         2|00D56029  |2023-05-02 15:37:25.000|
         1|00D56029  |2023-07-25 14:20:56.000|
         2|00D56029  |2023-07-25 16:26:53.000|

预期输出

  • 行1:入场2023-05-02 13:47:06.000 | 离场2023-05-02 15:31:00.000
  • 行2:入场2023-05-02 15:33:33.000 | 离场2023-05-02 15:37:25.000
  • 行3:入场2023-07-25 14:20:56.000 | 离场2023-07-25 16:26:53.000

实际输出

  • 行1:入场2023-05-02 13:47:06.000 | 离场2023-05-02 15:31:00.000
  • 行2:入场2023-05-02 15:32:28.000 | 离场2023-05-02 15:37:25.000
  • 行3:入场2023-07-25 14:20:56.000 | 离场2023-07-25 16:26:53.000

当前函数代码

CREATE OR REPLACE FUNCTION public.desi3()
 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
  -- Get the duration limit parameters
  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';

  cursorQuery := '
    WITH filtered_entries AS (
      SELECT
        id,
        facilitykey,
        systeminterfacekey,
        devicekey,
        datekey,
        timekey,
        tickettypekey,
        eventtypekey,
        manufacturerkey,
        devicetype,
        licenseplate,
        licenseplatekey,
        cardnumber,
        eventdate,
        etlsource,
        LAG(devicetype) OVER (PARTITION BY cardnumber ORDER BY eventdate) AS prev_devicetype,
        LEAD(devicetype) OVER (PARTITION BY cardnumber ORDER BY eventdate) AS next_devicetype
      FROM
        fact_entriesexits
      WHERE
        eventdate >= ' || quote_literal(durationLimitDate) || '
        AND devicetype IN (1, 2)
    ),
    valid_entries AS (
      SELECT
        fe.*,
        CASE
          WHEN devicetype = 1 AND (prev_devicetype IS NULL OR prev_devicetype = 2 OR (next_devicetype = 1 AND LEAD(next_devicetype) OVER (PARTITION BY cardnumber ORDER BY eventdate) = 2)) THEN 1
          WHEN devicetype = 2 AND (next_devicetype IS NULL OR next_devicetype = 1 OR prev_devicetype = 1) THEN 2
          ELSE NULL
        END AS startend
      FROM
        filtered_entries AS fe
    )
    SELECT * FROM valid_entries
    WHERE startend IS NOT NULL
    AND NOT EXISTS (
      SELECT 1
      FROM fact_parkingtransaction AS fp
      WHERE fp.eventid_arrival = valid_entries.id
      OR fp.eventid_departure = valid_entries.id
    )
    ORDER BY cardnumber, eventdate';

  OPEN cursorEvent SCROLL FOR EXECUTE cursorQuery;

  LOOP
    FETCH cursorEvent INTO arrived_entry;
    EXIT WHEN NOT FOUND;
    IF arrived_entry.startend = 1 THEN
      FETCH cursorEvent INTO departed_entry;
      IF departed_entry.startend = 2 THEN
        EXECUTE 'INSERT INTO fact_parkingtransaction (
            id,
            entryfacilitykey,
            exitfacilitykey,
            systeminterfacekey,
            manufacturerkey,
            tickettypekey,
            entrydatekey,
            entrytimekey,
            exitdatekey,
            exittimekey,
            entrydevicekey,
            exitdevicekey,
            entrytime,
            exittime,
            duration,
            eventid_arrival,
            eventid_departure,
            cardnumber,
            licenseplate,
            licenseplatekey,
            dateinserted,
            etlsource
          ) VALUES (
            nextval(''fact_parkingtransaction_id_seq''),
            ' || arrived_entry.facilitykey || ',
            ' || departed_entry.facilitykey || ',
            ' || arrived_entry.systeminterfacekey || ',
            ' || arrived_entry.manufacturerkey || ',
            ' || arrived_entry.tickettypekey || ',
            ' || arrived_entry.datekey || ',
            ' || arrived_entry.timekey || ',
            ' || departed_entry.datekey || ',
            ' || departed_entry.timekey || ',
            ' || arrived_entry.devicekey || ',
            ' || departed_entry.devicekey || ',
            ''' || arrived_entry.eventdate || ''',
            ''' || departed_entry.eventdate || ''',
            ' || date_part('epoch', departed_entry.eventdate::timestamp - arrived_entry.eventdate::timestamp) || ',
            ' || arrived_entry.id || ',
            ' || departed_entry.id || ',
            ''' || arrived_entry.cardnumber || ''',
            ''' || arrived_entry.licenseplate || ''',
            ' || arrived_entry.licenseplatekey || ',
            ''' || current_timestamp || ''',
            ''' || arrived_entry.etlsource || '''
          )';
        RAISE NOTICE 'New record inserted into fact_parkingtransaction for card number ''%''', arrived_entry.cardnumber;
      ELSE
        RAISE NOTICE 'Unexpected entry after entry found at event id ''%'' and card number ''%''', arrived_entry.id, arrived_entry.cardnumber;
      END IF;
    ELSE
      RAISE NOTICE 'Unexpected card number or car park change found at event id ''%'' and card number ''%''', arrived_entry.id, arrived_entry.cardnumber;
    END IF;
  END LOOP;
  CLOSE cursorEvent;

  EXECUTE 'SELECT MAX(eventdate) FROM fact_entriesexits WHERE eventdate >= ' || quote_literal(durationLimitDate) INTO maxEventTime;
  durationLimitDate := maxEventTime - interval '1 day' * durationLimitDays;
  EXECUTE 'UPDATE properties SET PROP_VALUE = ' || quote_literal(durationLimitDate) || ' WHERE prop_key = ''DURATION.LIMIT.DATE''';
  RAISE NOTICE 'Updated DURATION.LIMIT.DATE to ''%''', durationLimitDate;
END;
$function$
;

内容的提问来源于stack exchange,提问作者Djabone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:52:03