(SQL) Как условно фильтровать на основе значения, рассчитанного с использованием OVER - PullRequest
0 голосов
/ 08 июня 2018

У меня есть журнал клиентов, проходящих рабочий процесс.Я хочу сделать две вещи, и я борюсь с любой из них.

Первое: я хочу отфильтровать клиентов, которые не начали, войдя в первое состояние в начале рабочего процесса (введите состояние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(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 для этого, но мне бы очень хотелось узнать правильный способ сделать это с помощью БД.

1 Ответ

0 голосов
/ 08 июня 2018

Если я правильно понимаю, вы хотите по крайней мере одну строку с 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
...