关于分析:使用-Amazon-Athena-做漏斗分析实现更高效的数据湖检索

27次阅读

共计 13574 个字符,预计需要花费 34 分钟才能阅读完成。

在日常的业务经营过程中,管理者经常须要疾速理解业务的运行健康状况,辨认出瓶颈和问题,并制订应答的打算。要概览业务全景,咱们能够借助一些统计工具。「漏斗剖析」就是一种常见的工具,它很适宜多个环节按工夫程序串联的业务。

现实生活中,很多业务都是多环节串联。比方电商用户可能会浏览举荐的物品、比价、退出购物车、下单购买、评估;再比方培训机构的客户可能会看到广告、征询、试课、购买、正式上课、续费。

对于这类业务,咱们能够把触达每个环节的人数统计进去,并造成一个逐步递加的「漏斗」,就能看到每个环节的转化状况,定位到目前转化次要是卡在哪个步骤,再针对这个步骤补充定质的调研来确定解决方案。

在这篇文章中,咱们将应用 Amazon Athena 来编写这样的一个漏斗剖析工具,把一系列的时序数据(拜访日志)转化成为每个环节的数量,再把数量转化成递加的漏斗。除了展现最终成果之外,我也会展现整个设计过程,帮忙读者调整、设计本人的 SQL 语句。

📢  09.09-09.14 亚马逊云科技中国峰会线上峰会正在炽热进行中!想要理解更多亚马逊云科技最新技术公布和实际翻新,点击图片报名吧~

Amazon Athena 介绍

Amazon Athena 是数据湖查问服务。它让用户能够应用 SQL 语句对存在 Amazon S3 上的半结构化数据(JSON、CSV、Parquet 等)进行查问。此外,它还是无服务器的服务,这意味着用户无需关怀底层硬件资源,仅依照扫描数据的数量来进行免费。不扫描则没有其余闲置费用。

Amazon Athena 是 基于 Presto 实现 的。用户能够应用 Presto 的 SQL 语法和局部内置函数进行查问。

  • 基于 Presto 实现
    https://docs.aws.amazon.com/a…

漏斗剖析介绍

接下来咱们来看漏斗剖析。

在本文中,「漏斗剖析」指的是:统计触达业务流程上每个关键环节的用户人数,并剖析每个步骤的留存、转化、跳出率,以找到转化瓶颈。

漏斗剖析蕴含的输出如下:

  • 剖析者关怀的事件门路。比方咱们关怀「注册、浏览、下单」,那用户必须严格依照这个程序来执行每个环节;例如:用户能够只执行「注册」,算走了 1 步,或者执行「注册、浏览」,算走了 2 步,但「注册、搜寻、下单」只能算走了 1 步,因为「搜寻」不在咱们关怀的门路内,从而打断了漏斗。
  • 漏斗的工夫区间。比方设置为 10 天,则漏斗蕴含的环节必须在 10 天内走完,如果用户第 1 天注册了,也浏览了,然而始终到第 20 蠢才下单,那么这个也只能算走了 1 步。

漏斗剖析的输入有两个:

  • 漏斗步骤计数(FUNNEL_COUNT)。比方 A 用户走了 1 步,B 用户走了 3 步,C 用户走了 2 步,等等。
  • 漏斗人数统计(FUNNEL_SUM)。比方走到第 1 步的有 1000 人,这其中走到第 2 步的有 300 人,而这其中走到第 3 步的又有 50 人,等等。

测试数据筹备

我提前准备好了测试数据。数据结构如下:

数据字段如下。

  • event_name 是事件名字,中英文均可
  • user_id 是用户名
  • timestamp 是工夫戳

数据是 JSON 格局,应用 Glue 爬虫进行爬取,录入为数据表,读者也能够自行创立内部表。如果手上没有现成数据,也能够应用 generatedata.com 等模仿数据生成工具来生成简略的测试数据。

漏斗语句设计过程

接下来咱们来看如何一步步设计出漏斗剖析语句。

简略分类统计

