sql >> データベース >  >> RDS >> PostgreSQL

毎月の時間ごとにラベルでユーザーをマーク

    私のソリューションにはいくつかの合理的な要件がありますが、それがなくても作業できます(価格はある程度のパフォーマンスです)。

    トリガーで自動入力されるヘルパーテーブルをいくつか作成しました。要件は、UPDATE またはDELETE 訪問では許可されていません テーブル。

    mst_user テーブルには個別のuser_idが格納されます -sそしてそれはfirst_visituser_monthly_visit テーブルには、最後と最初の visit_dateが格納されます プロ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 REACTIVATE
    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. OracleはNumberデータ型の末尾のゼロを格納しますか?

    2. SQLiteでのDate()関数のしくみ

    3. MySQLカスタム主キージェネレータ

    4. Text[]配列列のテーブルインデックス