2

我正在尝试将事件归因于流量日志中的源。对于任何半精明的技术营销人员或网站管理员来说,这似乎是相当基本的领域,但我已经在 Google 上搜索了一下,令人惊讶的是,似乎没有人涵盖这一点。

背景:我使用的是 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每次访问(会话)可以多次访问。此外,用户在进入后可能会长时间处于空闲状态:aSubmit可能在进入后数小时内发生。

所以我认为我想要做的是选择所有CreationDateswhere Event =Submit和 Users (IP + UA checksum),然后找到最近的上一个 Event where = RefererHost is not '%mysite%',并将其存储...与那个相关联的地方Submit事件。然后我可以对Submit事件进行计数,按分组RefererHost来获得我正在寻找的内容。

这种方法对我来说有些意义,但我不知道如何编写一个“回溯”的查询来查找最近的先前引用。另外,我不确定单独的 SQL 是否可以在操作超时的情况下处理这个问题。而且我不确定我是否缺少边缘案例。有没有人做过这样的事情?

4

1 回答 1

5

如果您使用的是具有窗口函数的数据库,则可以使用相当短的查询来完成此操作。如果您想对实时数据进行修改,您还可以查看此查询的工作示例(带有一些虚拟数据):https ://modeanalytics.com/benn/reports/9f72b24dce58/query

其中的每一步都被分解为一个公用表表达式。虽然这样更容易描述,但如果您更喜欢这种风格,则可以将查询编写为一系列子查询。

第 1 步:我做了你的桌子。

WITH event_table AS (
    SELECT user_id AS dummy_ip,
           occurred_at,
           location AS dummy_referer,
           event_name
      FROM tutorial.playbook_events 
)

我的示例数据并未完全映射到您的示例,但这会创建一个大致相同的表。我映射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 标记该事件以将其标记为新会话,而是返回引用者。如果不是新会话,则返回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
)

这些窗口函数产生会话标志的运行总数(当它是新会话时为 1,当不是新会话时为 0)。结果是一列在会话未更改时保持不变,并在每次新会话开始时递增 1。根据您对该窗口函数的分区和排序方式,您可以创建对该用户唯一且全局唯一的会话 ID。

第 5 步:找到原始会话引用者。

with_session_referer AS (
    SELECT *,
           MAX(first_referer) OVER (PARTITION BY global_session_id) AS session_referer
      FROM with_session_ids
)

这个最终的窗口函数会寻找 的MAXfirst_refererfor that global_session_id。由于该列是null为该会话的第一个事件以外的每个值设置的,因此这将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,这是发生该事件的会话的第一个引用者。通过计算global_session_iddummy_ip,您还可以找到会话如何发生该事件,以及有多少不同的 IP 记录了该事件。

于 2015-07-09T01:37:55.850 回答