先来做步骤计数。先按用户来计算事件数量,这是一个分类统计的操作。既然是分类统计,咱们直觉可能就会想到应用 GROUP BY 语句,比方:

SELECT user_id, COUNT(1) AS events_count
FROM events_table
GROUP BY user_id

这个语句的意思是,计算每个用户别离触发了多少事件。语句执行后,后果如下图:

限度工夫窗口

这里有一个问题,就是它会统计这个用户所有触发过的事件,包含超出了咱们设定的事件窗口范畴的事件,所以,咱们还须要做一次过滤。比方咱们设定的工夫区间是 5 天,那么上面示意图中 2020-11-23 当前的事件就必须被过滤掉。

须要留神的是,这个要求无奈用简略的条件判断来解决,因为窗口的起始工夫须要依据用户触发第一条事件的工夫而定。比方用户 A 是 11 月 21 日触发第一条事件,那么窗口就从 11 月 21 日开始,而用户 B 是 12 月 1 日触发第一条事件,这个窗口则从 12 月 1 日开始。

要做到这一点,咱们须要借助一个两头表。这个两头表只蕴含用户 ID 和这个用户的窗口完结工夫,而后再用这个表来联结原来的事件表,从而能够用一个简略的条件判断来过滤掉超过工夫窗口的数据。

蕴含窗口完结的两头表语句如下:

SELECT user_id,
  DATE_ADD('second', 3600 * 24 * 5, MIN(timestamp)) AS max_span
FROM events_table
GROUP BY user_id

咱们首先应用 GROUP BY 依照把每个用户的事件分组,而后应用 MIN() 函数获取每个用户的第一条事件的工夫,再应用 DATE_ADD() 函数,在这个工夫的根底上减少了 5 天(假如窗口是 5 天),就失去了每个用户窗口完结的工夫。

把这个表和简略统计的表联结起来,语句如下:

WITH max_spans_table AS
(
  SELECT user_id,
    DATE_ADD('second', 3600 * 24 * 5, MIN(timestamp)) AS max_span
  FROM events_table
  GROUP BY user_id
)
SELECT e.user_id, COUNT(1) AS event_count
FROM events_table e
JOIN max_spans_table m ON e.user_id = m.user_id
WHERE e.timestamp <= m.max_span
GROUP BY e.user_id

能够看出,这有点相似给第一个表做了一个遍历,并且给用户 ID 雷同的条目加上了一个 max_span 参数,而后过滤掉了小于等于这个参数的行。

指定行为门路

不过这样还是有问题。尽管咱们过滤了窗口外的数据,但并没有方法指定事件门路。咱们关怀的漏斗可能是「注册、浏览、下单」,但用户的行为可能是「搜寻、浏览、下单」。咱们还须要指定行为门路。

要在 SQL 语句外面对几条数据的程序进行判断并不容易。因为 SQL 本来是用于操作和查问汇合的,所以对程序并不敏感。不过,还好事件名称只是简略的字符串,所以咱们能够采纳取巧的做法,把用户在某个工夫窗口内的事件全副拼接成一个长的字符串,而后和咱们预期的门路进行比照。

要把多条数据中的字段拼接到一起,咱们须要两个函数。首先是 ARRAY_AGG(),用于把多条数据中的某个字段值取出来,组成一个数组。而后是 ARRAY_JOIN(),用于把数组中的字符串拼接到一起。

这里,咱们假如咱们关怀的门路是「注册、询问客服、退出购物车、下单」。

此时的语句如下:

WITH max_spans_table AS
(
  SELECT user_id,
    DATE_ADD('second', 3600 * 24 * 5, MIN(timestamp)) AS max_span
  FROM events_table
  GROUP BY user_id
)
SELECT e.user_id, ARRAY_JOIN(ARRAY_AGG(e.event_name ORDER BY e.timestamp ASC), ',') AS event_seq, COUNT(1) AS event_count
FROM events_table e
JOIN max_spans_table m ON e.user_id = m.user_id
WHERE e.timestamp <= m.max_span
GROUP BY e.user_id
HAVING ARRAY_JOIN(ARRAY_AGG(e.event_name), ',') = 'REGISTER,INQUIRY,CART,ORDER'

