使用SQL分析,挖掘產品市場數據庫的價值
數據分析不僅僅是冷冰冰的數字和統計結果,創造力在其中扮演著重要的角色。創造力能夠為我們從數據集中提取最大化的價值。

以包含網站事件的表格為例,通過對用戶ID、事件名稱和時間戳等信息的分析,我們可以揭示出用戶行為和趨勢,進而實現諸如監測用戶參與度、衡量用戶增長、繪制客戶旅程以及個性化用戶體驗等多種目標。接下來,我們深入探討如何利用這些數據來回答關鍵問題,并展示數據背后的故事。
該表格由三列組成:
- user_id:顯示觸發事件的唯一用戶
- event_name:指示觸發的事件,例如查看、點擊、注冊、結賬、購買等
- timestamp:記錄事件發生的時間點

盡管數據有限,但可以用于多種目的,包括:
- 監測用戶參與度
- 衡量用戶增長
- 繪制客戶旅程
- 個性化用戶體驗
具體而言,本文將演示如何使用這些數據回答以下問題:
- 整體用戶會話和每個會話的事件趨勢如何?
- 使用情況是由新用戶還是回頭用戶推動的?
- 每周使用率增長率是多少?
1. 公共表達式和窗口函數
在開始之前,理解公共表達式(CTEs)和窗口函數的概念是有必要的。通過利用這些強大的功能,可以使用SQL進行更高級的分析。
CTE是一個命名的臨時結果集,可以在同一查詢中引用它,就像引用其他任何表一樣。這有助于將復雜查詢分解為較小、邏輯上的步驟。它們還可以通過減少復雜連接的需求并允許數據庫引擎緩存中間結果來提高查詢性能。
以下是一個簡單的CTE示例,計算每個用戶的網站訪問次數。主查詢引用了user_visits CTE來進行進一步的聚合,這次是計算返回用戶的數量。
WITH user_visits AS (
SELECT user_id, COUNT(DISTINCT visit_date) AS num_visits
FROM my_table
GROUP BY user_id
)
SELECT COUNT(*) AS num_returning_users
FROM user_visits
WHERE num_visits > 1;窗口函數非常適用于執行復雜任務,例如移動平均值和滾動總和。它們通過根據一個或多個列(例如日期或user_id)將數據分組為多個子集,并獨立地對每個子集執行計算來實現這一目的。
窗口函數(如LAG、LEAD、RANK和ROW_NUMBER())還需要指定數據分區的順序。下面的窗口函數使用LAG來計算當前行與前一行之間的時間差(以秒為單位)。
SELECT
user_id,
event_name,
timestamp,
TIMESTAMPDIFF(SECOND, LAG(timestamp) OVER (PARTITION BY user_id ORDER BY timestamp), timestamp) AS time_diff
FROM my_table;
2. 監測用戶參與度
企業使用參與度指標來了解用戶如何與其產品和/或服務進行交互。例如,每位用戶的會話數增加可能是用戶滿意度的積極指標。這些指標還可以洞察不同營銷渠道的效果,比如從一個渠道獲得的用戶是否比其他渠道更活躍。
為了回答關于會話和每個會話的事件的問題,將在原始數據集中添加一個新的列。該列將顯示特定用戶的會話編號。
下面的查詢首先使用名為sessions的CTE創建了一個名為new_session的列。使用LAG窗口函數,新會話被定義為行(事件)之間超過30分鐘的差異。這個新列包含布爾值,其中1表示新會話的開始,0表示現有會話的延續。
然后,session_ids CTE使用SUM窗口函數為每個事件分配session_id,通過對每個用戶的new_session值求和。
請注意,窗口函數放置在CASE語句內部。這是因為LAG需要從先前的行中檢索數據。如果沒有先前的行,這在由用戶觸發的第一個事件中是這種情況,將返回NULL值。使用CASE WHEN,NULL將被替換為值1。
WITH sessions AS (
SELECT user_id, event_name, timestamp,
CASE
-- 第一個事件總是開始一個新會話
WHEN LAG(timestamp, 1) OVER (PARTITION BY user_id ORDER BY timestamp) IS NULL THEN 1
-- 檢查事件之間是否超過30分鐘
WHEN timestamp - LAG(timestamp, 1) OVER (PARTITION BY user_id ORDER BY timestamp) >= INTERVAL '30 minutes' THEN 1
-- 否則,繼續當前會話
ELSE 0
END AS new_session
FROM my_table
), session_ids AS (
SELECT user_id, event_name, timestamp, SUM(new_session) OVER (PARTITION BY user_id ORDER BY timestamp) AS user_session_id
FROM sessions
)
SELECT user_id, event_name, timestamp, new_session, session_id
FROM session_ids
ORDER BY user_id, timestamp;最終查詢在SELECT語句中包含了new_session和user_session_id,你可以在下面看到它們作為新列:

