У меня есть журнал клиентов, выполняющих рабочий процесс. Я хочу сделать две вещи, и я борюсь с любой из них.
Во-первых: я хочу отфильтровать клиентов, которые не начали, перейдя в первое состояние в начале рабочего процесса (введите состояние 0).
Во-вторых, для оставшихся клиентов я хочу знать, сколько времени они потратили на каждый этап рабочего процесса.
В каждой записи есть:
Я попытался сделать запрос, который позволил бы мне получить временные метки входа и выхода, сгруппированные по клиенту, и указать следующее:
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;
Но сразу сталкиваюсь с несколькими проблемами. Запрос будет выполнен нормально, но:
Я попытался сосредоточиться на первой проблеме, добавив в свой выбор дополнительный атрибут следующим образом:
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, но это кажется неуклюжим, и я хочу узнать, как это сделать правильно. Также не помогает то, что я имею дело с 10 миллионами строк данных, поэтому эффективность важна.
Я использую Postgres 9.5, я не контролирую ни технологию БД, ни схему данных в этом случае.
Это было бы медленно, но я мог бы использовать для этого что-нибудь на моем Python, но мне бы очень хотелось знать, как это сделать с помощью БД.


Если я правильно понимаю, вам нужна хотя бы одна строка с 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
Примеры данных и желаемые результаты помогут В самом деле!