PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

маркиран потребител с етикет по време за всеки месец

Моето решение има някои разумни изисквания, но мога да работя и без него (цената е известна производителност).

Създадох няколко помощни таблици, които се попълват автоматично с тригер. Изискването е UPDATE или DELETE не е разрешено при посещение таблица.

mst_user таблицата съхранява различен user_id -s и е първо_посещение .user_monthly_visit таблицата съхранява последната и първата data_на_посещение pro user_id и месец .

ТАБЛИЦИ

CREATE TABLE mst_user (
  id BIGINT,
  first_visit TIMESTAMP,
  CONSTRAINT pk_mst_user PRIMARY KEY (id)
);

CREATE TABLE visit (
  user_id BIGINT, 
  visit_date TIMESTAMP,
  CONSTRAINT visit_user_fkey FOREIGN KEY (user_id) REFERENCES mst_user (id)
);

CREATE TABLE user_monthly_visit (
  user_id BIGINT,
  month DATE,
  first_visit_this_month TIMESTAMP,
  last_visit_this_month TIMESTAMP,
  CONSTRAINT pk_user_monthly_visit PRIMARY KEY (user_id, month),
  CONSTRAINT user_monthly_visit_user_fkey FOREIGN KEY (user_id) REFERENCES mst_user (id)
);

CREATE INDEX ix_user_monthly_visit_month ON user_monthly_visit(month);

ТРИГЕР

CREATE OR REPLACE FUNCTION trf_visit()  RETURNS trigger
  VOLATILE
AS $xx$

DECLARE
  l_user_id       BIGINT;
  l_row          RECORD;
  l_user_monthly_visit user_monthly_visit;
BEGIN
  IF (tg_op = 'INSERT')
  THEN
    l_row := NEW;
    INSERT INTO mst_user(id, first_visit) VALUES (l_row.user_id, l_row.visit_date)
    ON CONFLICT(id) DO UPDATE SET first_visit = LEAST(mst_user.first_visit, l_row.visit_date);
    
    INSERT INTO user_monthly_visit(user_id,month,first_visit_this_month,last_visit_this_month) VALUES (l_row.user_id,date_trunc('month',l_row.visit_date),l_row.visit_date,l_row.visit_date) 
    ON CONFLICT(user_id,month) DO UPDATE SET first_visit_this_month = LEAST(user_monthly_visit.first_visit_this_month,l_row.visit_date),
    last_visit_this_month = GREATEST(user_monthly_visit.last_visit_this_month,l_row.visit_date);
  ELSE
    RAISE EXCEPTION 'UPDATE and DELETE arent allowed!';
  END IF;
  RETURN l_row; 
END;
$xx$ LANGUAGE plpgsql;

CREATE TRIGGER trig_visit
  BEFORE INSERT OR DELETE OR UPDATE ON visit
  FOR EACH ROW
  EXECUTE PROCEDURE trf_visit();

ТЕСТНИ ДАННИ

INSERT INTO visit (user_id, visit_date)
VALUES (1, '20200101 122915');
INSERT INTO visit (user_id, visit_date)
VALUES (1, '20200102 123011');
INSERT INTO visit (user_id, visit_date)
VALUES (1, '20200401 123101');
INSERT INTO visit (user_id, visit_date)
VALUES (2, '20200501 123114');

ЗАЯВКА

SELECT mnt AS month, user_id,
CASE WHEN first_visit IS NULL OR first_visit> yyyymm + INTERVAL '1 month' THEN NULL
  WHEN first_visit_this_month = first_visit THEN 'FIRST'
  WHEN first_visit_this_month IS NULL AND last_three_month + INTERVAL '3 month' >= yyyymm THEN 'RETENTION'
  WHEN first_visit_this_month IS NOT NULL THEN 'REACTIVATE'
  ELSE NULL
END user_type
FROM
(SELECT date_part('month', gs.yyyymm)::INTEGER AS mnt, gs.yyyymm, u.id user_id, umv.first_visit_this_month, umv.last_visit_this_month, u.first_visit,
GREATEST(
  LAG(last_visit_this_month) OVER w,
  LAG(last_visit_this_month,2) OVER w,
  LAG(last_visit_this_month,3) OVER w
) last_three_month
FROM 
  generate_series('2020-01-01'::TIMESTAMP, '2020-12-01'::TIMESTAMP, INTERVAL '1 month') gs(yyyymm)
  CROSS JOIN mst_user u
  LEFT JOIN user_monthly_visit umv on (umv.user_id=u.id AND umv.month = gs.yyyymm)
  WINDOW w AS (PARTITION BY u.id ORDER BY gs.yyyymm)  
) monthly_visit
ORDER BY user_id,mnt;

РЕЗУЛТАТ

месец user_id user_type
1 1 ПЪРВИ
2 1 ЗАДЪРЖАНЕ
3 1 ЗАДЪРЖАНЕ
4 1 РЕАКТИВИРАНЕ
5 1 ЗАДЪРЖАНЕ
6 1 ЗАДЪРЖАНЕ
7 1 ЗАДЪРЖАНЕ
8 1 (null)
9 1 (null)
10 1 (null)
11 1 (null)
12 1 (null)
1 2 (null)
2 2 (null)
3 2 (null)
4 2 (null)
5 2 ПЪРВИ
6 2 ЗАДЪРЖАНЕ
7 2 ЗАДЪРЖАНЕ
8 2 ЗАДЪРЖАНЕ
9 2 (null)
10 2 (null)
11 2 (null)
12 2 (null)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Heroku Postgres:Твърде много връзки. Как да убия тези връзки?

  2. Запазване на времеви марки в Postgres въз основа на дати на Java

  3. Важна ли е позицията на колоната с идентификатор в Postgresql?

  4. SqlAlchemy (Postgres + Flask):Как да сумирам няколко колони?

  5. Как Log() работи в PostgreSQL