留神尽管咱们在 SELECT 时应用了别名 event_seq,然而给列取别名这个动作是在查问实现后在后果集中执行,所以 HAVING 语句中还是须要反复一遍 ARRAY_JOIN() 函数。此外,在 ARRAY_AGG() 函数中,咱们还用 timestamp 字段做了排序,确保这些事件名字依照工夫程序拼接。

查问后果如下:

计算用户的漏斗门路长度

察看后果,会发现只有 7 个用户,而咱们的测试数据集中有数千个用户,这显著不失常。究其原因,是因为咱们只简略地比对拼接好的字符串和输出的行为门路,就只能查到全副门路都走完的用户。

比方,咱们设定的输出是「A,B,C」,那么按程序正好触发了这三个事件的用户就会拼接出「A,B,C」字符串从而比对胜利。而只触发了 A,或者 A、B 的用户则无奈匹配上。此外,A 事件如果不是第一个事件,而是在两头(「D,A,B,C」),又或者在指标事件门路后又触发了别的事件(「A,B,C,D」),简略的匹配都无奈比照到。

此时咱们须要做两件事:

  • 找出某个用户的行为门路中第一个事件(比方「A」)所在的行,过滤掉它之前的行,否则门路在两头或者尾部就无奈匹配(退一步说,即使能够匹配,咱们也无奈正确计算窗口工夫)
  • 只保留能以「A」「A,B」和「A,B,C」结尾的事件门路,这是咱们关怀的指标门路

这时候咱们就会涉及 SQL 语言的表现力天花板。在一般编程语言中要做到这两件事很容易,因为咱们能够屡次循环,应用长期变量,再按须要过滤。而要在 SQL 中做到这样的过滤,就须要曲线救国了。

要做到第一点,咱们须要把每个用户的事件独自提出来,删掉咱们输出行为门路的第一个事件之前的其余事件。拿下图为例,如果咱们关怀的事件门路第 1 步是「注册」,那么在「注册」之前的步骤就都要删掉。

好在 SQL 中有提供「窗口函数」这样的概念。咱们能够把每个用户的事件做成一个「窗口」,并且用 ROW_NUMBER() 函数给窗口中的每条数据一个行编号。接下来,咱们要找到第一个事件所在的行编号,记录下来。而后,再联结原表,过滤掉这个用户下编号小于咱们记录下的编号的行。这和后面的工夫窗口过滤殊途同归。

窗口函数局部语句如下:

SELECT *,
  ROW_NUMBER() OVER (PARTITION BY id ORDER BY timestamp ASC) AS row_number
FROM events_table

应用 OVER () 语句,就造成了一个「窗口」,窗口由 PARTITION BY 批示的字段来作为划分,并且窗口内的数据条目能够排序。这有点像 GROUP BY 的逻辑,只不过 GROUP BY 只能用来做统计,而窗口函数则能够不做统计,只附加行编号。

执行后果如下:

能够看出,咱们给每个用户本人的事件进行了编号,后续只须要查找到咱们关怀的事件所在的编号并联结原表做过滤即可。

过滤事件做到了,要做到第二点,多重匹配,则绝对麻烦。

兴许你会想到 LIKE‘%%’等通配语句,但它只能匹配残缺蕴含全副事件步骤的门路,无奈匹配仅蕴含局部步骤的门路。如果应用 STRPOS(),再把两个参数反过来,那么的确能够匹配「A」「A,B」「A,B,C」以及「A,B,C,D,E」,然而却无奈匹配「A,E,F,G」这样只以「A」结尾的但后续是其余事件的门路。

幸好,咱们还有 REDUCE() 函数,能够循环解决数组数据。只需先把行为门路字符串拆成一个数组,而后利用 REDUCE() 函数,应用递归的形式,把后一个字符串附加在前一个后果上,就能失去蕴含「A」「A,B」「A,B,C」等门路子集的数组,从而进行多重匹配。

