Atenção
funciona melhor em sql 2008+



Há tempos vejo pessoas pedindo query pra contar tempo online de char, então resolvi por a mão na massa e fazer uma.


Se houver bugs eu fixo, só avisar aqui, mas aparentemente está 100% (fiz inúmeros testes).[B]


A contagem é feita em segundos e é armazenada:


- Tempo da conta: na MEMB_INFO, coluna TimeON.
- Tempo de cada char: na Character, coluna TimeON.




O tempo online é atualizado:


- Quando você seleciona char (mesmo se não trocar de char).
- Quando você troca de char.
- Quando você desloga a conta.


Vamos lá.




[B]♦ Versões atuais dos Scripts:


- TRIGGER: 2.1.2
- WZ_DISCONNECT_MEMB: 2.0.0




[B]♦ ALTER TABLES
Código PHP:
USE [MUOnline]GO
ALTER TABLE 
[dbo].[MEMB_STAT]ALTER COLUMN [ConnectTM] [datetimeNULLGO
ALTER TABLE 
[dbo].[MEMB_STAT]ALTER COLUMN [DisConnectTM] [datetimeNULLGO
ALTER TABLE 
[dbo].[MEMB_INFO]ADD    [TimeON] [bigintNOT NULL DEFAULT 0GO
ALTER TABLE 
[dbo].[Character]ADD    [ConnectTM] [datetimeNULLGO
ALTER TABLE 
[dbo].[Character]ADD    [DisConnectTM] [datetimeNULLGO
ALTER TABLE  
[dbo].[Character]ADD    [TimeON] [bigint] DEFAULT ((0)) NOT NULLGO 

♦ TRIGGER - AccountCharacter_Online


Código PHP:
/*| @author - Renato Valer| @version - 2.1.2| @last update - 2016/02/12 - 08h18min| @warning: Não me responsabilizo por uso incorreto e possíveis deadlocks. Use por sua conta e risco.*/
USE MuOnlineGO
IF EXISTS (SELECT name FROM sysobjects WHERE name 'AccountCharacter_Online' AND type 'TR')    DROP TRIGGER [AccountCharacter_Online]GO
CREATE TRIGGER 
[AccountCharacter_OnlineON [dbo].[AccountCharacterAFTER UPDATE AS    SET NOCOUNT ON
/*|    Hipóteses|    ||        1 - Se GameIDC foi atualizado, algum char foi foi logado. Surgem hipóteses:||            1.1 - GameIDC e Old_GameIDC são diferentes:|    |                1.1.1 - Old_GameIDC é NULL, logo é o primeiro char logado (e possivelmente o primeiro criado) na conta, |                    pois não existe GameIDC anterior.|                1.1.2 - Já existe um GameIDC anterior, logo significa que o cara acessou outro char. Surgem 2 hipóteses:||                    1.1.2.1 - O cara simplesmente trocou de char sem deslogar a conta:|                        1.1.2.1.1 - O cara logou um char, deletou ele e entrou em outro.|                    1.1.2.2 - O cara relogou a conta e entrou em outro char.|                        1.1.2.2.1 - O cara logou um char, deletou ele, saiu da conta, voltou e entrou em outro.|                        |            1.2 GameIDC e Old_GameIDC são iguais:|                        |                1.2.1 - O cara relogou o char.|                1.2.2 - O cara relogou a conta e entrou no mesmo char.||        2. Se GameIDC não foi atualizado, não precisa fazer nada, porque significa:|        |            2.1 - Que um char foi criado, mas não foi logado.|            2.2 - Que um char foi deletado sem nem mesmo ter sido logado.|                |        */
-- Hipótese 1IF UPDATE(GameIDCBEGIN
    
DECLARE @Login VARCHAR(10),    @GameIDC VARCHAR(10),    @Old_GameIDC VARCHAR(10),    @GameIDC_ConnectTM DATETIME,    @GameIDC_ConnectTM_Int INT,    @Old_GameIDC_ConnectTM DATETIME,    @Old_GameIDC_ConnectTM_Int INT,    @GameIDC_DisConnectTM DATETIME,    @GameIDC_DisConnectTM_Int INT,    @Old_GameIDC_DisConnectTM DATETIME,    @Old_GameIDC_DisConnectTM_Int INT,    @account_DisconnectTM DATETIME,    @account_DisconnectTM_Int INT,    @Now DATETIME,    @Now_Int INT,    @TimeON BIGINT;
    
SET @Login = (SELECT Id FROM INSERTED);    SET @GameIDC = (SELECT GameIDC FROM AccountCharacter WHERE Id = @Login);    SET @Old_GameIDC = (SELECT GameIDC FROM DELETED);    SET @Now GETDATE();    SET @Now_Int DATEDIFF(s'19700101', @Now);    SET @account_DisconnectTM = (SELECT DisconnectTM FROM MEMB_STAT WHERE memb___id = @Login);    SET @account_DisconnectTM_Int DATEDIFF(s'19700101', @account_DisconnectTM);
    -- 
Hipótese 1.1    IF(@GameIDC <> @Old_GameIDCBEGIN
        
-- Hipótese 1.1.1        IF(@Old_GameIDC IS NULLBEGIN                        UPDATE Character SET ConnectTM = @Now WHERE AccountID = @Login AND Name = @GameIDC;                    END        -- Hipótese 1.1.2        ELSE BEGIN                    SET @Old_GameIDC_DisconnectTM = @Now            SET @Old_GameIDC_DisconnectTM_Int = @Now_Int;                        -- Hipótese 1.1.2.1            IF @account_DisconnectTM_Int <> @Old_GameIDC_DisconnectTM_IntBEGIN                            -- Verificação da Hipótese 1.1.2.1.1                IF EXISTS(SELECT Name FROM Character WHERE AccountID = @Login AND Name = @Old_GameIDCBEGIN
                    SET 
@Old_GameIDC_ConnectTM = (SELECT ConnectTM FROM Character WHERE AccountID = @Login AND Name = @Old_GameIDC);                    SET @Old_GameIDC_ConnectTM_Int DATEDIFF(s'19700101', @Old_GameIDC_ConnectTM);                                SET @TimeON = @Old_GameIDC_DisconnectTM_Int - @Old_GameIDC_ConnectTM_Int;                                    UPDATE Character SET TimeON TimeON + @TimeONDisConnectTM = @Now WHERE AccountID = @Login AND Name = @Old_GameIDC;                                                    END                            END            -- Hipótese 1.1.2.2 e "fim" da Hipótese 1.1.2.1            -- A query é a mesma e um "else" é desnecessário.                    /*            |    Não é necessário atualizar tempo on, porque se o cara relogou a conta            |    a WZ_DISCONNECT_MEMB já fez o serviço. Só precisamos atualizar o momento            |    de connect do novo char.            */            UPDATE Character SET ConnectTM = @Now WHERE AccountID = @Login AND Name = @GameIDC;                                    END            END    --Hipótese 1.2.    ELSE BEGIN        -- Apenas precaução...        IF(@GameIDC IS NOT NULLBEGIN                        /*            |    Desnecessário checar se o char existe, porque se essa parte             |    do script está sendo executada, é porque o char foi logado            |    agora, logo é presumível que existe.            */                        SET @GameIDC_ConnectTM = (SELECT ConnectTM FROM Character WHERE AccountID = @Login AND Name = @GameIDC);            SET @GameIDC_ConnectTM_Int DATEDIFF(s'19700101', @GameIDC_ConnectTM);            SET @GameIDC_DisconnectTM_Int = @Now_Int;
            -- 
Hipótese 1.2.1            IF @account_DisconnectTM_Int < @GameIDC_ConnectTM_IntBEGIN                                SET @TimeON = @GameIDC_DisconnectTM_Int - @GameIDC_ConnectTM_Int;                UPDATE Character SET TimeON TimeON + @TimeONDisConnectTM = @NowConnectTM = @Now WHERE AccountID = @Login AND Name = @GameIDC;                            END            -- Hipótese 1.2.2            ELSE BEGIN                /*                    Não é necessário atualizar tempo on, porque se o cara relogou a conta                    a WZ_DISCONNECT_MEMB já fez o serviço.                */                UPDATE Character SET ConnectTM = @Now WHERE AccountID = @Login AND Name = @GameIDC;                            END                        END            END    SET NOCOUNT OFFEND 

♦ PROCEDURE WZ_DISCONNECT_MEMB


Código PHP:
/*| @modifications - Renato Valer| @version - 2.0.0| @last update - 2015/08/28 - 09h25min| @warning: Não me responsabilizo por uso incorreto e possíveis deadlocks. Use por sua conta e risco.*/
USE MuOnlineGO
IF EXISTS (SELECT FROM sys.objects WHERE type 'P' AND name 'WZ_DISCONNECT_MEMB')DROP PROCEDURE [DBO].[WZ_DISCONNECT_MEMB]GO
CREATE PROCEDURE 
[DBO].[WZ_DISCONNECT_MEMB] @memb___id VARCHAR(10) AS BEGIN
SET NOCOUNT ON
DECLARE @Find_ID VARCHAR(10),@ConnectStat TINYINT,@LoginTime INT,@LogoutTime INT,@ConnectTM INT,@DisConnectTM INT,@TimeON_Account BIGINT,@TimeON_Char BIGINT,@GameIDC VARCHAR(10),@CharConnectTM DATETIME,@CharConnectTM_Int INT,@Now DATETIME,@Now_Int INT;
SET @ConnectStat 0SET @Find_ID 'NOT'SET @Now GETDATE();
SELECT @Find_ID S.memb___id FROM MEMB_STAT S INNER JOIN MEMB_INFO I ON S.memb___id COLLATE DATABASE_DEFAULT I.memb___id WHERE I.memb___id = @memb___id;
IF( @
Find_ID <> 'NOT' BEGIN    
UPDATE MEMB_STAT SET ConnectStat 
= @ConnectStatDisconnectTM = @Now WHERE memb___id = @memb___id;

/*Selecionamos os momentos de login e logout da conta e convertemos para números inteiros.*/
SET @ConnectTM = (SELECT DATEDIFF(s'19700101'MEMB_STAT.ConnectTMFROM MEMB_STAT WHERE memb___id = @memb___id);SET @DisConnectTM DATEDIFF(s'19700101', @Now);

/*Executamos os cálculos para obtermos o tempo total online da conta.*/
SET @TimeON_Account = @DisConnectTM - @ConnectTM;
/*Atualizamos o tempo total online da conta.*/
UPDATE MEMB_INFO SET TimeON TimeON + @TimeON_Account WHERE memb___id = @memb___id;

/*Selecionando nick do último char logado*/    
SET @GameIDC = (SELECT GameIDC FROM AccountCharacter WHERE Id = @memb___id);
/*Algum char foi logado antes de sair da conta.Mesmo que o cara tenha logado na conta e criado o char, o GameIDCsó vai ser preenchido se o cara logar na conta.Sendo assim, se GameIDC for NULL, indica que nenhum charnunca foi logado nessa conta, então não tem necessidade de contar tempo on.*/    IF(@GameIDC IS NOT NULLBEGIN
/*Verificamos se esse char existe.Motivo: o cara pode ter clicado em "selecionar char", deletado o char e depois deslogado da conta.Se não existe, não precisa fazer nada.*/IF EXISTS (SELECT Name FROM Character WHERE AccountID = @memb___id AND Name = @GameIDCBEGIN
/*Verificação: quando foi o último connect desse char que acabou de deslogar?Se for nulo, significa que ocorreu algum problema na trigger, entãoadicionamos o valor de "agora" convertido em timestamp para possibilitar o cálculo.*/
SET @CharConnectTM = (SELECT ConnectTM FROM Character WHERE AccountID = @memb___id AND Name = @GameIDC);IF (@CharConnectTM IS NULLBEGINSET @CharConnectTM_Int DATEDIFF(s'19700101', @Now);END/*Se não for nulo, convertemos para timestamp.*/ELSE BEGINSET @CharConnectTM_Int DATEDIFF(s'19700101', @CharConnectTM);END
/*Executamos os cálculos para obtermos o tempo total onlinedo último char logado.*/
SET @TimeON_Char = (@DisConnectTM - @CharConnectTM_Int);
/*Atualizamos o tempo total online do último char logado.*/
UPDATE Character SET TimeON TimeON + @TimeON_CharDisConnectTM = @Now WHERE AccountID = @memb___id AND Name = @GameIDC;    
ENDENDENDSET NOCOUNT OFFEND 



Atenção

ATENÇÃO




Não me responsabilizo por uso incorreto.
Não me responsabilizo por eventuais deadlocks. Usem por conta e risco.


Agradecimentos a @navossoc, @Willerson, @Erick-Master e @viOleNt pela colaboração.


[]'s



Creditos Renato ( Imperyus )