121 lines
4.3 KiB
Python
121 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
||
"""查询2026年3月后未退费订单,激活课程在下单30天内的学习进度分布"""
|
||
import psycopg2
|
||
import os
|
||
import sys
|
||
|
||
# 数据库连接
|
||
conn = psycopg2.connect(
|
||
host="bj-postgres-16pob4sg.sql.tencentcdb.com",
|
||
port=28591,
|
||
user="ai_member",
|
||
password="LdfjdjL83h3h3^$&**YGG*",
|
||
dbname="vala_bi"
|
||
)
|
||
|
||
cur = conn.cursor()
|
||
|
||
sql = """WITH orders AS (
|
||
SELECT o.id, o.out_trade_no, o.account_id, o.pay_success_date
|
||
FROM bi_vala_order o
|
||
JOIN bi_vala_app_account a ON o.account_id = a.id
|
||
WHERE o.pay_success_date >= '2026-03-01'
|
||
AND o.order_status != 4
|
||
AND o.pay_success_date IS NOT NULL
|
||
AND a.status = 1
|
||
),
|
||
tickets AS (
|
||
SELECT o.out_trade_no, o.account_id, o.pay_success_date,
|
||
t.character_id, t.season_package_level
|
||
FROM orders o
|
||
JOIN bi_vala_seasonal_ticket t ON o.out_trade_no = t.out_trade_no
|
||
WHERE t.status = 1
|
||
AND t.deleted_at IS NULL
|
||
AND t.season_package_level IN ('A1', 'A2')
|
||
),
|
||
all_chapter_records AS (
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_0 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_1 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_2 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_3 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_4 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_5 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_6 WHERE play_status = 1
|
||
UNION ALL
|
||
SELECT user_id, chapter_id, created_at FROM bi_user_chapter_play_record_7 WHERE play_status = 1
|
||
),
|
||
level_records AS (
|
||
SELECT pr.user_id, pr.chapter_id, pr.created_at, l.course_level
|
||
FROM all_chapter_records pr
|
||
JOIN bi_level_unit_lesson l ON pr.chapter_id = l.id
|
||
WHERE l.course_level IN ('L1', 'L2')
|
||
),
|
||
ticket_progress AS (
|
||
SELECT
|
||
t.out_trade_no,
|
||
t.account_id,
|
||
t.character_id,
|
||
t.season_package_level,
|
||
t.pay_success_date,
|
||
COUNT(DISTINCT lr.chapter_id) AS completed_lessons
|
||
FROM tickets t
|
||
LEFT JOIN level_records lr
|
||
ON t.character_id = lr.user_id
|
||
AND lr.course_level = CASE
|
||
WHEN t.season_package_level = 'A1' THEN 'L1'
|
||
WHEN t.season_package_level = 'A2' THEN 'L2'
|
||
END
|
||
AND lr.created_at >= t.pay_success_date
|
||
AND lr.created_at <= t.pay_success_date + INTERVAL '30 days'
|
||
GROUP BY t.out_trade_no, t.account_id, t.character_id, t.season_package_level, t.pay_success_date
|
||
)
|
||
SELECT
|
||
CASE WHEN season_package_level = 'A1' THEN 'L1' ELSE 'L2' END AS course_level,
|
||
completed_lessons,
|
||
COUNT(*) AS ticket_count,
|
||
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (PARTITION BY season_package_level), 1) AS pct
|
||
FROM ticket_progress
|
||
GROUP BY season_package_level, completed_lessons
|
||
ORDER BY season_package_level, completed_lessons;"""
|
||
|
||
print("正在查询...")
|
||
cur.execute(sql)
|
||
rows = cur.fetchall()
|
||
|
||
print(f"\n{'='*60}")
|
||
print("2026年3月后未退费订单 → 激活课程下单30天内学习进度分布")
|
||
print(f"{'='*60}")
|
||
|
||
l1_rows = [r for r in rows if r[0] == 'L1']
|
||
l2_rows = [r for r in rows if r[0] == 'L2']
|
||
|
||
for label, data in [('L1', l1_rows), ('L2', l2_rows)]:
|
||
print(f"\n--- {label} ---")
|
||
total = sum(r[2] for r in data)
|
||
print(f"总 ticket 数: {total}")
|
||
print(f"{'完课数':>8} | {'ticket数':>10} | {'占比':>8}")
|
||
print("-" * 35)
|
||
for r in data:
|
||
print(f"{r[1]:>8} | {r[2]:>10} | {r[3]:>7}%")
|
||
|
||
# 统计摘要
|
||
if data:
|
||
completed_values = []
|
||
for r in data:
|
||
completed_values.extend([r[1]] * r[2])
|
||
import statistics
|
||
avg = sum(completed_values) / len(completed_values) if completed_values else 0
|
||
median = statistics.median(completed_values) if completed_values else 0
|
||
zero_pct = sum(1 for v in completed_values if v == 0) / len(completed_values) * 100 if completed_values else 0
|
||
print(f"\n摘要: 平均={avg:.1f}节, 中位数={median:.0f}节, 0节课占比={zero_pct:.1f}%")
|
||
|
||
cur.close()
|
||
conn.close()
|
||
print("\n查询完成。")
|