Pergunta

Am procurando uma única consulta que pode converter as informações a seguir na tabela

name:time    :state
a   :10:00 AM:login
b   :10:05 AM:login
a   :10:06 AM:chatting
a   :10:08 AM:Idle
b   :10:11 AM:chatting
a   :10:10 AM:Logout
b   :10:12 AM:Logout

para algo como isto (dado o intervalo de tempo dez horas - 10:15 como o período de consulta)

name: State    :Duration
a   : chatting :2 Minutes 
a   : Idle     :2 Minutes
b   : chatting :1 Minute

Isso pode ser feito apenas usando SQL? Estou usando Informix versão 11.5

Foi útil?

Solução

Isso pode ser feito em uma única instrução SQL. Aqui está a prova.

Configuração

CREATE TEMP TABLE eventtable
(
    name CHAR(3) NOT NULL,
    time DATETIME HOUR TO MINUTE NOT NULL,
    state CHAR(8) NOT NULL
);

INSERT INTO eventtable(name, time, state) VALUES('a', '10:00', 'login');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:05', 'login');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:06', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:08', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:11', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('a', '10:10', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('b', '10:12', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:01', 'login');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:02', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:03', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:04', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:05', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:06', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:07', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:08', 'Logout');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:09', 'login');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:11', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:12', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:13', 'chatting');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:14', 'Idle');
INSERT INTO eventtable(name, time, state) VALUES('c', '10:15', 'Logout');

Corrigir consulta

Observe as condições. A tabela de resultados deve excluir os períodos entre 'login' e o primeiro outro evento; Além disso, deve excluir o período entre 'Sair' eo próximo evento (presumivelmente um 'login'). A auto-junção entre a tabela no nome coluna e, em seguida, o assimétrica juntar-se no tempo coluna (usando '<') garante que os eventos estão em ordem de tempo. A NÃO EXISTE garante sub-select que apenas os eventos adjacentes são consideradas. Usando ENTRE E na sub-consulta é um erro, porque inclui seus pontos finais e é crucial que r1.time e r2.time são excluídos da gama; ele me levou alguns minutos para detectar esse erro (RAN consulta, mas não retornou linhas, mas por ?)!

SELECT r1.name, r1.state, r2.TIME - r1.TIME AS duration
    FROM eventtable r1, eventtable r2
    WHERE r1.name = r2.name
      AND r1.time < r2.time
      AND r1.state != 'login'
      AND r1.state != 'Logout'
      AND r1.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
                      AND DATETIME(10:15) HOUR TO MINUTE
      AND r2.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
                      AND DATETIME(10:15) HOUR TO MINUTE
      AND NOT EXISTS (SELECT 1 FROM eventtable r3
                            WHERE r3.time > r1.time AND r3.time < r2.time
                      AND r3.name = r1.name
                      AND r3.name = r2.name);

Isto produz a resposta:

name state      duration
a    chatting   0:02
a    Idle       0:02
b    chatting   0:01

c    chatting   0:01
c    Idle       0:01
c    Idle       0:01
c    Idle       0:01
c    chatting   0:01
c    Idle       0:01
c    chatting   0:01
c    Idle       0:01

O valor 'duração' é um minuto de intervalo HOUR TO; se você quiser um valor em apenas alguns minutos, você tem que convertê-lo com um elenco (usando 4 para a precisão para permitir intervalos de até 1440 minutos, ou 1 dia; os dados são ambíguos para prazos mais longos):

(r2.time - r1.time)::INTERVAL MINUTE(4) TO MINUTE

Ou:

CAST (r2.time - r1.time AS INTERVAL MINUTE(4) TO MINUTE)

IBM Informix Dynamic Server (IDS) tem muito detalhado notações para constantes de tempo. No padrão SQL, você poderia usar o tempo como o tipo e tempo '10: 00: 00' como um valor, mas os segundos seriam necessários em estrita SQL padrão. IDS fornece tipos exatos que as pessoas querem - como DATETIME HOUR a minuto. Você também iria escrever minuto de intervalo (4) em SQL padrão; o 'TO MINUTO' deve ser opcional.

incorreto consulta

Na minha comentário à resposta de Ray Hidayat, eu apontou que o EXISTE sub-consulta é necessário para garantir que os eventos em consideração são contíguos - há eventos sem intermediários. Aqui está a mesma consulta com início e fim vezes adicionada à saída, ea cláusula de falta (e 'duração' renomeado para 'lapso') existe:

