LogoCocode Skills

clickhouse Templates

kobic 프로젝트를 위한 분석 쿼리 템플릿입니다.

ClickHouse Query Templates#

kobic 프로젝트를 위한 분석 쿼리 템플릿입니다.

도서 분석#

일별 도서 판매 현황#

SELECT
    toDate(created_at) as date,
    count() as order_count,
    sum(total_amount) as revenue,
    uniqExact(user_id) as unique_buyers
FROM kobic_analytics.payment_book_order
WHERE created_at >= now() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date DESC

베스트셀러 TOP 10#

SELECT
    b.id as book_id,
    b.title as book_title,
    count(o.id) as sales_count,
    sum(o.total_amount) as total_revenue
FROM kobic_analytics.payment_book_order o
JOIN kobic_analytics.book_book b ON o.book_id = b.id
WHERE o.created_at >= now() - INTERVAL 30 DAY
GROUP BY b.id, b.title
ORDER BY sales_count DESC
LIMIT 10

출판사별 매출#

SELECT
    p.name as publisher_name,
    count(o.id) as order_count,
    sum(o.total_amount) as revenue
FROM kobic_analytics.payment_book_order o
JOIN kobic_analytics.book_book b ON o.book_id = b.id
JOIN kobic_analytics.book_publisher p ON b.publisher_id = p.id
WHERE o.created_at >= now() - INTERVAL 30 DAY
GROUP BY p.id, p.name
ORDER BY revenue DESC

사용자 분석#

DAU/WAU/MAU#

-- DAU
SELECT
    toDate(created_at) as date,
    uniqExact(user_id) as dau
FROM kobic_analytics.book_reading_progress
WHERE created_at >= now() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date;

-- WAU
SELECT
    toStartOfWeek(created_at) as week,
    uniqExact(user_id) as wau
FROM kobic_analytics.book_reading_progress
WHERE created_at >= now() - INTERVAL 12 WEEK
GROUP BY week
ORDER BY week;

-- MAU
SELECT
    toStartOfMonth(created_at) as month,
    uniqExact(user_id) as mau
FROM kobic_analytics.book_reading_progress
WHERE created_at >= now() - INTERVAL 12 MONTH
GROUP BY month
ORDER BY month;

신규 가입자 추이#

SELECT
    toDate(created) as date,
    count() as new_users
FROM kobic_analytics.serverpod_user_info
WHERE created >= now() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date

사용자 코호트 분석#

WITH first_purchase AS (
    SELECT
        user_id,
        toStartOfMonth(min(created_at)) as cohort_month
    FROM kobic_analytics.payment_book_order
    GROUP BY user_id
)
SELECT
    cohort_month,
    count() as cohort_size
FROM first_purchase
GROUP BY cohort_month
ORDER BY cohort_month

읽기 행동 분석#

일별 읽기 활동#

SELECT
    toDate(updated_at) as date,
    count() as reading_sessions,
    uniqExact(user_id) as active_readers,
    uniqExact(book_id) as books_read
FROM kobic_analytics.book_reading_progress
WHERE updated_at >= now() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date DESC

인기 도서 (읽기 기준)#

SELECT
    b.id as book_id,
    b.title as book_title,
    count(rp.id) as read_count,
    uniqExact(rp.user_id) as unique_readers,
    avg(rp.progress_percentage) as avg_progress
FROM kobic_analytics.book_reading_progress rp
JOIN kobic_analytics.book_book b ON rp.book_id = b.id
WHERE rp.updated_at >= now() - INTERVAL 7 DAY
GROUP BY b.id, b.title
ORDER BY unique_readers DESC
LIMIT 20

완독률 분석#

SELECT
    b.title as book_title,
    count() as total_readers,
    countIf(rp.progress_percentage >= 100) as completed_readers,
    round(completed_readers * 100.0 / total_readers, 2) as completion_rate
FROM kobic_analytics.book_reading_progress rp
JOIN kobic_analytics.book_book b ON rp.book_id = b.id
WHERE rp.updated_at >= now() - INTERVAL 30 DAY
GROUP BY b.id, b.title
HAVING total_readers >= 10
ORDER BY completion_rate DESC

읽기 시간대 분석#

SELECT
    toHour(updated_at) as hour,
    count() as reading_count,
    uniqExact(user_id) as unique_users
FROM kobic_analytics.book_reading_progress
WHERE updated_at >= now() - INTERVAL 7 DAY
GROUP BY hour
ORDER BY hour

필기 활동 분석#

일별 필기 활동#

SELECT
    toDate(created_at) as date,
    count() as scribble_count,
    uniqExact(user_id) as active_users,
    uniqExact(book_id) as books_with_scribbles
FROM kobic_analytics.scribble_activity
WHERE created_at >= now() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date DESC

필기 유형별 분석#

SELECT
    activity_type,
    count() as count,
    uniqExact(user_id) as unique_users
FROM kobic_analytics.scribble_activity
WHERE created_at >= now() - INTERVAL 7 DAY
GROUP BY activity_type
ORDER BY count DESC

필기 많은 도서 TOP 10#

SELECT
    b.title as book_title,
    count(s.id) as scribble_count,
    uniqExact(s.user_id) as unique_users
FROM kobic_analytics.scribble_activity s
JOIN kobic_analytics.book_book b ON s.book_id = b.id
WHERE s.created_at >= now() - INTERVAL 30 DAY
GROUP BY b.id, b.title
ORDER BY scribble_count DESC
LIMIT 10

결제 분석#

결제 수단별 현황#

SELECT
    payment_method,
    count() as transaction_count,
    sum(amount) as total_amount,
    avg(amount) as avg_amount
