【问题标题】:Counting events before a specific event在特定事件之前计数事件
【发布时间】:2020-04-16 23:18:46
【问题描述】:

假设我有一个包含以下列的表格:

date | event     | user_id |  unit_id  |cost |  ad_id  | spend
03-15 | impression | 2353   |   3436 | 0.15 |  NULL | NULL
03-15 | impression | 2353   |   3436 | 0.12 |  NULL | NULL
03-15 | impression | 1234   |   5678 | 0.10 |  NULL | NULL
03-15 | click | 1234   |   5678 |  NULL  |  NULL | NULL
03-15 | create_ad |     1234 | 5678 | NULL | 6789 | 10

我想计算在用户创建 ID 之前平均需要多少次展示。 在此特定场景中,用户 1234 需要一次印象才能创建广告。

我不确定我能否以某种方式使用 date 来区分事件(但从逻辑上讲,所有这些事件都应该在不同的时刻发生)。但是,您可以看到,impressions 在 ad_idspend 中有 NULL,而 create_id 在 spend 中有一个数字。

这个不行:

select i.user_id
     , i.unit_id
     , count(i.event) impressions_n
     , count(c.event) as ads_n
  from add4ad i
   left 
  join add4ad c
     on i.user_id = c.user_id 
   and i.unit_id = c.unit_id
 where i.event in ('impression')
   and c.spend <> NULL
 group 
    by i.user_id
     , i.unit_id 

我用这些数据创建了一个SQLFiddle

【问题讨论】:

  • c.spend &lt;&gt; NULL 更改为 c.spend is not NULL 并检查您是否得到正确的结果。
  • 它运行但结果不正确(即它不返回任何结果)。
  • 那么你必须更好地解释你需要什么并提供预期的结果。另外:...在用户创建 id 之前平均需要多少次展示... 只有在有按日期定义的订单时才有意义,而这里的情况并非如此,因为所有日期都相等。
  • 你的小提琴数据不同,真的没有返回数据。您的问题数据应返回一行。
  • @forpas,这很公平,我在小提琴中添加了更多数据。现在它有更多天数的数据,因此可以定义一些顺序。

标签: mysql sql join left-join self-join


【解决方案1】:

问题是为了检查你必须使用的 NULLS 是 NULL 还是不是 NULL。您在小提琴中的数据也不正确。它对小提琴中的 1234 没有印象。

select i.user_id, i.unit_id, count(i.event) as impressions_n,
count(c.event) as ads_n
from add4ad i
 left join add4ad c
  on i.user_id = c.user_id and i.unit_id = c.unit_id
where i.event in ('impression')
/*and c.event in ('create_ad')*/ and c.spend is not NULL
group by i.user_id, i.unit_id 

【讨论】:

  • 是的,我都更正了:添加了 1234 并使用了“不是 NULL”。但是,它不会改变结果。
  • 其打印 1234 与 1。您对用户 1234 只有 1 次展示。
  • 您的小提琴中的事件ID也不正确。这是正确数据的更新小提琴:sqlfiddle.com/#!9/70a44d/2
【解决方案2】:

我去了 SQL Fiddle 并通过 MS SQL 引擎运行了测试。

CREATE TABLE add4ad (date date, event varchar(10), user_id int,
                   unit_id int, cost float, ad_id float, spend float);
INSERT INTO add4ad (date, Event, user_id,unit_id,cost,ad_id,spend)
VALUES
    ('2018-03-15','impression','2353','3436','0.15',NULL,NULL),
    ('2018-03-15','impression','2353','3436','0.12',NULL,NULL),
    ('2018-03-15','impression','2353','3436','0.10',NULL,NULL),
    ('2018-03-15','click','1234','5678', NULL, NULL,NULL),
    ('2018-03-15','create_ad','2353','5678', NULL, 6789,10);

我的查询

