如何使用SQL将事件归因于流量日志中的访问源?
How to use SQL to attribute events to visit source in traffic logs?
我正在尝试将事件归因于流量日志中的来源。对于任何半精明的技术营销人员或网站管理员来说,这似乎是相当基本的领域,但我在谷歌上搜索了一下,令人惊讶的是,似乎没有人涵盖这一点。
背景: 我正在使用 T-SQL 我们 运行 在 SQL Server 2016 上。我的事件日志 ( HAProxy) 看起来像这样,其中 RefererHost
对于内部推荐是 '%mysite%'
,但对于外部推荐(入口)可以是其他任何东西。
User Agent || IP || RefererHost || Event || CreationDate
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || google.com || Home/View || 2015-05-29 00:00:25
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || mysite/x || Home/View || 2015-05-29 00:00:27
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || yahoo.com || Home/View || 2015-05-29 00:00:49
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || mysite/y || Submit || 2015-05-29 00:01:28
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || mysite/p || Photo/View || 2015-05-29 00:02:04
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || mysite/n || Submit || 2015-05-29 00:02:09
目标: 我试图将所有 Submit
事件与其入口相关联。汇总后,结果将如下所示:
RefererHost || SubmitCount ||
------------------------------
google.com || 1 ||
yahoo.com || 1 ||
复杂因素:但这是一个非常简单的例子。事实上,单个用户每个周期可以访问多次,并且每个访问(会话)他们可以 Submit
多次。此外,用户在进入后可能会长时间闲置:Submit
可能会在进入后数小时内发生。
所以我 认为 我想做的是 select 对于所有 CreationDates
事件 = Submit
和用户 ( IP + UA 校验和),然后找到最近的先前事件,其中 = RefererHost 不是 '%mysite%'
,并将其存储...与该 Submit
事件关联的某个位置。然后我可以计算 Submit
个事件,按 RefererHost
分组以获得我要查找的内容。
这种方法对我来说有些意义,但我不知道如何编写 "looks back" 的查询来查找最近的先前引用者。另外,我不确定 SQL 是否可以在操作超时的情况下单独处理这个问题。而且我不确定我是否遗漏了一个边缘案例。以前有人做过这样的事吗?
如果您正在使用具有 window 函数的数据库,您可以使用相当短的查询来完成此操作。如果您想在实时数据上修改此查询,您还可以看到此查询的工作示例(带有一些虚拟数据):https://modeanalytics.com/benn/reports/9f72b24dce58/query
其中的每一步都分解为一个通用的 table 表达式。虽然这使描述更容易,但如果您更喜欢这种风格,则可以将查询编写为一系列子查询。
第一步:我做了你的table。
WITH event_table AS (
SELECT user_id AS dummy_ip,
occurred_at,
location AS dummy_referer,
event_name
FROM tutorial.playbook_events
)
我的示例数据没有完全映射到您的示例,但这创建了一个大致相同的 table。我将 user_id
映射到 ip_address
,因为这两个字段在概念上是相同的。 location
和 referer
彼此完全没有关系,但它们都是与每个事件关联的事件属性。我的数据中有一个 location
字段,所以我使用了它。我想可以把它想象成物理推荐人之类的。
第 2 步:确定自上次事件以来的时间。
with_last_event AS (
SELECT *,
LAG(occurred_at,1) OVER (PARTITION BY dummy_ip ORDER BY occurred_at) AS last_event
FROM event_table
)
此处的 LAG 函数查找该 IP 上最后一个事件的时间。如果没有最后一个事件,则为空。
第 3 步:找出哪些事件标志着新会话的开始。
with_new_session_flag AS (
SELECT *,
CASE WHEN EXTRACT('EPOCH' FROM occurred_at) - EXTRACT('EPOCH' FROM last_event) >= (60 * 10) OR last_event IS NULL
THEN 1 ELSE 0 END AS is_new_session,
CASE WHEN EXTRACT('EPOCH' FROM occurred_at) - EXTRACT('EPOCH' FROM last_event) >= (60 * 10) OR last_event IS NULL
THEN dummy_referer ELSE NULL END AS first_referer
FROM with_last_event
)
大多数平台将新会话定义为一段时间不活动后的操作。第一个 case 语句通过查找自上一个事件以来已经过了多长时间来做到这一点。如果它比您选择的时间长(在本例中为 60 秒 * 10,即 10 分钟),则该事件将被标记为新会话中的第一个事件。它标有 1;非第一事件用 0 标记。
第二个 case 语句找到相同的事件,但它没有用 1 标记该事件以将其标记为新会话,而是 return 引用者。如果不是新会话,则 returns null
.
第 4 步:创建会话 ID。
with_session_ids AS (
SELECT *,
SUM(is_new_session) OVER (ORDER BY dummy_ip, occurred_at) AS global_session_id,
SUM(is_new_session) OVER (PARTITION BY dummy_ip ORDER BY occurred_at) AS user_session_id
FROM with_new_session_flag
)
这些 window 函数产生 运行 总会话标志(新会话时为 1 的列,不是新会话时为 0 的列)。结果是一个列在会话未更改时保持不变,并在每次新会话开始时递增 1。根据您如何划分和排序此 window 函数,您可以创建对该用户唯一且全局唯一的会话 ID。
第 5 步:查找原始会话 referer。
with_session_referer AS (
SELECT *,
MAX(first_referer) OVER (PARTITION BY global_session_id) AS session_referer
FROM with_session_ids
)
最后的 window 函数查找 first_referer
的 MAX
值 global_session_id
。由于该列对于除该会话的第一个事件以外的每个值都设为 null
,因此对于该会话中的每个事件,这将 return 该会话的 first_referer
。
第 6 步:数数。
SELECT session_referer,
COUNT(1) AS total_events,
COUNT(DISTINCT global_session_id) AS distinct_sessions,
COUNT(DISTINCT dummy_ip) AS distinct_ips
FROM with_session_referer
WHERE event_name = 'send_message'
GROUP BY 1
最后一步很简单 - 将您的事件过滤为您关心的事件(Submit
,在您的示例中)。然后通过 session_referer
计算事件数,这是发生该事件的会话的第一个 referer。通过计算 global_session_id
和 dummy_ip
,您还可以找到会话如何产生该事件,以及有多少不同的 IP 记录了该事件。
我正在尝试将事件归因于流量日志中的来源。对于任何半精明的技术营销人员或网站管理员来说,这似乎是相当基本的领域,但我在谷歌上搜索了一下,令人惊讶的是,似乎没有人涵盖这一点。
背景: 我正在使用 T-SQL 我们 运行 在 SQL Server 2016 上。我的事件日志 ( HAProxy) 看起来像这样,其中 RefererHost
对于内部推荐是 '%mysite%'
,但对于外部推荐(入口)可以是其他任何东西。
User Agent || IP || RefererHost || Event || CreationDate
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || google.com || Home/View || 2015-05-29 00:00:25
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || mysite/x || Home/View || 2015-05-29 00:00:27
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || yahoo.com || Home/View || 2015-05-29 00:00:49
------------------------------------------------------------------------------
qwertyuiop || 99.99.99.9 || mysite/y || Submit || 2015-05-29 00:01:28
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || mysite/p || Photo/View || 2015-05-29 00:02:04
------------------------------------------------------------------------------
abcdefghij || 11.11.11.1 || mysite/n || Submit || 2015-05-29 00:02:09
目标: 我试图将所有 Submit
事件与其入口相关联。汇总后,结果将如下所示:
RefererHost || SubmitCount ||
------------------------------
google.com || 1 ||
yahoo.com || 1 ||
复杂因素:但这是一个非常简单的例子。事实上,单个用户每个周期可以访问多次,并且每个访问(会话)他们可以 Submit
多次。此外,用户在进入后可能会长时间闲置:Submit
可能会在进入后数小时内发生。
所以我 认为 我想做的是 select 对于所有 CreationDates
事件 = Submit
和用户 ( IP + UA 校验和),然后找到最近的先前事件,其中 = RefererHost 不是 '%mysite%'
,并将其存储...与该 Submit
事件关联的某个位置。然后我可以计算 Submit
个事件,按 RefererHost
分组以获得我要查找的内容。
这种方法对我来说有些意义,但我不知道如何编写 "looks back" 的查询来查找最近的先前引用者。另外,我不确定 SQL 是否可以在操作超时的情况下单独处理这个问题。而且我不确定我是否遗漏了一个边缘案例。以前有人做过这样的事吗?
如果您正在使用具有 window 函数的数据库,您可以使用相当短的查询来完成此操作。如果您想在实时数据上修改此查询,您还可以看到此查询的工作示例(带有一些虚拟数据):https://modeanalytics.com/benn/reports/9f72b24dce58/query
其中的每一步都分解为一个通用的 table 表达式。虽然这使描述更容易,但如果您更喜欢这种风格,则可以将查询编写为一系列子查询。
第一步:我做了你的table。
WITH event_table AS (
SELECT user_id AS dummy_ip,
occurred_at,
location AS dummy_referer,
event_name
FROM tutorial.playbook_events
)
我的示例数据没有完全映射到您的示例,但这创建了一个大致相同的 table。我将 user_id
映射到 ip_address
,因为这两个字段在概念上是相同的。 location
和 referer
彼此完全没有关系,但它们都是与每个事件关联的事件属性。我的数据中有一个 location
字段,所以我使用了它。我想可以把它想象成物理推荐人之类的。
第 2 步:确定自上次事件以来的时间。
with_last_event AS (
SELECT *,
LAG(occurred_at,1) OVER (PARTITION BY dummy_ip ORDER BY occurred_at) AS last_event
FROM event_table
)
此处的 LAG 函数查找该 IP 上最后一个事件的时间。如果没有最后一个事件,则为空。
第 3 步:找出哪些事件标志着新会话的开始。
with_new_session_flag AS (
SELECT *,
CASE WHEN EXTRACT('EPOCH' FROM occurred_at) - EXTRACT('EPOCH' FROM last_event) >= (60 * 10) OR last_event IS NULL
THEN 1 ELSE 0 END AS is_new_session,
CASE WHEN EXTRACT('EPOCH' FROM occurred_at) - EXTRACT('EPOCH' FROM last_event) >= (60 * 10) OR last_event IS NULL
THEN dummy_referer ELSE NULL END AS first_referer
FROM with_last_event
)
大多数平台将新会话定义为一段时间不活动后的操作。第一个 case 语句通过查找自上一个事件以来已经过了多长时间来做到这一点。如果它比您选择的时间长(在本例中为 60 秒 * 10,即 10 分钟),则该事件将被标记为新会话中的第一个事件。它标有 1;非第一事件用 0 标记。
第二个 case 语句找到相同的事件,但它没有用 1 标记该事件以将其标记为新会话,而是 return 引用者。如果不是新会话,则 returns null
.
第 4 步:创建会话 ID。
with_session_ids AS (
SELECT *,
SUM(is_new_session) OVER (ORDER BY dummy_ip, occurred_at) AS global_session_id,
SUM(is_new_session) OVER (PARTITION BY dummy_ip ORDER BY occurred_at) AS user_session_id
FROM with_new_session_flag
)
这些 window 函数产生 运行 总会话标志(新会话时为 1 的列,不是新会话时为 0 的列)。结果是一个列在会话未更改时保持不变,并在每次新会话开始时递增 1。根据您如何划分和排序此 window 函数,您可以创建对该用户唯一且全局唯一的会话 ID。
第 5 步:查找原始会话 referer。
with_session_referer AS (
SELECT *,
MAX(first_referer) OVER (PARTITION BY global_session_id) AS session_referer
FROM with_session_ids
)
最后的 window 函数查找 first_referer
的 MAX
值 global_session_id
。由于该列对于除该会话的第一个事件以外的每个值都设为 null
,因此对于该会话中的每个事件,这将 return 该会话的 first_referer
。
第 6 步:数数。
SELECT session_referer,
COUNT(1) AS total_events,
COUNT(DISTINCT global_session_id) AS distinct_sessions,
COUNT(DISTINCT dummy_ip) AS distinct_ips
FROM with_session_referer
WHERE event_name = 'send_message'
GROUP BY 1
最后一步很简单 - 将您的事件过滤为您关心的事件(Submit
,在您的示例中)。然后通过 session_referer
计算事件数,这是发生该事件的会话的第一个 referer。通过计算 global_session_id
和 dummy_ip
,您还可以找到会话如何产生该事件,以及有多少不同的 IP 记录了该事件。