SQL - конечный автомат - отчетность по историческим данным на основе изменений - PullRequest
1 голос
/ 08 октября 2008

Я хочу записывать пользовательские состояния, а затем иметь возможность составлять исторические отчеты на основе записей об изменениях, которые мы сохранили. Я пытаюсь сделать это в SQL (используя PostgreSQL), и у меня есть предложенная структура для записи пользовательских изменений, как показано ниже.

CREATE TABLE users (
  userid SERIAL NOT NULL PRIMARY KEY, 
  name VARCHAR(40), 
  status CHAR NOT NULL
);

CREATE TABLE status_log (
  logid SERIAL, 
  userid INTEGER NOT NULL REFERENCES users(userid), 
  status CHAR NOT NULL, 
  logcreated TIMESTAMP
);

Это моя предложенная структура таблицы, основанная на данных.

Для поля состояния «а» представляет активного пользователя, а «s» представляет приостановленного пользователя,

INSERT INTO status_log (userid, status, logcreated) VALUES (1, 's', '2008-01-01'); 
INSERT INTO status_log (userid, status, logcreated) VALUES (1, 'a', '2008-02-01'); 

Таким образом, этот пользователь был приостановлен 1 января и снова активен 1 февраля.

Если я хочу получить приостановленный список клиентов 15 января 2008 года, тогда должен появиться идентификатор пользователя 1. Если я получу приостановленный список клиентов 15 февраля 2008 года, то идентификатор пользователя 1 не должен отображаться.

1) Это лучший способ структурировать эти данные для этого вида запроса?

2) Как запросить данные либо в этой структуре, либо в предложенной вами измененной структуре, чтобы я мог просто иметь дату (скажем, 15 января) и найти список клиентов, которые имели активный статус на эту дату в SQL только? Это работа для SQL?

Ответы [ 4 ]

2 голосов
/ 08 октября 2008

Это можно сделать, но было бы намного эффективнее, если бы вы сохранили дату окончания каждого журнала. С вашей моделью вы должны сделать что-то вроде:

select l1.userid
from status_log l1
where l1.status='s'
and l1.logcreated = (select max(l2.logcreated)
                     from status_log l2
                     where l2.userid = l1.userid
                     and   l2.logcreated <= date '2008-02-15'
                    );

С дополнительной колонкой это будет больше похоже на:

select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and logsuperseded >= date '2008-02-15';

(Извиняюсь за любые синтаксические ошибки, я не знаю Postgresql.)

Чтобы решить некоторые дополнительные вопросы, поднятые Филом:

Пользователь может быть снова перемещен из активного, приостановленного, отмененного в активное. Это упрощенная версия, в действительности, существует еще больше состояний, и людей можно перемещать непосредственно из одного состояния в другое.

Это будет выглядеть в таблице следующим образом:

userid  from       to         status
FRED    2008-01-01 2008-01-31 s
FRED    2008-02-01 2008-02-07 c
FRED    2008-02-08            a

Я использовал ноль для даты "до" текущей записи. Я мог бы использовать будущую дату, например 2999-12-31, но в некоторых случаях предпочтительнее нуль.

Кроме того, для текущего статуса также не будет «даты окончания», так что я думаю, что это немного нарушает ваш запрос?

Да, мой запрос должен быть переписан как

select userid
from status_log
where status='s'
and logcreated <= date '2008-02-15'
and (logsuperseded is null or logsuperseded >= date '2008-02-15');

Недостатком этого дизайна является то, что всякий раз, когда изменяется статус пользователя, вы должны заканчивать дату его текущего status_log, а также создавать новый. Однако это не сложно, и я думаю, что преимущество запроса, вероятно, перевешивает это.

1 голос
/ 09 октября 2008

Поддерживает ли Postgres аналитические запросы? Это дало бы активных пользователей 2008-02-15

select userid
from
(
select logid, 
       userid, 
       status, 
       logcreated,
       max(logcreated) over (partition by userid) max_logcreated_by_user
from   status_log
where  logcreated <= date '2008-02-15'
)
where  logcreated = max_logcreated_by_user
  and  status     = 'a'
/
0 голосов
/ 09 октября 2008

@ Фил

Мне нравится решение Тони. Похоже, что наиболее достойно моделировать описанную ситуацию. Любой конкретный пользователь имеет статус на определенный период времени (минута, час, день и т. Д.), Но это на время, а не на мгновение. Поскольку вы хотите знать, кто был активным в течение определенного периода времени, моделирование информации как длительности кажется наилучшим подходом.

Я не уверен, что дополнительные статусы являются проблемой. Если кто-то активен, затем приостановлен, затем отменен, а затем снова активен, каждый из этих статусов будет применим в течение определенного периода времени, не так ли? Это может быть очень короткая продолжительность, например, несколько секунд или минута, но она все равно будет продолжительной.

Вы обеспокоены тем, что статус человека может меняться несколько раз в течение определенного дня, но вы хотите знать, кто был активным в течение определенного дня? Если это так, то вам просто нужно более конкретно определить, что значит быть активным в данный день. Если достаточно, чтобы они были активны в течение какой-либо части этого дня, то ответ Тони работает хорошо, как есть. Если бы они должны были быть активными в течение определенного периода времени в данный день, то решение Тони можно изменить, чтобы просто определить продолжительность времени (в часах, минутах или днях) и добавить дополнительные ограничения в предложении WHERE. получить правильную дату, статус и продолжительность времени в этом статусе.

Что касается отсутствия «конечной даты» для текущего статуса, то это также не проблема, поскольку дата окончания была обнуляемой. Просто используйте что-то вроде этого "WHERE enddate <= '2008-08-15' или enddate is null". </p>

0 голосов
/ 08 октября 2008

@ Тони, дата окончания не обязательно применима.

Пользователь может быть снова переведен из активного, приостановленного, отмененного или активного. Это упрощенная версия, в действительности, существует еще больше состояний, и людей можно перемещать непосредственно из одного состояния в другое.

Кроме того, для текущего статуса также не будет "даты окончания", так что я думаю, что это немного нарушает ваш запрос?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...