Como implementar uma entidade com um número máximo desconhecido de atributos?

12

Estou projetando um programa de simulação de beisebol e tive um problema ao projetar o esquema boxscore. O problema que tenho é que quero acompanhar quantas execuções são pontuadas em cada turno. A maneira como faço isso no programa atual é usar uma matriz dinâmica que cresce para cada turno jogado.

Para aqueles que não estão familiarizados com o jogo de beisebol, os jogos costumam ter nove entradas, a menos que o jogo ainda esteja empatado no final do nono turno. Portanto, os jogos de beisebol têm um comprimento indeterminado, o que significa que não posso projetar o banco de dados para ter apenas 9 colunas para as execuções pontuadas a cada turno (bem tecnicamente 18 (9-innings * 2-equipes). Uma idéia que tive foi serializar a matriz e codificá-lo como Base64 antes de armazená-lo no banco de dados.No entanto, eu não sei se isso é uma boa técnica para usar e eu queria saber se alguém tem uma idéia melhor.

Caso isso importe, o banco de dados em que estou desenvolvendo é o PostgreSQL.

Todas as sugestões são muito apreciadas! Obrigado!

Philip Lombardi
fonte

Respostas:

7

Você poderia fazer isso. Isso permitiria um bom desempenho para jogos de duração normal, além de permitir que você também armazene jogos de longa duração.

CREATE TABLE InningRuns (
    GameId INT NOT NULL REFERENCES [...],
    Team CHAR(4) NOT NULL, --'Home','Away'
    Inning1 TINYINT, --Seeing how more than 255 runs are not really possible in an inning
    Inning2 TINYINT,
    [...],
    Inning9 TINYINT,
    ExtraInnings XML | TINYINT[] | VARBINARY | ETC., --Use to hold any runs in extra innings.
    PRIMARY KEY (GameId, Team)
)

Você pode normalizar ainda mais e ter uma linha para cada combinação única de jogo, equipe e turno. Isso permitiria o número de entradas que o tipo de dados InningId permitiria.

CREATE TABLE InningRuns (
    InningRunId INT IDENTITY PRIMARY KEY,
    GameId INT NOT NULL REFERENCES [...],
    Team CHAR(4) NOT NULL, --'Home','Away'
    InningId TINYINT, --Seeing how more than 255 innings might be excessive
    Runs TINYINT,
    UNIQUE (GameId, Team, InningId)
)

Edit : Eu sei que o PostgreSQL usa Sequências em vez de IDENTITY, não lembro a sintaxe correta imediatamente, então traduza de acordo.

Eric Humphrey - lotes de ajuda
fonte
haha, eu gosto que propositadamente não li sua resposta até que escrevi a minha e estamos muito próximos da outra. Agradável.
jcolebrand
Obrigado por esta resposta, faz sentido e será como eu implemento o esquema de pontuação da caixa.
Philip Lombardi
4

Eu não acho que haja algo errado em apenas ter uma coluna

inning_score int[]

para 1 a 9 e além. Esse é um dos poucos lugares em que o uso de uma matriz pode ser razoável.

Peter Eisentraut
fonte
3

Então, o que estou vendo aqui é um pouco contraditório, porque as entradas não são realmente diretamente um atributo dos jogos, exceto indiretamente. Mas talvez seja só eu. Pessoalmente, sugeriria algo mais como uma tabela RunsScored e o vincularia a uma tabela GamesHeader, de algum tipo, então considere:

CREATE TABLE GamesHeader (
    GameID     INT IDENTITY(1,1),
    HomeTeamID INT,  --FK to teams table, naturally
    AwayTeamID INT,  --FK to teams table, naturally
    FinalInningsCount BYTE,  -- for faster reporting after the game is over
    FinalHomeScore BYTE,     -- for faster reporting after the game is over
    FinalAwayScore BYTE,     -- for faster reporting after the game is over
    --Other attribs
)

CREATE TABLE RunsScored (
    RunsScoredID BIGINT IDENTITY(1,1), -- for faster reverse traversal, possibly. May not be needed, this depends on your setup, as the normalization will show a composite key anyways
    PlayerID INT,   --FK to players table naturally
    GameID INT,     --FK to GamesHeader table naturally
    Inning BYTE, --wait for the payoff
    RunsEarned,     --because you may want to track this by the player ... really the problem is that there's not a single naturalized setup for this, so you may be intersecting this table to another stats table elsewhere. idk, it depends on your model. I'm going for fairly simplistic atm. Wanted to demonstrate something else entirely, but this needs to be accounted for.
     -- other attribs
)

SELECT MAX(r.Inning) FROM RunsScored r JOIN GamesHeader g ON g.GameID = r.GameID WHERE GameID = 'x'

Isso lhe dará o máximo de Inning jogado para um jogo em particular, e você pode refinar ainda mais por PlayerID -> TeamID para descobrir mais detalhes, se quiser. O que esses podem ser, não tenho certeza.

Eu provavelmente refino a segunda tabela para não ser RunsScored, mas algo sobre o AtBat, porque é isso que você está rastreando. Eu só queria mostrar como você poderia desnormalizar o turno da mesa de jogo. Eu ajustaria meu modelo para fluir dessa maneira, se esse fosse meu projeto. HTH. YMMV.

Observe também que sou do tipo TSQL, mas acho que os conceitos expressos abaixo funcionam muito bem para explicar meu conceito. A semântica da linguagem provavelmente não se alinhará.

jcolebrand
fonte