通過這個表,現在可以計算每日總會話數。首先,我們需要創建一個新的global_session_id,它將以全局而不是用戶的級別區分會話。
這可以通過使用CONCAT(user_id, '-', session_id)來組合user_id和user_session_id來完成。例如,將user_id 001和user_session_id 1組合的結果將是一個新的全局session_id,即001-1。最后,通過按DATE(timestamp)分組計算不同的global_session_id的計數,可以得到每日會話的視圖。
SELECT
DATE(timestamp) AS date,
-- 將user_id和user_session_id連接起來,創建一個全局會話id
COUNT(DISTINCT CONCAT(user_id, '-', user_session_id)) AS unique_sessions
FROM
my_table
GROUP BY
DATE(timestamp)利用global_session_id,我們還可以計算每個會話的事件數。在下面的查詢中,user_actions CTE按global_session_id和date分組事件,然后計算唯一事件的timestamps。這樣就可以得到每個日期上每個會話的事件數。
在主查詢中,我們計算不同的global_session_id的數量,從而得到每日會話的總數。然后,我們SUM(session_event_count),得到每日事件的總數,然后將其除以每日會話數,得到每個會話的平均事件數。按日期分組可以得到每天每個會話的平均事件數。
WITH user_actions AS (
SELECT
CONCAT(user_id, '-', user_session_id) AS global_session_id,
DATE(timestamp) AS date,
-- 計算每個會話和日期的唯一事件數
COUNT(DISTINCT timestamp) AS session_event_count
FROM
my_table
GROUP BY
CONCAT(user_id, '-', user_session_id), DATE(timestamp)
)
SELECT
date,
-- 通過計算不同的global_session_id的數量來計算總會話數
COUNT(DISTINCT global_session_id) AS sessions,
-- 求和所有會話中的事件數
SUM(session_event_count) AS total_events,
-- 將總事件數除以總會話數
SUM(session_event_count) / COUNT(DISTINCT global_session_id) AS avg_events_per_session
FROM
user_actions
GROUP BY
date;3. 測量保留和增長
在高使用率的情況下,如果由新用戶推動,可能會掩蓋用戶流失的問題。因此,留存率是了解用戶參與度的另一個重要指標。通過分析user_session_id列,我們可以確定新用戶和老用戶的比例。
下面創建了兩個CTE來將計算分解為連續的部分。第一個CTE計算每日唯一用戶總數。第二個CTE計算每日唯一回頭用戶總數,使用user_session_id > 1來識別回頭用戶。
然后,將這些CTE使用日期列進行連接,然后計算返回比率,即每日回頭用戶除以每日總用戶數。
WITH all_users AS (
-- 計算每日所有用戶數
SELECT
COUNT(DISTINCT users_id) AS total_users,
DATE(timestamp) AS date
FROM
my_table
GROUP BY
date),
returning_users AS (
-- 計算每日回頭用戶數
SELECT
COUNT(DISTINCT users_id) AS returning_users,
DATE(timestamp) AS date
FROM
my_table
WHERE user_session_id > 1
GROUP BY
date)
SELECT
-- 連接CTE并將回頭用戶除以總用戶數
all_users.date,
total_users,
returning_users,
ROUND((returning_users / all_users), 2) AS returning_ratio
FROM all_users
LEFT JOIN returning_users ON returning_users.date = all_users.date除了衡量現有用戶的保留情況外,增長率對于提供用戶漏斗的更廣泛圖景也很有用。下面的查詢計算了每周的增長率,這對于評估短期營銷活動是合適的,盡管相同的計算也可以在較長的時間段內進行。
首先,我們使用DATE_TRUNC函數將timestamp值提取為周的起始日期,時間間隔設置為week。接下來,我們計算DISTINCT user_id的數量,其中user_session_id = 1,表示這是用戶的第一次會話。這為我們提供了weekly_new_users,我們可以在窗口函數中使用它來計算累積用戶獲取。這里非常關鍵的是,對該窗口函數按week_start進行排序,并設置范圍為UNBOUNDED PRECEDING AND CURRENT ROW,這將對當前周和之前所有周的weekly_new_users值進行求和。
最后,我們通過將當前累積用戶減去先前累積用戶,并將結果除以先前累積用戶來計算每周的增長率。
WITH weekly_new_users AS (
-- 計算每周的新用戶數
SELECT
DATE_TRUNC('week', timestamp) AS week_start,
COUNT(DISTINCT user_id) AS weekly_new_users
FROM
my_table
WHERE user_session_id = 1
GROUP BY
DATE_TRUNC('week', timestamp),
weekly_cumulative AS (
-- 對每周新用戶數進行累加
SELECT
week_start,
sum(weekly_new_users) OVER (ORDER BY week_start RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum_users
FROM
weekly_new_users
)
-- 使用累積用戶計算每周增長率
SELECT
DATE(week_start) AS week_start,
cum_users,
ROUND(((cum_users - LAG(cum_users) OVER (ORDER BY week_start))/LAG(cum_users) OVER (ORDER BY week_start)) * 100, 2) AS weekly_growth_rate,
FROM
weekly_cumulative4. 結論
雖然具體指標的相關性取決于業務模型、行業和增長階段,但上述示例清楚地展示了SQL在提供業務洞察方面的強大和多功能性。通過將這些工具與創造性思維相結合,即使是基本的數據集也能夠為各種利益相關者提供價值。





