FROM kobic_analytics.payment_wallet_transaction
WHERE created_at >= now() - INTERVAL 30 DAY
GROUP BY payment_method
ORDER BY total_amount DESC

쿠폰 사용 현황#

SELECT
    c.code as coupon_code,
    c.discount_type,
    count(uc.id) as usage_count,
    sum(uc.discount_amount) as total_discount
FROM kobic_analytics.payment_user_coupon uc
JOIN kobic_analytics.payment_coupon c ON uc.coupon_id = c.id
WHERE uc.used_at >= now() - INTERVAL 30 DAY
GROUP BY c.id, c.code, c.discount_type
ORDER BY usage_count DESC

ARPU (사용자당 평균 매출)#

SELECT
    toStartOfMonth(created_at) as month,
    sum(total_amount) as total_revenue,
    uniqExact(user_id) as paying_users,
    round(total_revenue / paying_users, 2) as arpu
FROM kobic_analytics.payment_book_order
WHERE created_at >= now() - INTERVAL 6 MONTH
GROUP BY month
ORDER BY month

알림 분석#

알림 발송 현황#

SELECT
    toDate(created_at) as date,
    notification_type,
    count() as sent_count
FROM kobic_analytics.notification_send
WHERE created_at >= now() - INTERVAL 7 DAY
GROUP BY date, notification_type
ORDER BY date DESC, sent_count DESC

알림 효과 분석 (클릭률)#

SELECT
    ns.notification_type,
    count(ns.id) as sent,
    count(nr.id) as received,
    countIf(nr.clicked = true) as clicked,
    round(clicked * 100.0 / sent, 2) as click_rate
FROM kobic_analytics.notification_send ns
LEFT JOIN kobic_analytics.notification_receive nr ON ns.id = nr.notification_id
WHERE ns.created_at >= now() - INTERVAL 7 DAY
GROUP BY ns.notification_type
ORDER BY click_rate DESC

퍼널 분석#

가입 퍼널#

WITH funnel AS (
    SELECT
        user_id,
        minIf(created_at, event_name = 'sign_up_started') as step1,
        minIf(created_at, event_name = 'email_verified') as step2,
        minIf(created_at, event_name = 'profile_completed') as step3,
        minIf(created_at, event_name = 'first_book_view') as step4
    FROM events
    WHERE created_at >= now() - INTERVAL 7 DAY
    GROUP BY user_id
)
SELECT
    count() as total_users,
    countIf(step1 IS NOT NULL) as started,
    countIf(step2 IS NOT NULL) as email_verified,
    countIf(step3 IS NOT NULL) as profile_completed,
    countIf(step4 IS NOT NULL) as first_book_view
FROM funnel

구매 퍼널#

WITH purchase_funnel AS (
    SELECT
        user_id,
        minIf(created_at, event_name = 'book_detail_view') as view,
        minIf(created_at, event_name = 'add_to_cart') as cart,
        minIf(created_at, event_name = 'checkout_started') as checkout,
        minIf(created_at, event_name = 'purchase_completed') as purchase
    FROM events
    WHERE created_at >= now() - INTERVAL 7 DAY
    GROUP BY user_id
)
SELECT
    countIf(view IS NOT NULL) as viewed,
    countIf(cart IS NOT NULL) as added_to_cart,
    countIf(checkout IS NOT NULL) as started_checkout,
    countIf(purchase IS NOT NULL) as purchased,
    round(purchased * 100.0 / viewed, 2) as conversion_rate
FROM purchase_funnel

리텐션 분석#

주간 리텐션#

WITH first_activity AS (
    SELECT
        user_id,
        toStartOfWeek(min(created_at)) as cohort_week
    FROM kobic_analytics.book_reading_progress
    GROUP BY user_id
),
weekly_activity AS (
    SELECT
        user_id,
        toStartOfWeek(created_at) as activity_week
    FROM kobic_analytics.book_reading_progress
    GROUP BY user_id, activity_week
)
SELECT
    fa.cohort_week,
    dateDiff('week', fa.cohort_week, wa.activity_week) as week_number,
    uniqExact(fa.user_id) as users
FROM first_activity fa
JOIN weekly_activity wa ON fa.user_id = wa.user_id
WHERE wa.activity_week >= fa.cohort_week
    AND fa.cohort_week >= now() - INTERVAL 8 WEEK
GROUP BY fa.cohort_week, week_number
ORDER BY fa.cohort_week, week_number

Dart 코드 예시#

커스텀 쿼리 실행#

import 'package:serverpod_clickhouse/serverpod_clickhouse.dart';

Future<List<Map<String, dynamic>>> getTopBooks({int days = 30}) async {
  final service = ClickHouseService.instance;

  final result = await service.analytics.custom('''
    SELECT
        b.id as book_id,
        b.title,
        count(o.id) as sales_count,
        sum(o.total_amount) as revenue
    FROM kobic_analytics.payment_book_order o
    JOIN kobic_analytics.book_book b ON o.book_id = b.id
    WHERE o.created_at >= now() - INTERVAL {days} DAY
    GROUP BY b.id, b.title
    ORDER BY sales_count DESC
    LIMIT 10
  ''', params: {'days': days});

  return result.rows;
}

대시보드 데이터 조회#

Future<DashboardData> getDashboardData() async {
  final service = ClickHouseService.instance;

  // 병렬 쿼리 실행
  final results = await Future.wait([
    service.analytics.dau(days: 7),
    service.analytics.dailyRevenue(revenueTable: 'payment_book_order', days: 7),
    service.analytics.custom('SELECT count() as total FROM kobic_analytics.user'),
  ]);

  return DashboardData(
    dau: results[0].rows,
    revenue: results[1].rows,
    totalUsers: results[2].rows.first['total'] as int,
  );
}