Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Bancos de dados:Fazendo um Log de ações, como lidar com várias referências?


O seguinte é como eu faria isso. Eu tenho mais alguns comentários na parte inferior depois de você ter visto o esquema.

Registro

LogID - ID de log exclusivo

Hora - data/hora do evento

LogType - String ou ID

(comentário lateral, eu usaria um id aqui para que você possa usar uma tabela de mensagens mostrada abaixo, mas se você quiser quick n dirty você pode apenas uma string exclusiva para cada tempo de log (por exemplo, "Game Started", "Message Sent" , etc)

LogActor

LogID - chave externa

LogActorType - String ou ID (como acima, se for ID, você precisará de uma tabela de pesquisa)

LogActorID - Este é um id exclusivo para a tabela para o tipo, por exemplo, Usuário, Grupo, Jogo

Sequência - esta é uma ordenação dos atores.

LogMessage

LogType - chave externa

Mensagem - string longa (varchar(max)?)

Idioma - string(5) para que você possa digitar um idioma diferente, por exemplo, "US-en"

Dados de exemplo (usando seus 3 exemplos)

Registro
ID  Time   LogType 
1   1/1/10 1
2   1/1/10 2
3   1/1/10 3

LogActor
LogID LogActorType LogActorID Sequence
1     User         1          1
1     User         2          2
2     User         1          1
2     User         2          2
2     User         2          3
2     Game         1          4
3     User         3          1
3     Group        1          2

LogMessage
LogType Message 
1       {0} Made a new friend {1}
2       {0}, {1}, {2} played a game ({3})
3       {0} joined a group ({1})

Do utilizador
ID Name
1  User A
2  User B
3  User C

Jogo
ID Name
1  Name of game

Grupo
ID Name
1  Name of group

Então, aqui estão as coisas boas sobre este design.

  • É muito fácil estender

  • Ele lida com questões multilíngue independente dos atores

  • É autodocumentado, a tabela LogMessage explica exatamente o que os dados que você está armazenando devem dizer.

Algumas coisas ruins sobre isso.

  • Você tem que fazer algum processamento complicado para ler as mensagens.

  • Você não pode simplesmente olhar para o banco de dados e ver o que aconteceu.

Na minha experiência, as partes boas desse tipo de design superam as partes ruins. O que eu fiz para me permitir dar uma olhada rápida e suja no log é fazer uma visualização (que eu não uso para o código do aplicativo) que eu possa ver quando precisar ver o que está acontecendo na parte de trás fim.

Deixe-me saber se você tem perguntas.

Atualização - Algumas consultas de exemplo

Todos os meus exemplos estão no sqlserver 2005+, deixe-me saber se há uma versão diferente que você deseja que eu direcione.

Veja a tabela LogActor (Existem várias maneiras de fazer isso, a melhor depende de muitas coisas, incluindo distribuição de dados, casos de uso, etc.) Aqui estão duas:

a)
SELECT 
  LogId,
  COLLESCE(U.Name,Ga.Name,Go.Name) AS Name,
  Sequence
FROM LogActor A
LEFT JOIN User U ON A.LogActorID = U.[ID] AND LogActorType = "User"
LEFT JOIN Game Ga ON A.LogActorID = Ga.[ID] AND LogActorType = "Game"
LEFT JOIN Group Go ON A.LogActorID = Go.[ID] AND LogActorType = "Group"
ORDER BY LogID, Sequence

b)
SELECT 
  LogId,
  U.Name AS Name,
  Sequence
FROM LogActor A
INNER JOIN User U ON A.LogActorID = U.[ID] AND LogActorType = "User"
UNION ALL
SELECT 
  LogId,
  Ga.Name AS Name,
  Sequence
FROM LogActor A
INNER JOIN Game Ga ON A.LogActorID = Ga.[ID] AND LogActorType = "Game"
UNION ALL
SELECT 
  LogId,
  Go.Name AS Name,
  Sequence
FROM LogActor A
INNER JOIN Group Go ON A.LogActorID = Go.[ID] AND LogActorType = "Group"
ORDER BY LogID, Sequence

Em geral, acho que a) é melhor que b) Por exemplo, se estiver faltando um ator, digite a) o incluirá (com um nome nulo). No entanto, b) é mais fácil de manter (porque as instruções UNION ALL o tornam mais modular). Existem outras maneiras de fazer isso (por exemplo, CTE, visualizações, etc). Estou inclinado a fazê-lo como b) e pelo que vi isso parece ser pelo menos uma prática padrão, se não a melhor prática.

Então, os últimos 10 itens no log ficariam assim:
SELECT 
  LogId,
  M.Message,
  COLLESCE(U.Name,Ga.Name,Go.Name) AS Name,
  Time,
  A.Sequence
FROM Log
LEFT JOIN LogActor A ON Log.LogID = A.LogID
LEFT JOIN User U ON A.LogActorID = U.[ID] AND LogActorType = "User"
LEFT JOIN Game Ga ON A.LogActorID = Ga.[ID] AND LogActorType = "Game"
LEFT JOIN Group Go ON A.LogActorID = Go.[ID] AND LogActorType = "Group"
LEFT JOIN LogMessage M ON Log.LogType = M.LogMessage
WHERE LogID IN (SELECT Top 10 LogID FROM Log ORDER BY Date DESC)
ORDER BY Date, LogID, A.Sequence

NB - Como você pode ver, é mais fácil selecionar todos os itens de log de uma data do que o último X, porque precisamos de uma subconsulta (provavelmente muito rápida) para isso.