SELECT r1.name, r1.state, r2.TIME - r1.TIME AS lapse,
       r1.time AS start, r2.time AS end
    FROM eventtable r1, eventtable r2
    WHERE r1.name = r2.name
      AND r1.time < r2.time
      AND r1.state != 'login'
      AND r1.state != 'Logout'
      AND r1.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
                      AND DATETIME(10:15) HOUR TO MINUTE
      AND r2.time BETWEEN DATETIME(10:00) HOUR TO MINUTE
                      AND DATETIME(10:15) HOUR TO MINUTE;

Isto produz a resposta:

name state     lapse start end
a    chatting   0:04 10:06 10:10
a    chatting   0:02 10:06 10:08
a    Idle       0:02 10:08 10:10
b    chatting   0:01 10:11 10:12
c    chatting   0:13 10:02 10:15
c    chatting   0:12 10:02 10:14
c    chatting   0:11 10:02 10:13
c    chatting   0:10 10:02 10:12
c    chatting   0:09 10:02 10:11
c    chatting   0:07 10:02 10:09
c    chatting   0:06 10:02 10:08
c    chatting   0:05 10:02 10:07
c    chatting   0:04 10:02 10:06
c    chatting   0:03 10:02 10:05
c    chatting   0:02 10:02 10:04
c    chatting   0:01 10:02 10:03
c    Idle       0:12 10:03 10:15
c    Idle       0:11 10:03 10:14
c    Idle       0:10 10:03 10:13
c    Idle       0:09 10:03 10:12
c    Idle       0:08 10:03 10:11
c    Idle       0:06 10:03 10:09
c    Idle       0:05 10:03 10:08
c    Idle       0:04 10:03 10:07
c    Idle       0:03 10:03 10:06
c    Idle       0:02 10:03 10:05
c    Idle       0:01 10:03 10:04
c    Idle       0:10 10:05 10:15
c    Idle       0:09 10:05 10:14
c    Idle       0:08 10:05 10:13
c    Idle       0:07 10:05 10:12
c    Idle       0:06 10:05 10:11
c    Idle       0:04 10:05 10:09
c    Idle       0:03 10:05 10:08
c    Idle       0:02 10:05 10:07
c    Idle       0:01 10:05 10:06
c    Idle       0:08 10:07 10:15
c    Idle       0:07 10:07 10:14
c    Idle       0:06 10:07 10:13
c    Idle       0:05 10:07 10:12
c    Idle       0:04 10:07 10:11
c    Idle       0:02 10:07 10:09
c    Idle       0:01 10:07 10:08
c    chatting   0:04 10:11 10:15
c    chatting   0:03 10:11 10:14
c    chatting   0:02 10:11 10:13
c    chatting   0:01 10:11 10:12
c    Idle       0:03 10:12 10:15
c    Idle       0:02 10:12 10:14
c    Idle       0:01 10:12 10:13
c    chatting   0:02 10:13 10:15
c    chatting   0:01 10:13 10:14
c    Idle       0:01 10:14 10:15

Isso mostra como cada linha início elegíveis para o usuário 'c' é combinado com cada linha final elegíveis, dando muitas linhas espúrias de dados. A NÃO EXISTE sub-consulta é um tema comum quando se trata de consultas com base no tempo. Você pode encontrar informações sobre estas operações em Snodgrass do " Desenvolvendo aplicações orientadas a tempo no SQL "(PDF disponível online no URL), e em Data, Darwen e Lorentzos" temporal dados eo modelo relacional ".

Outras dicas

Eu tenho certeza que isso pode ser feito usando apenas SQL, ele vai me levar um pouco de tempo para chegar a uma consulta para você, eu vou editá-lo em quando eu sou feito. Os passos básicos que eu acho que seria primeiro para calcular a quantidade de tempo que cada um leva (feito tomando cada entrada e juntando-lo para a próxima entrada e subtraindo para encontrar a diferença de tempo), então um grupo simples pela cláusula com uma soma será fácil chegar -lo no formulário que você descreveu.

Edit: Aqui o que eu vim com

SELECT l.userid, l.state, SUM(t.minutes) AS duration
FROM Log l 
INNER JOIN (
    SELECT l1.id, (l2.time - l1.time) AS minutes
    FROM Log l1, Log l2
    WHERE l2.time == ( 
        -- find the next entry --
        SELECT TOP 1 ls.time
        FROM Log ls
        WHERE ls.Time > l1.Time && ls.userid = l1.userid
        ORDER BY ls.Time
    )
) t ON l.id == t.id
GROUP BY l.userid, l.state
ORDER BY l.userid

Esta é semi-pseudocódigo, fiz-se todos os nomes de tabelas e coisas, e você não vai ser capaz de simplesmente subtrair um tempo de outro, você provavelmente vai estar usando a função DATEDIFF. Além disso, porém, eu acho que é a essência dele. Acho SQL é um dos mais línguas incríveis, você pode fazer quase tudo com pouco código.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top