(SQL)如何根据使用OVER计算的值进行条件筛选

时间:2018-06-08 18:37:42

标签: sql postgresql postgresql-9.5

我有一份客户通过工作流程的日志。我想做两件事,我正在努力解决其中任何一件事。

首先是:我希望过滤掉在工作流程开始时输入第一个状态而未启动的客户(输入状态0)。

其次是:对于剩下的客户,我想知道他们在工作流程的每一步中花了多少时间。

每条记录都有:

  • CUSTOMER_ID(整数)
  • STATE(整数)
  • ACTION(进入或退出此状态,varchar)
  • UPDATE_DT(进入时间戳)

我尝试进行一个查询,这样我就可以获得按客户和状态分组的进入和退出时间戳,如下所示:

SELECT
    CUSTOMER_ID,
    STATE,
    MIN(UPDATE_DT) AS ENTRY_DATE,
    MAX(UPDATE_DT) AS EXIT_DATE
FROM LOG_DATA
GROUP BY CUSTOMER_ID, STATE
ORDER BY CUSTOMER_ID, STATE;

但我立即遇到了一些问题。查询运行得很好但是:

  • 我没有删除因未进入状态0而未启动的客户
  • 并非所有客户都保证每个州都有进入和退出日期,所以有时我的MIN / MAX无法解决问题

我试图通过在我的选择中引入一个额外的属性来专注于第一个问题:

MIN(STATE) OVER(PARTITION BY CUSTOMER_ID) AS EARLIEST_STATE

但后来遇到了一些问题。我无法将EARLIEST_STATE包含为WHERE或GROUP BY HAVING的条件,因为WHERE它不存在,GROUP BY将不允许我包含EARLIEST_STATE。

正如我认为通过它变得更糟--MIN(STATE)只能证明,客户最多只能获得STATE = 0但不是说他们有一条记录表示ACTION =“enter”且STATE = 0。所以这种方法失败不仅因为我无法让它运行,而且因为它在逻辑上也不正确。

我知道我可以用SELECT做多个SELECT,但这感觉很笨,我想学习正确的方法来做到这一点。我处理1000万行数据也没有帮助,因此效率很重要。

我正在使用Postgres 9.5,在这种情况下,我无法控制数据库技术或数据架构。

这会很慢,但我可以使用我的Python来做这件事,但我真的想知道使用数据库执行此操作的正确方法。

1 个答案:

答案 0 :(得分:0)

如果我理解正确,您希望对于结果集中的任何客户,至少有一行Action = 'Enter'state = 0。这表明了一个窗口函数:

SELECT CUSTOMER_ID, STATE,
       MIN(UPDATE_DT) AS ENTRY_DATE,
       MAX(UPDATE_DT) AS EXIT_DATE,
FROM (SELECT l.*,
             SUM(CASE WHEN ACTION = 'Enter' AND state = 0 THEN 1 ELSE 0 END) OVER (PARTITION BY CUSTOMER_ID) as num_validenter
      FROM LOG_DATA l
     ) l
WHERE num_validenter > 0
GROUP BY CUSTOMER_ID, STATE
ORDER BY CUSTOMER_ID, STATE