MySQL 显示特定值后计数

MySQL Count after an specific value shows

问题是,我需要在达到人数后计算 pages/hits 的平均人数 页面(包括 pax hit)。

数据库是:

CREATE TABLE search (
  SESSION_ID INTEGER,
  HIT_NUMBER INTEGER,
  PAGE VARCHAR(24),
  MEDIUM_T VARCHAR(24) 
);

INSERT INTO search
  (SESSION_ID, HIT_NUMBER, PAGE, MEDIUM_T)
VALUES
  ('123', '1', 'home', 'direct'),
  ('123', '2', 'flights_home', 'direct'),
  ('123', '3', 'results', 'direct'),
  ('456', '1', 'pax', 'metasearch'),
  ('789', '1', 'home', 'partners'),
  ('789', '2', 'flights_home', 'partners'),
  ('789', '3', 'results', 'partners'),
  ('789', '4', 'home', 'partners'),
  ('146', '1', 'results', 'SEM'),
  ('146', '2', 'pax', 'SEM'),
  ('146', '3', 'payment', 'SEM'),
  ('146', '4', 'confirmation', 'SEM');

而我的做法是:

SELECT s1.SESSION_ID, COUNT(*) as sCOUNT
FROM search s1
WHERE PAGE = 'pax'
GROUP BY s1.SESSION_ID

UNION ALL

SELECT 'Total AVG', AVG(a.sCOUNT)
FROM (
  SELECT COUNT(*) as sCOUNT
  FROM search s2
  GROUP BY s2.SESSION_ID
) a

显然 3r 行是错误的,我的代码遗漏了显示 'pax' 之后开始计数的部分,我对此一无所知。

先谢谢你:)

查找所有 pax 个页面及其之后的页面可以用 exists 完成。休息很简单:

SELECT AVG(hits)
FROM (
    SELECT session_id, COUNT(*) AS hits
    FROM search AS s1
    WHERE page = 'pax' OR EXISTS (
        SELECT *
        FROM search AS s2
        WHERE s2.session_id = s1.session_id
        AND   s2.hit_number < s1.hit_number
        AND   s2.page = 'pax'
    )
    GROUP BY session_id
) AS x

如果使用 MySQL 8 那么 window 函数提供了一个更简单的解决方案:

WITH cte1 AS (
    SELECT session_id, MAX(CASE WHEN page = 'pax' THEN 1 END) OVER (
        PARTITION BY session_id
        ORDER BY hit_number
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS countme
    FROM search
), cte2 as (
    SELECT COUNT(*) AS hits
    FROM cte1
    WHERE countme IS NOT NULL
    GROUP BY session_id
)
SELECT AVG(hits)
FROM cte2

我的方法使用 WITH CTE (common-table-expression) pre-declare 底层查询基础是什么,然后从中进行查询和平均。

第一个前提未明确包含在您的样本数据中。会发生什么 IF 用户在多个页面之间来回跳动并多次点击 PAX 页面。您现在有多个 pax 页面点击。我会假设你想要第一个实例到这样的 pax 页面,并且包括所有页面点击。此解决方案应该有助于解决这个问题。

让我们看看带有最终别名“pxHits”的 inner-most from 子句。

我按会话 ID 分组并获取 pax 页面命中的第一个实例(如果没有遇到此类 pax 页面,则为 null),但也获取每个会话的最高命中数。 HAVING 子句将确保仅 returns 返回具有 PAX 页面的那些会话,而将所有其他会话排除在结果之外。

这将导致两个条目传递到外部 select,其中包括 1 + lastHitNumber - firstPaxHit 计算。 1 + 的原因是因为您至少点击了该页面一次。但是,在您的会话 456 的情况下,第一个和最后一个点击是第一页,您需要它,因为 lastHitNumber - firstPaxHit 将净为零。如果一个人有 25 个页面点击并到达第 26 页的 pax 页面,这将是正确的。你的结果仍然是 1 通过 1 + 26 - 26 = 1 总页面包括 pax 页面,而不是之前的 25。

您的其他排位赛将是 146。第一个 pax hit 是 2,但他们进行了最高页面点击 4。所以 1 + 4 - 2 = 3 总页数。

现在进入决赛。由于您可以看到事情是如何准备的,我们现在可以得到平均值。您不能 mix/auto 转换不同的数据类型(session_id 与 'Total Avg' 的固定消息。它们必须是同一类型。所以我的查询是将 session_id 转换为要匹配的字符。我碰巧首先从 WITH CTE 别名中获取 AVERAGE 查询作为简单的 select,然后获取实际的 session_id 和计数。

with PaxSummary as
(
select
        pxHits.*,
        1 + lastHitNumber - firstPaxHit HitsIncludingPax
    from
        ( select
                session_id,
                min( case when page = 'pax'
                            then hit_number
                            else null end ) firstPaxHit,
                max( hit_number ) lastHitNumber
            from
                search
            group by
                session_id
            having
                min( case when page = 'pax'
                        then hit_number
                        else null end ) > 0 ) pxHits
)


select
        'Avg Pax Pages' FinalMsg,
        avg( ps2.HitsIncludingPax ) HitsIncludingPax 
    from
        PaxSummary ps2
union all
select 
        cast( ps1.session_id as varchar) FinalMsg,
        ps1.HitsIncludingPax
    from
        PaxSummary ps1

作为 EXISTS (correlated subquery) 模式的替代方案,我们可以编写一个查询,为每个 session_id 获取第一个 'pax' 命中的 hit_number,然后使用作为内联视图。

大致如下:

-- count hits on or after the first 'pax' of each session_id that has a 'pax' hit

SELECT s.session_id
     , COUNT(*) AS cnt_hits_after_pax
  FROM ( -- get the first 'pax' hit for each session_id
         -- exclude session_id that do not have a 'pax' hit
         SELECT px.session_id      AS pax_session_id
              , MIN(px.hit_number) AS pax_hit_number
           FROM search px
          WHERE px.page = 'pax'
       ) p
       -- all the hits for session_id on or after the first 'pax' hit
  JOIN search s
    ON s.session_id  = p.session_id
   AND s.hit_number >= p.hit_number
 GROUP BY s.session_id
   
  

要从该查询中获取平均值,我们可以将其包裹起来并将其转换为内联视图

SELECT AVG(c.cnt_hits_after_pax) AS avg_cnt_hits_after_pax
  FROM (
         -- query above goes here
       ) c