3

我有这种情况:用户在网站上进行搜索,其中一些人购买了。在购买条件为真且搜索会话 = 购买会话的情况下,您将如何选择 18 岁以上进行搜索但没有进行后续购买的用户

这会选择具有搜索事件且年龄超过 18 岁的用户。

select DISTINCT ON (id) id, email, users.age, events.type, events.createdon 
from users 
   LEFT JOIN events ON events.user = users.users 
where events.type='search' 
  and age>18 
  and events.condition is true

我将如何添加他们之前在同一会话中执行搜索事件的条件?

事件的表结构:

-----------------------------------------------------
| user |   event   |    date    | condition | session |
------------------------------------------------------|
|  1   |  search   | 08-10-2013 |   true    |    A    |
|  1   |  search   | 08-10-2013 |   true    |    A    |
|  2   |  search   | 08-10-2013 |   false   |    B    |
|  2   | purchase  | 09-10-2013 |   false   |    A    |
|  2   |  search   | 09-10-2013 |   true    |    C    |
|  1   | purchase  | 09-10-2013 |   true    |    A    |
|  3   |  search   | 09-10-2013 |   false   |    D    |
|  2   |  search   | 10-10-2013 |   true    |    H    |
|  4   |  search   | 10-10-2013 |   false   |    E    |
|  4   |  search   | 10-10-2013 |   false   |    E    |
|  3   |  search   | 11-10-2013 |   true    |    D    |
|  2   |  other    | 11-10-2013 |   true    |    H    |
|  1   |  search   | 11-10-2013 |   true    |    F    |
|  1   | purchase  | 12-10-2013 |   true    |    F    |
|  3   | purchase  | 12-10-2013 |   false   |    D    |
|  4   |  search   | 12-10-2013 |   true    |    G    |    
|  2   |  other    | 12-10-2013 |   true    |    A    |   
-----------------------------------------------------

用户表是:

------------------------
| user | email  | age  |
------------------------
|  1   |   a    |  22  |
|  2   |   b    |  34  |
|  3   |   c    |  15  |
|  4   |   d    |  44  |
|  5   |   e    |  39  |
------------------------

结果应该是 2 和 4:

1    a   -> NO because did purchase with condition=true
2    b   -> YES because did search, did purchase BUT condition=false
3    c   -> NO because did search, did purchase with condition=false BUT age<18
4    d   -> YES because did search and no purchase 

谢谢,我刚从 Mongo 开始使用 Postgres,而且好多了!

更新:例如更正的结果

4

2 回答 2

2

如果您只需要users表中的数据:

select
    u.*
from users as u
where
    u.age > 18 and
    exists (
        select *
        from events as e1
        where 
            e1.user = u.user and e1.condition is true and
            e1.event = 'search' and
            not exists (
                select *
                from events as e2
                where
                    e2.user = u.user and e2.condition is true and
                    e2.event = 'purchase' and
                    e2.session = e1.session and e2.date > e1.date
            )
    )

如果也可以这样做:

select
    u.*
from users as u
where
    u.age > 18 and
    exists (
        select *           
        from (
            select
                max(case when e.event = 'search' then e.date end) as search_m_date,
                max(case when e.event = 'purchase' then e.date end) as purchase_m_date
            from events as e
            where e.user = u.user and e.condition is true
            group by e.session
        ) as a
        where
            a.search_m_date is not null and
            (a.purchase_m_date is null or a.search_m_date > a.purchase_m_date)
    )

但实际上我更喜欢第一个

sql fiddle demo

于 2013-09-17T09:08:22.697 回答
1

这是逐步构建完整查询的一种方法:

  1. 获取所有搜索事件:

    SELECT *
    FROM events AS s
    WHERE event = 'search'
      AND condition IS TRUE
    
  2. 过滤掉成功购买事件之后的那些:

    SELECT *
    FROM
      events AS s
    LEFT JOIN
      events AS p
      ON  s.user = p.user
      AND s.session = p.session
      AND p.event = 'purchase'
      AND p.condition IS TRUE
      AND p.date > s.date  -- this assumes that `date` stores both date and time
    WHERE s.event = 'search'
      AND s.condition IS TRUE
      AND p.event IS NULL
    
  3. users从上一个结果集中返回ID且年龄在18岁以上的表中获取用户:

    SELECT *
    FROM users
    WHERE age > 18
      AND user IN (
        SELECT
          s.user
        FROM
          events AS s
        LEFT JOIN
          events AS p
          ON  s.user = p.user
          AND s.session = p.session
          AND p.event = 'purchase'
          AND p.condition IS TRUE
          AND p.date > s.date
        WHERE s.event = 'search'
          AND s.condition IS TRUE
          AND p.event IS NULL
      )
    ;
    

这应该会给你想要的结果,尽管不一定是以最有效的方式。您可以使用反连接,在此查询中,它是使用该LEFT JOIN + WHERE IS NULL技术实现的,用它重写它NOT EXISTS以查看其性能是否更好。IN此外,您可以尝试用等效的谓词重写谓词EXISTS。(本质上,@Roman Pekar 的第一个解决方案可以看作是建议的两次重写的结果。)

于 2013-09-17T11:01:42.483 回答