要做到这一点,咱们除了须要拿到数组中的字符串值,还须要拿到这个值所对应的下标。如果下标为 1 阐明是第一个元素,一成不变,否则把以后的字符串取出来,和上一轮的字符串合并,增加到输入数组内。这个用 IF() 函数来做。

REDUCE() 函数自身没有提供下标参数,所以咱们须要借助 CARDINALITY() 函数获取数组长度,再用 SEQUENCE() 函数来生成一个程序数字的数组作为下标列表。遍历这个数组,就达到了相似其余语言的 for 循环或者 enumerate() 的成果。

这部分的语句如下:

SELECT REDUCE(SEQUENCE(1, CARDINALITY(event_steps)), ARRAY [], 
    (s, i) -> 
      IF(
        i > 1, 
        s || (CAST(s[i-1] AS VARCHAR) || ',' || event_steps[i]),
        s || event_steps[i]), 
    s -> s) AS event_step_combos
FROM (SELECT SPLIT('REGISTER,INQUIRY,CART,ORDER', ',') AS event_steps
)

这里用到了 CAST() 函数来把数组值设定为字符串,再应用 || 操作符来做字符串的连贯。

上面是本轮的输入示例。因为控制台输入没有明确表明字符串,所以我把输入的局部用红线标记辨别了一下。能够看出输入的数组中蕴含四个字符串,合乎咱们的要求。

不过,尽管有了这样一个数组,咱们还须要对所有的事件门路做匹配,并且记录下匹配到的到底是「A」、「A,B」还是「A,B,C」,这样咱们能力做后续的统计。要做到这一点,咱们须要联合几个函数。

首先还是 REDUCE() 和 SEQUENCE() 函数,用于执行循环操作,最初输入一个数字,代表到底匹配到哪一个字符串。最初是 IF(),用于判断是否曾经匹配到,如果曾经匹配到,则维持原输出什么都不做,而如果没匹配到,则应用 STRPOS() 进行匹配。

因为一旦匹配出后果,就不会再进行匹配,所以咱们必须先匹配最长的字符串。这就要求咱们应用 REVERSE() 函数把字符串依照从长到短,反向排列。

这又引发一个问题,那就是因为咱们的输出时数组反过来了,所以得出的数组下标也是反的。咱们还必须再它下标反过来,也即是把下标 1、2、3、4 换成 4、3、2、1。通过剖析可知,用数组长度减去下标再加 1 就能够把下标程序反过来,所以咱们把这部分增加下来。

把后面局部代码都交融到一起,此时代码如下:

WITH 
-- 拆分输出的事件门路
params AS
(
  SELECT REVERSE(REDUCE(SEQUENCE(1, CARDINALITY(event_steps)), ARRAY [], 
      (s, i) -> 
        IF(
          i > 1, 
          s || (CAST(s[i-1] AS VARCHAR) || ',' || event_steps[i]),
          s || event_steps[i]), 
      s -> s)) AS event_step_combos
  FROM (SELECT SPLIT('REGISTER,INQUIRY,CART,ORDER', ',') AS event_steps
  )
),
-- 给各个用户触发的事件进行独立行编号
events_with_row_numbers_table AS
(
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY timestamp ASC) AS row_number
  FROM events_table
),
-- 找到用户触发的漏斗中的第一个事件并记录行编号
events_starter_event_with_row_number AS (SELECT user_id, MAX(row_number) AS starter_event_row_number
  FROM events_with_row_numbers_table
  WHERE event_name = 'REGISTER'
  GROUP BY user_id
),
-- 过滤掉漏斗中第一个事件之前的事件
events_trimmed_table AS (
  SELECT e.user_id, e.event_name, e.timestamp
  FROM events_with_row_numbers_table e
  JOIN events_starter_event_with_row_number er ON e.user_id = er.user_id
  WHERE e.row_number >= er.starter_event_row_number
),
-- 找到工夫窗口起点
max_spans_table AS
(
  SELECT user_id,
    DATE_ADD('second', 3600 * 24 * 5, MIN(timestamp)) AS max_span
  FROM events_trimmed_table
  GROUP BY user_id
),
-- 把用户的事件组合成一个字符串
events_seq_table AS
(
  SELECT e.user_id, 
    ARRAY_JOIN(ARRAY_DISTINCT(ARRAY_AGG(e.event_name ORDER BY e.timestamp ASC)), ',') AS event_seq
  FROM events_trimmed_table e
  JOIN max_spans_table m ON e.user_id = m.user_id
  WHERE e.timestamp <= m.max_span
  GROUP BY e.user_id
)
-- 找出用户停在了哪一步
SELECT e.user_id, e.event_seq, REDUCE(SEQUENCE(1, CARDINALITY(p.event_step_combos)), 
  0, 
  (s, i) -> IF(
    s > 0, 
    s, 
    IF(STRPOS(e.event_seq, p.event_step_combos[i]) = 1, 
      CARDINALITY(p.event_step_combos)-i+1, 
      0
    )
  ), 
  s -> s
) AS funnel_step
FROM events_seq_table e
CROSS JOIN params p

