ãã®è¨äºã¯ç¾
éç¤ ã¢ããã³ãã«ã¬ã³ãã¼ 2024ã®13æ¥ç®ã®è¨äºã§ãã
qiita.com
ãã¿ã¾ãããã¿ã¤ãã«ã®ãæ°æéã§è¨å®ãããã¯é£ãã¿ã¤ãã«ã§ãã
ï¼GA4âBQã®è¨å®ã§ä¸æ¥ç½®ããªãã¨ãã¼ã¿ã§ã¦ããªãï¼
ããã«ã¡ã¯ãç¾
éç¤ã®æ£®å·ã§ãã
æè¿ã¯æ°´åºãã³ã¼ãã¼ã«ããã£ã¦ã¾ãã
ä»åã¯ã¢ã¯ã»ã¹è§£æç³»ã®è©±ã§ãã
Google Analyticsã使ã£ã¦ããæ¹ã¯å¤ãã¨ãããç¾å¨äººï¼IT人éï¼ã®å¿
é ãã¼ã«ã§ãã人権ããªã¨æãã¾ãã
ãã GAã®ã¬ãã¼ãã£ã¦æ
£ãã¦ãªãã¨ä½¿ãã¥ããã§ãããã
æ¢ç´¢ã¬ãã¼ããå
±æã§ãããã©ã³ãã¼ãã¦ç·¨éãããããªãããã©ãããã§ãã
ã¨ã³ã¸ãã¢çã«ã¯ãã¼ã¿ãã¼ã¹ã«å¯¾ãã¦SQLãå®è¡ã§ãããã¨ãåºæ¬ç人権ã§ãããå¿ççå®å
¨æ§ã§ããï¼ã¯ãªãã¯ã«ãã«ããï¼
ãããªç§ã«æå ±ï¼
GAãã¼ã¿ã¯BQã«ãã¼ã¿éããããLooker Studioã§å¯è¦åããã°ã微課éã¦ã¼ã¶ã¼ã¨ãã¦çãã¦ãããã¨ãã§ãã¾ãã
失ããã権å©ã¨å°å³ãåãæ»ãã
ä»åã¯æ¢ã«GAã®è¨å®ãæ¸ãã§ããåæã§è¨è¼ãã¦ãã¾ãã
Google Analytics â Google BigQuery
ã¾ãã¯GAãã¼ã¿ãBQã«è»¢éä¿åããè¨å®ããã¾ãã
å·¦ä¸ã®è¨å®ï¼æ¯è»ã¢ã¤ã³ã³ï¼ããããµã¼ãã¹éã®ãªã³ã¯è¨å®ãå
ã«ãããBigQueryã®ãªã³ã¯ããé¸æãã¾ãã
ããã¦å³ã®ããªã³ã¯ãããBQã¸ã®è»¢éè¨å®ããã¾ãã
BQããã¸ã§ã¯ãã¯åèªè¨å®ãã¦ããã¦ãã¤ãã³ããã¼ã¿ã®ã¨ã¯ã¹ãã¼ãã¿ã¤ãã®ãæ¯æ¥ãã ããã§ãã¯ãå
¥ãã¦ããã°OKã§ãã
Streamingãã¦ã¼ã¶ã¼ãã¼ã¿ã¯å¿
è¦ã«å¿ãã¦ãã§ãã¯ãã¦éã£ã¦ãã ããã
è¨å®ãçµãã£ããã飯ãé£ã¹ã¦ãã風åã«å
¥ã£ã¦ãã²ã¼ã ãã¦ãã£ããå¯ã¦èµ·ããã¨ãæåã®æ¥æ¬¡ãã¼ã¿è»¢éãçµãã£ã¦ãããã¨æãã¾ãã
https://console.cloud.google.com/bigquery
ï¼event_...
ãæ¥æ¬¡ã¤ãã³ããã¼ã¿ï¼
Google BigQuery : éè¨ãã¼ãã«ã®ä½æ
BQã«ãã¼ã¿ã転éãããã®ã§ãã®ã¾ã¾ã§ã使ãã¾ããã
ãã¼ãã¼ã¿ãæ¯åã¯ã¨ãªããã®ã¯å¹çãæªãã®ã§ãæ¥æ¬¡ã®éè¨ãã¼ã¿ã®ãã¼ãã«ãä½ãã¾ãã
幸ãã«ãã¦ãBQåä½ã§ã¹ã±ã¸ã¥ã¼ã«ã¯ã¨ãªæ©è½ãããã®ã§ããã使ãã¾ãã
ãã¶ãAPIãEnabeããªãã¨ä½¿ããªãã®ã§Enablingãã¾ããããããã§ããªããEnablerã
https://console.cloud.google.com/apis/library/bigquerydatatransfer.googleapis.com
ç®çã«åãã¦è¤æ°ã®ãã¼ãã«ãä½ãã¾ããã
ããã¤ãä¾åé¢ä¿ãããã®ã§ã¹ãããã§ä»¥ä¸ã®å¦çãå®è¡ãã¾ã
- äºåæºå
- [1-a] ãã¼ãã¼ã¿ã®ãã¹ããããåï¼é
ååï¼ãUNNESTããããè£å©ã«ã©ã ã追å ãããã¼ãã«ãä½ã
- éè¨ãã¼ãã«ä½æ
- [2-b] 1-a ã®ãã¼ã¿ããæ¥å¥ã§ãã¼ã¿éè¨ãããã¼ãã«ãä½ãï¼1è¡ = 1æ¥ã®ç·è¨ï¼
- [2-c] 1-a ã®ãã¼ã¿ããæ¥å¥ã»ãã¼ã¸ãã¹å¥ã®ã¦ãã¼ã¯ã¦ã¼ã¶ã¼æ°ãéè¨ãããã¼ãã«ãä½ã ï¼1è¡ = 1URLã®ç·è¨ï¼
- [2-d] 1-a ã®ãã¼ã¿ãæ¥å¥ã»ã¦ã¼ã¶ã¼å¥ã§ã¦ãã¼ã¯ã«ãããã¼ãã«ãä½ãï¼ã»ãã·ã§ã³åä½ããã¦ã¼ã¶ã¼åä½ã«ãã, 1è¡ = 1ã¦ã¼ã¶ã¼ï¼
- [2-e] 1-a ã®ãã¼ã¿ããæ¥å¥ã»ã¦ã¼ã¶ã¼å¥ã§ã®ãã¡ãã«ééçã®ãã¼ãã«ãä½ã ï¼1è¡ = 1ã¦ã¼ã¶ã¼ï¼
- ãã¡ãã«ééçã®æ¥å¥éè¨ãã¼ãã«ä½æ
- [3-f] 2-e ã®ãã¼ã¿ãæ¥å¥ã®å¹³åã»Xæ¥ç§»åå¹³åãéè¨ãããã¼ãã«ãä½ã ï¼1è¡ = 1æ¥ã®ç·è¨ï¼
以éã¯å
·ä½çãªSQLãç¨ãã¦è§£èª¬ãã¦ããã¾ãã
analytics
ã¨ãããã¼ã¿ã»ãããBQä¸ã«ä½ã£ã¦ãã¦ããã«ãã¼ã¿ã貯ãã¦ããåæã§è©±ãé²ãã¾ãã
ãã¼ã¿ã»ããã®ä½æ
ã¾ã㯠analytics
ã¨ãããã¼ã¿ã»ãããä½æãã¾ãã
ååã¯é©å½ã«å¤ãã¦ãã ããã
asia-northeast1 ãªã¼ã¸ã§ã³ã ã¨20%ãããæéãé«ãã¨æãã¾ãããæ°ã«ãªããªãUSã¨ãã«ãã¦ãã ããã
äºåæºåãã¼ãã«ã®ä½æ [1-a]
ãã®äºåãã¼ãã«ãå
¨ã¦ã®èµ·ç¹ã«ãªãã¾ãã
å
ã®GAã®ãã¼ãã«ã以éã®SQLã§ä½¿ããããããã«æ´å½¢ãã¾ãã
CREATE TEMPORARY FUNCTION date_from() AS ( 4 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
CREATE TEMPORARY FUNCTION date_YYYYMMDD(i INT64) AS ( FORMAT_DATE("%Y%m%d", DATE_SUB(cur_date(), INTERVAL i DAY)) );
WITH
_raw1 AS (
SELECT * FROM `analytics_123456789.events_*`
WHERE _table_suffix BETWEEN date_YYYYMMDD(date_from()) AND date_YYYYMMDD(date_to())
),
_raw2 AS (
SELECT
DATE(timestamp_micros(event_timestamp), 'Asia/Tokyo') as date_jst,
TIMESTAMP_TRUNC(DATETIME(timestamp_micros(event_timestamp), 'Asia/Tokyo'), SECOND) as datetime_jst,
FARM_FINGERPRINT(CONCAT(event_timestamp, event_name, user_pseudo_id)) as row_id,
(SELECT CASE WHEN p.value.int_value IS NOT NULL THEN SAFE_CAST(p.value.int_value as string) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "ga_session_id") as ga_session_id,
(SELECT CASE WHEN p.value.int_value IS NOT NULL THEN SAFE_CAST(p.value.int_value as INT64) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "ga_session_number") as ga_session_number,
(SELECT CASE WHEN p.value.int_value IS NOT NULL THEN SAFE_CAST(p.value.int_value as INT64) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "engaged_session_event") as engaged_session_event,
(SELECT CASE WHEN p.value.int_value IS NOT NULL THEN SAFE_CAST(p.value.int_value as INT64) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "engagement_time_msec") as engagement_time_msec,
(SELECT CASE WHEN p.value.int_value IS NOT NULL THEN SAFE_CAST(p.value.int_value as INT64) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "entrances") as entrances,
(SELECT CASE WHEN p.value.string_value IS NOT NULL THEN SAFE_CAST(p.value.string_value as string) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "page_referrer") as page_referrer,
(SELECT CASE WHEN p.value.string_value IS NOT NULL THEN SAFE_CAST(p.value.string_value as string) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "page_location") as page_location,
(SELECT CASE WHEN p.value.string_value IS NOT NULL THEN SAFE_CAST(p.value.string_value as string) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "page_title") as page_title,
(SELECT CASE WHEN p.value.string_value IS NOT NULL THEN SAFE_CAST(p.value.string_value as INT64) ELSE null END FROM UNNEST(event_params) as p WHERE p.key = "session_engaged") as session_engaged,
*,
FROM _raw1
),
raw AS (
SELECT
row_id,
date_jst,
datetime_jst,
CONCAT(user_pseudo_id, ga_session_id) as user_session_id,
event_date,
event_timestamp,
event_name,
event_previous_timestamp,
event_value_in_usd,
event_bundle_sequence_id,
event_server_timestamp_offset,
session_engaged,
ga_session_id,
ga_session_number,
page_referrer,
page_location,
page_title,
`change-your-common-project.get_path_from_url`(page_location) as page_path,
`change-your-common-project.get_query_from_url`(page_location) as page_query,
engaged_session_event,
engagement_time_msec,
engagement_time_msec / 1000 as engagement_time_sec,
user_id,
user_pseudo_id,
privacy_info,
user_first_touch_timestamp,
user_ltv,
device,
geo,
app_info,
traffic_source,
stream_id,
platform,
event_dimensions,
ecommerce,
items,
collected_traffic_source,
is_active_user,
`change-your-common-project.common.channel_grouping`(LOWER(traffic_source.source), LOWER(traffic_source.medium), LOWER(traffic_source.name)) as channel_group,
CASE WHEN event_name = 'page_view' THEN 1 ELSE 0 END as is_pv,
CASE WHEN event_name = 'page_view' AND entrances = 1 THEN 1 ELSE 0 END as is_landing,
CASE WHEN event_name = 'user_engagement' AND session_engaged = 1 THEN 1 ELSE 0 END as is_engaged,
CASE WHEN event_name IN ('purchase', '決æ¸å®äº') THEN 1 ELSE 0 END as is_cv,
FROM _raw2
),
_stats1 AS (
SELECT
date_jst,
COUNT(DISTINCT user_session_id) as stats_sessions,
COUNT(DISTINCT CASE WHEN is_pv = 1 THEN user_session_id END) as stats_pv_sessions,
COUNT(DISTINCT CASE WHEN session_engaged = 1 THEN user_session_id END) as stats_engaged_sessions,
FROM raw
GROUP BY date_jst
),
stats AS (
SELECT
date_jst,
stats_sessions,
stats_engaged_sessions,
stats_sessions - stats_engaged_sessions as stats_bounced_sessions,
SAFE_DIVIDE(stats_engaged_sessions, stats_sessions) as stats_engagement_rate,
SAFE_DIVIDE(stats_sessions - stats_engaged_sessions, stats_sessions) as stats_bounce_rate,
SAFE_DIVIDE(stats_pv_sessions, stats_sessions) as stats_event_count_per_session,
FROM _stats1
),
_engage1 AS (
SELECT
date_jst,
user_session_id,
SUM(CASE WHEN session_engaged = 1 THEN engagement_time_sec END) as session_engagement_time_sec,
FROM raw
GROUP BY date_jst, user_session_id
),
engage AS (
SELECT
t1.date_jst,
SAFE_DIVIDE(SUM(session_engagement_time_sec), MAX(stats_engaged_sessions)) as avg_engagement_time_sec,
FROM _engage1 t1
JOIN stats t2 ON t1.date_jst = t2.date_jst
GROUP BY t1.date_jst
),
_sess1 AS (
SELECT
date_jst,
user_session_id,
(MAX(event_timestamp) - MIN(event_timestamp)) / 1000000 as session_length_in_sec,
FROM raw
GROUP BY date_jst, user_session_id
),
sess AS (
SELECT
date_jst,
SAFE_DIVIDE(SUM(session_length_in_sec), COUNT(DISTINCT user_session_id)) as avg_session_duration_sec,
FROM _sess1
GROUP BY date_jst
),
_views1 AS (
SELECT
date_jst,
user_session_id,
SUM(CASE WHEN is_pv = 1 THEN 1 ELSE 0 END) as session_pv,
FROM raw
GROUP BY date_jst, user_session_id
),
viewscount AS (
SELECT
date_jst,
SAFE_DIVIDE(SUM(session_pv), COUNT(DISTINCT user_session_id)) as avg_views_per_session,
FROM _views1
GROUP BY date_jst
)
SELECT
raw.*,
st.stats_sessions,
st.stats_engaged_sessions,
st.stats_bounced_sessions,
st.stats_engagement_rate,
st.stats_bounce_rate,
st.stats_event_count_per_session,
e.avg_engagement_time_sec,
se.avg_session_duration_sec,
v.avg_views_per_session,
FROM raw
JOIN stats st ON st.date_jst = raw.date_jst
JOIN engage e ON e.date_jst = raw.date_jst
JOIN sess se ON se.date_jst = raw.date_jst
JOIN viewscount v ON v.date_jst = raw.date_jst
;
SQLã«å°ãã³ã¡ã³ãå
¥ãã¦ããã®ã§å
容ã¯ããã§ç¢ºèªãã¦ãã ããã
(1)
ã®ç®æã¯æ°¸ç¶é¢æ°ã使ã£ã¦ãã¾ããè¤æ°ã®GAããã¸ã§ã¯ãã§ä½¿ãåããããã«å¥ã®å
±éããã¸ã§ã¯ãã§å®ç¾©ãã¦ãã¾ãã
channel_grouping
ã¯ãã£ãã«åãæ´å½¢ããé¢æ°ã§ãã
CREATE OR REPLACE FUNCTION `change-your-common-project.channel_grouping`(tsource STRING, medium STRING, campaign STRING) AS (
CASE
WHEN (tsource = 'direct' OR tsource = '(direct)' OR tsource IS NULL)
AND (regexp_contains(medium, r'^(\(not set\)|\(none\))$') OR medium IS NULL)
THEN 'direct'
WHEN regexp_contains(campaign, r'^(.*(([^a-df-z]|^)shop|shopping).*)$')
AND regexp_contains(medium, r'^(.*cp.*|ppc|paid.*)$')
THEN 'paid_shopping'
WHEN regexp_contains(tsource, r'^(google|yahoo|bing)$')
AND regexp_contains(medium, r'^(.*cp.*|ppc|paid.*)$')
THEN 'paid_search'
WHEN regexp_contains(tsource, r'^(twitter|facebook|fb|instagram|ig|linkedin|pinterest)$')
AND regexp_contains(medium, r'^(.*cp.*|ppc|paid.*|social_paid)$')
THEN 'paid_social'
WHEN regexp_contains(tsource, r'^(youtube)$')
AND regexp_contains(medium, r'^(.*cp.*|ppc|paid.*)$')
THEN 'paid_video'
WHEN regexp_contains(medium, r'^(display|banner|expandable|interstitial|cpm)$')
THEN 'paid_display'
WHEN regexp_contains(medium, r'^(.*cp.*|ppc|paid.*)$')
THEN 'paid_other'
WHEN regexp_contains(medium, r'^(.*(([^a-df-z]|^)shop|shopping).*)$')
THEN 'organic_shopping'
WHEN regexp_contains(tsource, r'^.*(twitter|t\.co|facebook|instagram|linkedin|lnkd\.in|pinterest|tiktok).*')
or regexp_contains(medium, r'^(social|social_advertising|social-advertising|social_network|social-network|social network|social_media|social-media|social media|sm|social-unpaid|social_unpaid)$')
THEN 'organic_social'
WHEN regexp_contains(medium, r'^(.*video.*)$')
THEN 'organic_video'
WHEN regexp_contains(tsource, r'^(google|bing|yahoo|baidu|duckduckgo|yandex|ask)$')
or medium = 'organic'
THEN 'organic_search'
WHEN regexp_contains(tsource, r'^(email|mail|e-mail|e_mail|e mail|mail\.google\.com)$')
or regexp_contains(medium, r'^(email|mail|e-mail|e_mail|e mail)$')
THEN 'email'
WHEN regexp_contains(medium, r'^(affiliate|affiliates)$')
THEN 'affiliate'
WHEN medium = 'referral'
THEN 'referral'
WHEN medium = 'audio'
THEN 'audio'
WHEN medium = 'sms'
THEN 'sms'
WHEN ends_with(medium, 'push')
or regexp_contains(medium, r'.*(mobile|notification).*')
THEN 'mobile_push'
ELSE '(other)'
END
);
get_path_from_url
ã¯URLãããã¼ã¸ãã¹ãæãåºãé¢æ°ã§ãã
CREATE OR REPLACE FUNCTION `change-your-common-project.common.get_path_from_url`(url STRING) RETURNS STRING LANGUAGE js AS R"""
var path = '/' + url.split('/').splice(3).join("/")
return path.split('?')[0];
""";
get_query_from_url
ã¯URLããã¯ã¨ãªãã©ã¡ã¼ã¿ãæãåºãé¢æ°ã§ãã
utm_...
çã®ãã©ã¡ã¼ã¿ã¯ç¡è¦ããããã«ãã¦ãã¾ãã
CREATE OR REPLACE FUNCTION `change-your-common-project.common.get_query_from_url`(url STRING) RETURNS STRING LANGUAGE js AS R"""
parts = url.split('?');
if (parts.length < 2) return '';
var ignore_keys = {
'utm_source': true,
'utm_medium': true,
'utm_campaign': true,
'utm_content': true,
'utm_term': true,
'email': true,
'hash': true,
'gclid': true,
'fbclid': true,
'mc_cid': true,
'mc_eid': true,
'msclkid': true,
}
var results = []
var list = parts[1].split('&')
for (i = 0; i < list.length; i++) {
var item = list[i]
if (!ignore_keys[item.split('=')[0]]) {
results.push(item)
}
}
return results.join("&");
""";
ã¹ã±ã¸ã¥ã¼ã«ã¯ã¨ãªã®è¨å®
ä¸è¨ã®SQLã®ãã¹ãå®è¡ã¨ã¹ã±ã¸ã¥ã¼ã«ã¯ã¨ãªè¨å®ãè¡ãã¾ãã
ï¼BQ Data Transfer APIãEnableãã¦ãã ããï¼
å·¦ãµã¤ããã¼ãããã¹ã±ã¸ã¥ã¼ã«ãããã¯ã¨ãªããé¸æãããã®ã¾ã¾ä½æã«é²ã¿ã¾ãã
SQLå
¥åãã£ã¼ã«ãã§å
ã»ã©ã® 1-a
ã®SQLãå
¥åããä¸åº¦ãå®è¡ããã¦ã¿ã¦ã¨ã©ã¼ãçºçããªãã確ããã¾ãã
åé¡ãªãçµæãåºåãããããã¹ã±ã¸ã¥ã¼ã«ããé¸æãã¦è¨å®ãä¿åãã¾ãã
以ä¸ã¯è¨å®ã®ä¾ã§ãã
- ç¹°ãè¿ãã®é »åº¦: æ¥
- æå»: 03:00 (UTC)
- ãè¨å®ããæå»ã«éå§ã
- ç´è¿ã®æ¥ä»ã® 12:00 (JST)
- ãçµäºããªãã
- ãã¼ã¿ã»ãã: analytics
- Table Id: ga4_raw
- å®å
ãã¼ãã«ã®ãã¼ãã£ã·ã§ãã³ã°ãã£ã¼ã«ã: date_jst
- ããã¼ãã«ã«è¿½å ããã
ä¿åå¾ã«ããã¹ãå®è¡ãã¦ãã¼ã¿ãæ°ãããã¼ãã«ã«ä¿åãããã試ãã¾ãã
ä½æãããã¹ã±ã¸ã¥ã¼ã«ã¯ã¨ãªãé¸æãããããã¯ãã£ã«ã®ã¹ã±ã¸ã¥ã¼ã«æ§æããé¸ã³ã¾ãã
ã1åéãã®ã¹ã±ã¸ã¥ã¼ã«ãããã¯ã¨ãªããé¸ã¹ã°ãã¹ãå®è¡ã§ãã¾ãã
å®è¡ãã¦æ°åå¾
ã¤ã¨å®äºãã¦ããã¨æãã¾ãã
æåãã¦ããã°æ°ãããã¼ãã«ãä½æããã¦ããã¯ããªã®ã§ããã¼ã¿ã確èªãã¦ã¿ã¦ãã ããã
ã¾ããSQLä¸ã§ã³ã¡ã³ãã¢ã¦ããã¦ããéè¤æé¤ã®æ¡ä»¶ã追å ããã¨åå®è¡ãã¦ããã¼ã¿ãéè¤ããªãã®ã§ã¢ã³ã³ã¡ã³ããã¦åä¿åãã¦ããã¦ãã ããã
以éã® 2-b ~ 3-f ã®SQLã§ãåæ§ã«ã¹ã±ã¸ã¥ã¼ã«ã¯ã¨ãªãè¨å®ãã¦ãã ããã
å®è¡æéã ãã¯å¤ããå¿
è¦ãããã 1-a
以éã®æéï¼5åå¾ã®12:05ã¨ãï¼ã«ãã¦ãã ããã
2-b ~ 2-e 㯠1-a
以å¤ã«ä¾åé¢ä¿ãç¡ãå¥ã®ãã¼ãã«ãªã®ã§å
¨ã¦åãæéã«è¨å®ãã¦ãåé¡ããã¾ããã
æ¥æ¬¡ãµããªã¼ãã¼ãã«ã®ä½æ [2-b]
[1-a]
ã® ãã¼ãã«ã使ã£ã¦æ¥æ¬¡ãµããªã¼ãã¼ã¿ãä¿åãããã¼ãã«ãä½ãã¾ãã
CREATE TEMPORARY FUNCTION date_from() AS ( 4 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
WITH
raw AS (
SELECT
*,
FROM `analytics.ga4_raw`
WHERE date_jst BETWEEN DATE_SUB(cur_date(), INTERVAL date_from() DAY) AND DATE_SUB(cur_date(), INTERVAL date_to() DAY)
),
_stats1 AS (
SELECT
date_jst,
COUNT(DISTINCT user_session_id) as sum_sessions,
COUNT(DISTINCT user_pseudo_id) as sum_user,
COUNT(DISTINCT CASE WHEN ga_session_number = 1 THEN user_pseudo_id END) as sum_user_new,
COUNT(DISTINCT CASE WHEN is_pv = 1 THEN user_session_id END) as sum_pv_sessions,
COUNT(DISTINCT CASE WHEN session_engaged = 1 THEN user_session_id END) as sum_engaged_sessions,
COUNT(DISTINCT CASE WHEN is_cv = 1 THEN user_session_id END) as sum_cv_sessions,
COUNT(DISTINCT CASE WHEN is_cv = 1 THEN user_pseudo_id END) as sum_cv_users,
FROM raw
GROUP BY date_jst
),
stats AS (
SELECT
date_jst,
sum_sessions,
sum_engaged_sessions,
sum_sessions - sum_engaged_sessions as sum_bounced_sessions,
sum_user,
sum_user_new,
sum_user - sum_user_new as sum_user_old,
sum_cv_sessions,
sum_cv_users,
SAFE_DIVIDE(sum_engaged_sessions, sum_sessions) as avg_engagement_rate,
SAFE_DIVIDE(sum_sessions - sum_engaged_sessions, sum_sessions) as avg_bounce_rate,
SAFE_DIVIDE(sum_pv_sessions, sum_sessions) as avg_event_count_per_session,
SAFE_DIVIDE(sum_user_new, sum_user) as rate_new_user,
SAFE_DIVIDE(sum_cv_sessions, sum_sessions) as rate_cvr_per_session,
SAFE_DIVIDE(sum_cv_users, sum_user) as rate_cvr_per_user,
SAFE_DIVIDE(sum_sessions, sum_user) as avg_session_per_user,
FROM _stats1
),
_engage1 AS (
SELECT
date_jst,
user_session_id,
SUM(CASE WHEN session_engaged = 1 THEN engagement_time_sec END) as session_engagement_time_sec,
FROM raw
GROUP BY date_jst, user_session_id
),
engage AS (
SELECT
t1.date_jst,
SAFE_DIVIDE(SUM(session_engagement_time_sec), MAX(sum_engaged_sessions)) as avg_engagement_time_sec,
FROM _engage1 t1
JOIN stats t2 ON t1.date_jst = t2.date_jst
GROUP BY t1.date_jst
),
_sess1 AS (
SELECT
date_jst,
user_session_id,
(MAX(event_timestamp) - MIN(event_timestamp)) / 1000000 as session_length_in_sec,
FROM raw
GROUP BY date_jst, user_session_id
),
sess AS (
SELECT
date_jst,
SAFE_DIVIDE(SUM(session_length_in_sec), COUNT(DISTINCT user_session_id)) as avg_session_duration_sec,
FROM _sess1
GROUP BY date_jst
),
_views1 AS (
SELECT
date_jst,
user_session_id,
SUM(CASE WHEN is_pv = 1 THEN 1 ELSE 0 END) as sum_pv_sessions,
FROM raw
GROUP BY date_jst, user_session_id
),
viewscount AS (
SELECT
date_jst,
SUM(sum_pv_sessions) as sum_pv_sessions,
SAFE_DIVIDE(SUM(sum_pv_sessions), COUNT(DISTINCT user_session_id)) as avg_pv_per_session,
FROM _views1
GROUP BY date_jst
)
SELECT
st.date_jst,
st.sum_user,
st.sum_user_new,
st.sum_user_old,
st.rate_new_user,
st.sum_cv_sessions,
st.sum_cv_users,
st.rate_cvr_per_session,
st.rate_cvr_per_user,
st.sum_sessions,
st.sum_engaged_sessions,
st.sum_bounced_sessions,
st.avg_engagement_rate,
st.avg_bounce_rate,
st.avg_event_count_per_session,
st.avg_session_per_user,
e.avg_engagement_time_sec,
se.avg_session_duration_sec,
v.sum_pv_sessions,
v.avg_pv_per_session,
FROM stats st
JOIN engage e ON e.date_jst = st.date_jst
JOIN sess se ON se.date_jst = st.date_jst
JOIN viewscount v ON v.date_jst = st.date_jst
;
ãã®SQLã§ã¯ä»¥ä¸ã®ãããªãã¼ã¿ãåå¾ã§ãã¾ãã
ã«ã©ã åã§å¯ãã¦ãã ããã
date_jst |
sum_user |
sum_user_new |
sum_user_old |
rate_new_user |
sum_cv_sessions |
sum_cv_users |
rate_cvr_per_session |
rate_cvr_per_user |
sum_sessions |
sum_engaged_sessions |
sum_bounced_sessions |
avg_engagement_rate |
avg_bounce_rate |
avg_event_count_per_session |
avg_session_per_user |
avg_engagement_time_sec |
avg_session_duration_sec |
sum_pv_sessions |
avg_pv_per_session |
2024-12-01 |
1000 |
500 |
100 |
0.5025249169 |
5 |
5 |
0.0150195346 |
0.01790697674 |
1100 |
900 |
300 |
0.5048315983 |
0.2025168402 |
0.9557072872 |
1.105049834 |
100.1146624 |
150.9513281 |
5000 |
5.077770974 |
æ¥å¥ãã¼ã¸ãã¹ãã¼ãã«ã®ä½æ [2-c]
[1-a]
ã® ãã¼ãã«ã使ã£ã¦æ¥å¥ãã¼ã¸ãã¹ãã¼ã¿ãä¿åãããã¼ãã«ãä½ãã¾ãã
CREATE TEMPORARY FUNCTION date_from() AS ( 4 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
WITH
raw AS (
SELECT
*,
FROM `analytics.ga4_raw`
WHERE date_jst BETWEEN DATE_SUB(cur_date(), INTERVAL date_from() DAY) AND DATE_SUB(cur_date(), INTERVAL date_to() DAY)
),
page1 AS (
SELECT
date_jst,
page_path,
COUNT(DISTINCT user_session_id) as pv_session,
COUNT(DISTINCT user_pseudo_id) as pv_user,
COUNT(DISTINCT CASE WHEN is_landing = 1 THEN user_session_id END) as pv_first_page_session,
COUNT(DISTINCT CASE WHEN is_landing = 1 THEN user_pseudo_id END) as pv_first_page_user,
FROM raw
GROUP BY date_jst, page_path
),
_page2 AS (
SELECT
date_jst,
page_path,
CASE WHEN is_landing = 1 THEN page_path ELSE null END as first_page,
CASE WHEN is_landing = 1 THEN LEAD(page_path, 1) OVER (partition by user_session_id ORDER BY datetime_jst ASC) ELSE null END as second_page,
CASE WHEN page_path = first_value(page_path) OVER (partition by user_session_id ORDER BY datetime_jst DESC) THEN page_path ELSE null END as last_page,
FROM raw
),
page2 AS (
SELECT
date_jst,
page_path,
SUM(CASE WHEN page_path = second_page THEN 1 ELSE 0 END) as pv_second_page,
SUM(CASE WHEN page_path = last_page THEN 1 ELSE 0 END) as pv_last_page,
FROM _page2
GROUP BY date_jst, page_path
)
SELECT
p1.date_jst,
p1.page_path,
pv_session,
pv_user,
pv_first_page_session,
pv_first_page_user,
pv_second_page,
pv_last_page,
FROM page1 p1
JOIN page2 p2 ON p1.date_jst = p2.date_jst AND p1.page_path = p2.page_path
;
ãã®SQLã§ã¯ä»¥ä¸ã®ãããªãã¼ã¿ãåå¾ã§ãã¾ãã
date_jst |
page_path |
pv_session |
pv_user |
pv_first_page_session |
pv_first_page_user |
pv_second_page |
pv_last_page |
2024-12-01 |
/terms_of_use |
6 |
4 |
1 |
1 |
1 |
1 |
æ¥å¥ã¦ã¼ã¶ã¼ã¤ãã³ããã¼ãã«ã®ä½æ [2-d]
[1-a]
ã® ãã¼ãã«ã使ã£ã¦æ¥å¥ã¦ã¼ã¶ã¼ã¤ãã³ããã¼ã¿ãä¿åãããã¼ãã«ãä½ãã¾ãã
CREATE TEMPORARY FUNCTION date_from() AS ( 4 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
WITH
raw AS (
SELECT
*,
FROM `analytics.ga4_raw`
WHERE date_jst BETWEEN DATE_SUB(cur_date(), INTERVAL date_from() DAY) AND DATE_SUB(cur_date(), INTERVAL date_to() DAY)
),
_etl1 AS (
SELECT
date_jst,
user_session_id,
MIN(datetime_jst) as min_datetime_jst,
MAX(datetime_jst) as max_datetime_jst,
MAX(user_pseudo_id) as user_pseudo_id,
MIN(ga_session_number) as ga_session_number,
AVG(engagement_time_sec) as engagement_time_sec,
MAX(is_engaged) as is_engaged,
MAX(is_cv) as is_cv,
SUM(is_pv) as count_pv,
MAX(device.language) as device_language,
MAX(device.category) as device_category,
MAX(device.operating_system) as device_os,
MAX(device.operating_system_version) as device_os_version,
MAX(geo.country) as geo_country,
MAX(geo.region) as geo_region,
MAX(traffic_source.source) as user_traffic_source,
MAX(traffic_source.medium) as user_traffic_medium,
MAX(traffic_source.name) as user_traffic_name,
MAX(collected_traffic_source.manual_source) as session_traffic_source,
MAX(collected_traffic_source.manual_medium) as session_traffic_medium,
MAX(collected_traffic_source.manual_campaign_name) as session_traffic_name,
MAX(channel_group) as channel_group,
FROM raw
GROUP BY date_jst, user_session_id
)
SELECT
*
FROM _etl1
;
ãã®SQLã§ã¯ä»¥ä¸ã®ãããªãã¼ã¿ãåå¾ã§ãã¾ãã
date_jst |
user_session_id |
min_datetime_jst |
max_datetime_jst |
user_pseudo_id |
ga_session_number |
engagement_time_sec |
is_engaged |
is_cv |
count_pv |
device_language |
device_category |
device_os |
device_os_version |
geo_country |
geo_region |
user_traffic_source |
user_traffic_medium |
user_traffic_name |
session_traffic_source |
session_traffic_medium |
session_traffic_name |
channel_group |
2024-12-01 |
100000000 |
2024-12-01 0:04:41 |
2024-12-01 0:05:10 |
100000000 |
1 |
3.181 |
0 |
0 |
1 |
en-us |
desktop |
Linux |
Linux 6.9.0 |
Japan |
Tokyo |
facebook.com |
referral |
(referral) |
facebook.com |
referral |
(referral) |
organic_social |
æ¥å¥ã¦ã¼ã¶ã¼å¥ãã¡ãã«ééçãã¼ãã«ã®ä½æ [2-e]
CREATE TEMPORARY FUNCTION date_from() AS ( 4 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
WITH
raw AS (
SELECT
*,
FROM `analytics.ga4_raw`
WHERE date_jst BETWEEN DATE_SUB(cur_date(), INTERVAL date_from() DAY) AND DATE_SUB(cur_date(), INTERVAL date_to() DAY)
),
_data AS (
SELECT
date_jst,
user_pseudo_id,
MIN(datetime_jst) as min_datetime_jst,
MAX(datetime_jst) as max_datetime_jst,
MIN(ga_session_number) as ga_session_number,
SUM(is_pv) as count_pv,
MAX(device.language) as device_language,
MAX(device.category) as device_category,
MAX(device.operating_system) as device_os,
MAX(device.operating_system_version) as device_os_version,
MAX(geo.country) as geo_country,
MAX(geo.region) as geo_region,
MAX(traffic_source.source) as user_traffic_source,
MAX(traffic_source.medium) as user_traffic_medium,
MAX(traffic_source.name) as user_traffic_name,
MAX(collected_traffic_source.manual_source) as session_traffic_source,
MAX(collected_traffic_source.manual_medium) as session_traffic_medium,
MAX(collected_traffic_source.manual_campaign_name) as session_traffic_name,
MAX(channel_group) as channel_group,
COUNT(DISTINCT CASE WHEN page_path = '/cart' AND page_query LIKE '%page=1%' THEN 1 ELSE NULL END) as is_cart1,
COUNT(DISTINCT CASE WHEN page_path = '/cart' AND page_query LIKE '%page=2%' THEN 1 ELSE NULL END) as is_cart2,
COUNT(DISTINCT CASE WHEN page_path = '/cart' AND page_query LIKE '%page=3%' THEN 1 ELSE NULL END) as is_cart3,
COUNT(DISTINCT CASE WHEN page_path = '/cart' AND page_query LIKE '%page=4%' THEN 1 ELSE NULL END) as is_cart4,
COUNT(DISTINCT CASE WHEN page_path = '/cart/thanks' THEN 1 ELSE NULL END) as is_thanks,
FROM raw
GROUP BY date_jst, user_pseudo_id
)
SELECT
*
FROM _data
;
ãã®SQLã§ã¯ä»¥ä¸ã®ãããªãã¼ã¿ãåå¾ã§ãã¾ãã
date_jst |
user_pseudo_id |
min_datetime_jst |
max_datetime_jst |
ga_session_number |
count_pv |
device_language |
device_category |
device_os |
device_os_version |
geo_country |
geo_region |
user_traffic_source |
user_traffic_medium |
user_traffic_name |
session_traffic_source |
session_traffic_medium |
session_traffic_name |
channel_group |
is_cart1 |
is_cart2 |
is_cart3 |
is_cart4 |
is_thanks |
2024-12-14 |
100000000 |
2024-12-01 17:24:39 |
2024-12-01 17:24:43 |
1 |
1 |
ja |
mobile |
iOS |
iOS 17.6.1 |
Japan |
Tokyo |
mail.google.com |
referral |
(referral) |
mail.google.com |
referral |
(referral) |
email |
1 |
0 |
0 |
0 |
0 |
ãã¡ãã«ééçã®éè¨ãã¼ãã«ä½æ [3-f]
æå¾ã« 2-e ã使ã£ã¦ãã¡ãã«ééçã®éè¨ãã¼ãã«ãä½ãã¾ãã
ãã¡ãã«ééçãCVRã®æ¥å¥å¹³åã3æ¥é移åå¹³åã7æ¥é移åå¹³åãä¿åãã¾ãã
CREATE TEMPORARY FUNCTION date_from() AS ( 15 );
CREATE TEMPORARY FUNCTION date_to() AS ( 1 );
CREATE TEMPORARY FUNCTION cur_date() AS ( CURRENT_DATE("Asia/Tokyo") );
WITH
avg_data AS (
SELECT
date_jst,
COUNT(user_pseudo_id) as user_count,
SUM(is_thanks) as cv_count,
SUM(is_thanks) / COUNT(user_pseudo_id) as cvr,
AVG(is_cart1) as is_cart1,
AVG(is_cart2) as is_cart2,
AVG(is_cart3) as is_cart3,
AVG(is_cart4) as is_cart4,
AVG(is_thanks) as is_thanks,
FROM
`analytics.ga4_funnel_cv`
WHERE date_jst BETWEEN DATE_SUB(cur_date(), INTERVAL date_from() DAY) AND DATE_SUB(cur_date(), INTERVAL date_to() DAY)
GROUP BY date_jst
),
cvr_data AS (
SELECT
date_jst,
AVG(is_cart1) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as is_cart1_avg7,
AVG(is_cart2) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as is_cart2_avg7,
AVG(is_cart3) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as is_cart3_avg7,
AVG(is_cart4) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as is_cart4_avg7,
AVG(is_thanks) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as is_thanks_avg7,
AVG(cvr) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW) as cvr_avg7,
AVG(is_cart1) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as is_cart1_avg3,
AVG(is_cart2) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as is_cart2_avg3,
AVG(is_cart3) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as is_cart3_avg3,
AVG(is_cart4) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as is_cart4_avg3,
AVG(is_thanks) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as is_thanks_avg3,
AVG(cvr) OVER(ORDER BY UNIX_DATE(date_jst) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) as cvr_avg3,
FROM
avg_data
)
SELECT
avg_data.date_jst,
user_count,
cv_count,
cvr,
is_cart1,
is_cart2,
is_cart3,
is_cart4,
is_thanks,
is_cart1_avg7,
is_cart2_avg7,
is_cart3_avg7,
is_cart4_avg7,
is_thanks_avg7,
cvr_avg7,
is_cart1_avg3,
is_cart2_avg3,
is_cart3_avg3,
is_cart4_avg3,
is_thanks_avg3,
cvr_avg3,
FROM
avg_data
JOIN cvr_data ON avg_data.date_jst = cvr_data.date_jst
;
ãã®SQLã§ã¯ä»¥ä¸ã®ãããªãã¼ã¿ãåå¾ã§ãã¾ãã
date_jst |
user_count |
cv_count |
cvr |
is_cart1 |
is_cart2 |
is_cart3 |
is_cart4 |
is_thanks |
is_cart1_avg7 |
is_cart2_avg7 |
is_cart3_avg7 |
is_cart4_avg7 |
is_thanks_avg7 |
cvr_avg7 |
is_cart1_avg3 |
is_cart2_avg3 |
is_cart3_avg3 |
is_cart4_avg3 |
is_thanks_avg3 |
cvr_avg3 |
2024-12-01 |
1000 |
20 |
0.0162601626 |
0.112195122 |
0.06097560976 |
0.04471544715 |
0.02195121951 |
0.0162601626 |
0.1180583458 |
0.07179818466 |
0.04718445738 |
0.02393212923 |
0.01794440009 |
0.01794440009 |
0.1063511487 |
0.06086483479 |
0.04442387541 |
0.02290562374 |
0.01832657378 |
0.01832657378 |
ããã§æºåã¯æ´ãã¾ããã
Looker Studio ã§ã°ã©ãä½æ
ãã¨ã¯Looker Studioã§å¯è¦åããã ãã§ãã
説æä¸ãã¤ãããä½æãã¾ããä»ã®æ¹ã
ãå
¬éãã¦ããGAç¨ã®ãã³ãã¬ããã¼ã¹ã«ä½ã£ãã»ãã楽ã ã¨æãã¾ãã
ä»ã®ãã³ãã¬ã使ãå ´åã¯ã³ãã¼ãã¦ããã¼ã¿ã½ã¼ã¹ãç´GAããBQã«å¤æ´ãã¦ãããããã¨ã°ã©ãã¨ææ¨ãè¨å®ãã¦ããã¤ã¡ã¼ã¸ã§ãã
lookerstudio.google.com
ä¸å¿1ã°ã©ãåã ã軽ã説æãã¾ãã
ä¾ã¨ãã¦ãã¡ãã«ã®7æ¥é移åå¹³åã®æ¨ç§»ãè¦ãããã®ã°ã©ããä½æãã¦ããã¾ãã
ã¾ãã¯Looker Studioã«ã¢ã¯ã»ã¹ããã空ã®ã¬ãã¼ãããä½æãã¾ãã
ãã¼ã¿æ¥ç¶ã¯BigQueryãé¸æãã
ãã¼ãã«ã¯å
ç¨ä½ã£ãBQã®ãã¼ãã«ã®ãããããé¸æãã¦ã¾ããï¼å¾ã§ä»ã®ãã¼ãã«ã追å ã§ãã¾ãï¼
date_jst ã¯æéãã£ã¡ã³ã·ã§ã³ã¨ãã¦ä½¿ãããã«ãã¾ãã
ããã¦ãã°ã©ããã®é¨åãé¸æãã¦è¡¨å½¢å¼ããæéã°ã©ãã«å¤æ´ãã¾ãã
ãã®å¾ãææ¨ãé©å½ã«é¸æããã¨ããã£ã½ãã°ã©ããåºæ¥ä¸ããã¾ãã
ãã¨ã¯è¦ãããæ´å½¢ããããã¦ããã¨æçµçã«ã¯ãããªæãã«ãªãã¾ãã
ã¹ããã1ããã®å°éç㯠SUM(is_cart3) / SUM(is_cart1)
ã
次ã¹ãããééç㯠SUM(is_cart3) / SUM(is_cart2)
ã¿ãããªæãã§è¨ç®å¼ãè¨å®ãã¦ãã¾ãã
æ°åã®å¾åãå¤ãã£ã¦ããé¨åã§éå»ã«ãªãªã¼ã¹ããå±¥æ´ãè¦ãããã¦ããã®å½±é¿ã調ã¹ããã§ããããã«ãªãã¾ããã
ããã§GAã§è¦ã¦ãããã¼ã¿ãBQ+Lookerã«ç§»ããã¨ãã§ããã¨ã³ã¸ãã¢ããã¼ã±ããã¸ãã¹ããªãã¬ã¼ã·ã§ã³ãåãããã·ã¥ãã¼ããåç
§ãã¦è°è«ãããããªããã¨æãã¾ãã
ãªãGAã®å®å
¨ãªä»£æ¿ã§ã¯ãªãã®ã§ãã¢ãããã¯ãªåæã¯æ®éã«æ¢ç´¢ã¬ãã¼ã使ã£ãæ¹ãæ©ãå ´åãããã®ã§ã
GAãSQLã両æ¹ä½¿ããã¨è¯ããã¨æãã¾ãã