with e10 as (select  user_id, event, date, rowid=row_number() over (Partition by user_id order by date)
from add4ad
where event='create_ad'
),
e20 as ( -- get the first create_ad event
select user_id, date
  from e10
  where rowid=1
  )
  select a.user_id, count(1) as N
  from e20 inner join add4ad a
  on e20.user_id=a.user_id
  and a.date<=e20.date
  and a.event='impression'
  group by a.user_id

【讨论】:

  • 看起来运行良好并解决了问题!你介意回顾一下你的思考过程吗(鉴于 MS SQL 语法比其他变体更具异国情调)。
  • 这是关于您原始帖子中的假设。我的查询假设每个用户可能有多个“create_ad”事件,并且在“create_ad”之后可能有“印象”事件。您的最新帖子基本上否认了两者的可能性。
  • 此查询基于宽松的假设 - 每个用户只有一个“create_ad”事件,并且在“create_ad”之后可能有“印象”事件。 ` with e20 as (select user_id, date from add4ad where event='create_ad') select a.user_id, count(1) as N from e20 inner join add4ad a on e20.user_id=a.user_id and a.date
  • 我相信,一旦创建了给定的 ad_id,就没有理由再产生与同一 unit_id 相关联的印象(只是试图评估问题的业务逻辑)。
【解决方案3】:

如果我猜对了,您需要计算不同的广告

CREATE TABLE add4ad (`date` date, `event` varchar(10), `user_id` int,
                   `unit_id` int, `cost` float, `ad_id` float, `spend` float);
INSERT INTO add4ad (`date`, `Event`, `user_id`,`unit_id`,`cost`,`ad_id`,`spend`)
VALUES
    ('2018-03-15','impression','2353','3436','0.15',NULL,NULL),
    ('2018-03-15','impression','2353','3436','0.12',NULL,NULL),
    ('2018-03-15','impression','2353','3436','0.10',NULL,NULL),
    ('2018-03-15','impression','1234','5678','0.10',NULL,NULL),
    ('2018-03-15','click','1234','5678', NULL, NULL,NULL),
    ('2018-03-15','create_ad','1234','5678', NULL, 6789,10),
    ('2018-03-16','impression','8765','8871','0.10',NULL,NULL),
    ('2018-03-16','impression','8765','8871','0.10',NULL,NULL),
    ('2018-03-16','impression','8765','8871','0.2',NULL,NULL),
    ('2018-03-16','impression','8765','8871','0.23',NULL,NULL),
    ('2018-03-16','click','8765','8871', NULL, NULL,NULL),
    ('2018-03-16','create_ad','8765','8871', NULL, 6789,10);

select i.user_id, i.unit_id, count(i.event) as impressions_n,
    count(distinct c.event) as ads_n
from add4ad i
join add4ad c
   on i.user_id = c.user_id and i.unit_id = c.unit_id
where i.event in ('impression')
   and c.event in ('create_ad') and c.spend is not NULL
group by i.user_id, i.unit_id 

返回

user_id unit_id impressions_n   ads_n
1234    5678    1   1
8765    8871    4   1

我已将left join 替换为join,因为where 可以有效地使您的连接inner 如果您仍然需要左连接,请将谓词移动到ON 子句或在其中处理NULL。

fiddle

【讨论】:

  • 效果很好,但只适用于一组。在上面的小提琴中,我添加了更多数据(另一天)。而且您的代码仅捕获一组。但这个想法似乎是正确的。
  • 查看编辑版本。查询返回 2 行。预期的结果是什么?
  • 编辑后的版本按预期工作。谢谢你的支持!
【解决方案4】:

似乎这是解决方案:

select sum(c.impressions_n) / count(1) as average_num_of_impressions from (
select count(i.event) as impressions_n 
  from add4ad i 
  join add4ad c
     on i.user_id = c.user_id and i.unit_id = c.unit_id
 where i.event in ('impression') and c.event in ('create_ad')
 group by i.user_id, i.unit_id              ) c

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-21
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 2012-10-21
    • 2013-08-15
    • 1970-01-01
    相关资源
    最近更新 更多