能够看出因为两头表的呈现和函数的减少,查问语句曾经变得很长了。我退出了正文,帮忙读者看清楚每个两头表的目标。

此时后果如下:

能够看出,能匹配门路中的几个步骤,funnel_step 就是几。这样,咱们就取得了每个用户走过的事件门路长度。

漏斗人数统计

接下来咱们对每个步骤的人数做统计。

咱们先计算每个步骤的用户数量,这是一个简略的统计。这部分代码如下:

SELECT COUNT(1) AS funnel_step_count
FROM events_funnel_step_table
GROUP BY funnel_step

可这个语句得出的后果是每个步骤独自的人数,但咱们须要的不是独自的人数,而是漏斗,所以咱们心愿最初出现的是环环相扣的统计。比方 1000 集体注册,其中 200 集体浏览,其中 15 集体下单。这意味着咱们须要对步骤做累加。

这时候咱们又须要借助窗口函数。窗口函数中有个非凡版的 SUM() 函数,这个版本的 SUM() 函数会把原来的「求和」变成「窗口间累加」,这合乎咱们的须要。此时咱们省略了 PARTITION BY 语句,这就意味着每条记录本人就是一个窗口,而 SUM() 也变成了「按记录累加」的意思。

这部分代码如下:

SELECT 
  SUM(funnel_step_count) 
    OVER (ORDER BY funnel_step ASC) 
    AS funnel_step_acc_sum
FROM events_funnel_step_count_table

察看后果咱们会发现,这和咱们想的漏斗反过来了。因为漏斗是越来越少,而累加则是越来越多,咱们须要用一个总数,逐渐减去这个累加值。

这部分代码如下:

SELECT (funnel_total_sum-funnel_step_acc_count) AS funnel_step_converts
FROM events_funnel_step_acc_count_table
CROSS JOIN (SELECT SUM(funnel_step_count) AS funnel_total_sum
  FROM events_funnel_step_count_table
)

再次察看后果咱们会发现,此时的漏斗最初会变成 0。这是因为咱们的累加是从 0 开始,最初也会减到 0,而咱们实际上心愿只减到倒数第二步而后进行。换句话说,就是咱们要用本行的值,去减上一行的累加值。

对于 SQL 来说,这就意味着一个表对本身做一个联结,然而要错开一行。用语句来示意如下:

SELECT e.funnel_step, (funnel_total_sum-e2.funnel_step_acc_count) AS funnel_step_converts
FROM events_funnel_step_acc_count_table e
LEFT JOIN events_funnel_step_acc_count_table e2 ON e.funnel_step = e2.funnel_step+1
CROSS JOIN (SELECT SUM(funnel_step_count) AS funnel_total_sum
  FROM events_funnel_step_count_table
)

后果如下。

察看后果又会发现一个问题,那就是第 1 行的值是空的。这是因为当左表是第 1 行时,右表的上一行不存在,所以 LEFT JOIN 的后果是 NULL,而针对 NULL 做数学运算,后果也只能是 NULL。

为解决这个问题,咱们应用 COALESCE() 函数。这个函数能够帮忙咱们设置默认值。它会返回它的参数中第一个非 NULL 的值。咱们把这个默认值设置为 0,这样,咱们就能够对它进行失常计算。

加上后语句如下:

SELECT e.funnel_step, (funnel_total_sum-COALESCE(e2.funnel_step_acc_count, 0)) AS funnel_step_converts
FROM events_funnel_step_acc_count_table e
LEFT JOIN events_funnel_step_acc_count_table e2 ON e.funnel_step = e2.funnel_step+1
CROSS JOIN (SELECT SUM(funnel_step_count) AS funnel_total_sum
  FROM events_funnel_step_count_table
)
ORDER BY funnel_step ASC

成果如下:

参数提取

当初咱们的指标就达成了。还剩下最初一个问题,那就是咱们须要在不同语句,屡次手动插入咱们的参数。最好是咱们能把参数都一次写到第一条语句中,应用起来更不便。

要做到这一点,咱们应用 VALUES 来创立一个只有一行的长期表用于存储参数,而后用 AS 来给字段命名。应用时只须要从这个长期表 SELECT 须要的字段即可。

至此,咱们用 Amazon Athena 的 SQL 编写了漏斗剖析统计函数。因为 Amazon Athena 的底层是基于 Presto,所以这个语句也能够运行于兼容版本的 SQL 引擎上。

最终残缺 SQL 语句如下:

WITH 
-- 原始参数
input AS
(SELECT time_window, SPLIT(event_chain, ',') AS event_steps
  FROM (VALUES ('REGISTER,INQUIRY,CART,ORDER', 3600 * 24 * 5))
  AS input (event_chain, time_window)
),
-- 拆分输出的事件门路
params AS
(
  SELECT time_window, event_steps, REVERSE(
    REDUCE(SEQUENCE(1, CARDINALITY(event_steps)), ARRAY [], 
      (s, i) -> 
        IF(
          i > 1, 
          s || (CAST(s[i-1] AS VARCHAR) || ',' || event_steps[i]),
          s || event_steps[i]), 
      s -> s
    )
  ) AS event_step_combos
  FROM input
),
-- 给各个用户触发的事件进行独立行编号
events_with_row_numbers_table AS
(
  SELECT *,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY timestamp ASC) AS row_number
  FROM events_table
),
-- 找到用户触发的漏斗中的第一个事件并记录行编号
events_starter_event_with_row_number AS (SELECT user_id, MAX(row_number) AS starter_event_row_number
  FROM events_with_row_numbers_table
  WHERE event_name = (SELECT event_steps[1] FROM params)
  GROUP BY user_id
),
-- 过滤掉漏斗中第一个事件之前的事件
events_trimmed_table AS (
  SELECT e.user_id, e.event_name, e.timestamp
  FROM events_with_row_numbers_table e
  JOIN events_starter_event_with_row_number er ON e.user_id = er.user_id
  WHERE e.row_number >= er.starter_event_row_number
),
-- 找到工夫窗口起点
max_spans_table AS
(
  SELECT user_id,
    DATE_ADD('second', (SELECT time_window FROM params), MIN(timestamp)) AS max_span
  FROM events_trimmed_table
  GROUP BY user_id
),
-- 把用户的事件组合成一个字符串
events_seq_table AS
(
  SELECT e.user_id, 
    ARRAY_JOIN(ARRAY_DISTINCT(ARRAY_AGG(e.event_name ORDER BY e.timestamp ASC)), ',') AS event_seq
  FROM events_trimmed_table e
  JOIN max_spans_table m ON e.user_id = m.user_id
  WHERE e.timestamp <= m.max_span
  GROUP BY e.user_id
),
-- 计算每个用户走完的事件门路长度
events_funnel_step_table AS
(
  SELECT e.user_id, e.event_seq, REDUCE(SEQUENCE(1, CARDINALITY(p.event_step_combos)), 
    0, 
    (s, i) -> IF(
      s > 0, 
      s, 
      IF(STRPOS(e.event_seq, p.event_step_combos[i]) = 1, 
        CARDINALITY(p.event_step_combos)-i+1, 
        0
      )
    ), 
    s -> s
  ) AS funnel_step
  FROM events_seq_table e
  CROSS JOIN params p
),
-- 计算走完不同事件门路长度的人数
events_funnel_step_count_table AS
(SELECT funnel_step, COUNT(1) AS funnel_step_count
  FROM events_funnel_step_table
  GROUP BY funnel_step
),
-- 按程序累加事件门路上每个步骤的人数
events_funnel_step_acc_count_table AS
(SELECT funnel_step, SUM(funnel_step_count) OVER (ORDER BY funnel_step ASC) AS funnel_step_acc_count
  FROM events_funnel_step_count_table
),
-- 给用户事件门路长度统计减少一个罕用的别名
funnel_count AS (SELECT * FROM events_funnel_step_count_table),
-- 把累加变成错一行累减取得步骤转化
funnel_sum AS (SELECT e.funnel_step, (funnel_total_sum-COALESCE(e2.funnel_step_acc_count, 0)) AS funnel_step_converts
  FROM events_funnel_step_acc_count_table e
  LEFT JOIN events_funnel_step_acc_count_table e2 ON e.funnel_step = e2.funnel_step+1
  CROSS JOIN (SELECT MAX(funnel_step_acc_count) AS funnel_total_sum
    FROM events_funnel_step_acc_count_table
  )
  ORDER BY funnel_step ASC
)
SELECT *
FROM funnel_sum

每个两头表都能够独自 SELECT 进去作为调试优化之用。最初的 funnel_count 和 funnel_sum 表则能够用于做漏斗剖析

更多的思考

应用 SQL 尽管达到了目标,但这个计划仍有改善空间。比方:

  • 当事件反复时,比方「A,A,B,B,C,C」,应该如何解决和判断?
  • 当漏斗反复时,比方「A,B,C,A,B,C」,应该如何解决?
  • 如果容许两头插入其余步骤,比方「A,B,X,C」,应该如何解决?
  • 如果参数输出有误,应该如何解决和提醒?

这些问题没有标准答案。有的问题兴许能够在 SQL 内解决,有的问题可能要留给后期 ETL 流程,有的则可能须要专门的利用程序逻辑来判断。读者能够依据理论的状况,隔靴搔痒解决。

此外,漏斗剖析通常须要对数据做屡次扫描,以确保数据连贯产生,并且产生在同一个工夫区间,这会间接影响扫描的数据量以及语句执行效率。读者也能够依据本身须要,对原始数据和语句自身进行批改、调整、优化,晋升整体执行效率。

最初值得一提的是,从后果能够看出,尽管咱们写进去了这样的函数,然而它的可读性并不强。诸如 IF() 函数的嵌套,REDUCE() + SEQUENCE() + CARDINALITY() 的形式来示意简略的 for 循环让人目迷五色。应用单行表再加 CROSS JOIN 的形式传递参数,用意也不是很清晰。

诚然,对于日常、固化的剖析工作,很多时候咱们能够通过 ETL 把数据转换成更不便的统计形式。这不仅更好读、更好保护,也能够晋升剖析的效率。

不过,笔者认为 SQL 作为绝对简略的语言,其疾速试错、摸索的性能依然能在业务梳理和设计期极大地晋升效率。在数据湖的时代,数据的构造变动很快,可摸索的空间也更大,熟练掌握 SQL 就更显得重要。

总结

本文介绍了如何应用 Amazon Athena 来进行漏斗剖析,并重介绍了残缺的思考过程和两头用到的 SQL 语法及函数。心愿能帮忙读者对数据湖进行更高效的摸索。

本篇作者


张玳
亚马逊云科技解决方案架构师
十余年企业软件研发、设计和征询教训,专一企业业务与亚马逊云科技服务的有机联合。译有《软件之道》《精益守业实战》《精益设计》《互联网思维的企业》,著有《体验设计白书》等书籍。

正文完
 0