MMOLDB

This page lists the 3 schemata of MMOLDB, each table within them, and each column of each table.

The formatting is hopefully serviceable, but not ideal. We are eagerly accepting any help to improve the looks and legibility over at the MMOLDB github and discord channel.

taxa

Contains categorical information. You can think of each taxa table as an enum, and each row as a variant of that enum. The difference between taxa and an actual enum is that taxa tables have extra columns with properties about the variants.

The correct way to use any column that references a taxa is to join on said taxa table and use the taxa's name in your where and group by clauses. However, I recognize that I won't be able to stop people from using IDs instead, so those are guaranteed to be stable.

taxa.event_type

The outcome of the pitch (or lack of pitch, in the case of a balk). The types are very granular to facilitate grouping them in as many ways as possible. Some common ways to group events are provided by the many boolean is_* columns.

Name Type Description
id bigint

An arbitrary ID for an event type. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using event type ids in your queries or results.

name text

The code-friendly name for this event type. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this event type.

ends_plate_appearance boolean

Does an event of this type end the current plate appearance?

is_in_play boolean

Does an event of this type result in a ball in play?

is_hit boolean

Does an event of this type count as a hit, for the purposes of batter statistics?

is_error boolean

Does an event of this type count as an error, for the purposes of both batter and pitcher statistics?

is_ball boolean

Does an event of this type represent a pitch outside the strike zone on which the batter did not swing?

is_strike boolean

Does an event of this type represent either a pitch inside the strike zone or a pitch on which the batter swung?

is_strikeout boolean

Does an event of this type count as a strikeout, for the purposes of pitcher statistics?

is_basic_strike boolean

Does an event of this type count as either a strikeout or a non-PA-ending strike?

is_foul boolean

Does an event of this type count as a foul ball or foul tip?

is_foul_tip boolean

Does an event of this type count as a foul tip?

batter_swung boolean

Does an event of this type represent a pitch on which the batter swung?

taxa.fielder_location

A location on the field, identified by its associated fielder position.

Name Type Description
id bigint

The standard baseball fielder number. 1 = pitcher, 2 = catcher, etc.

name text

The code-friendly name for this fielder location. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this fielder location.

abbreviation text

The standard one- or two-letter abbreviation for this fielder location.

area text

'Infield' or 'Outfield'

taxa.fair_ball_type

The characterization of a batted ball's trajectory.

Name Type Description
id bigint

An arbitrary ID for a fair ball type. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using fair ball type ids in your queries or results.

name text

The code-friendly name for this fair ball type. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this fair ball type.

taxa.slot

A possibly-approximate player slot in the roster.

MMOLB has been inconsistent in the past with how it identifies pitchers who field a ball. At different times (and in different events) they have been identified just as the pitcher ("P"), as the type of pitcher (e.g. "SP"), and with their full roster slot identification (e.g. "SP3"). We do the best we can to provide the most specific identification available.

Note that because of this MMOLB inconsistency, a position player who is currently pitching may be identified as "Pitcher" or as their assigned roster slot.

Eventually we plan to reference other sources to accurately determine the player's roster slot, at which time the approximate values will be removed from this taxa.

Approximate slots will also be used in case there is a pitcher with a higher slot than expected, e.g. "SP6" or "RP4". There are no instances of this as of this writing (at the end of Season 2).

Name Type Description
id bigint

The slot's ordinal position on the MMOLB team page, for concrete slots. For approximate slots (see the table description) this is an arbitrary number.

name text

The code-friendly name for this slot. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this slot.

abbreviation text

The standard abbreviation for this slot, as seen on the MMOLB team page.

role text

'Pitcher' or 'Batter'. NOTE: Players in a batting slot who are currently pitching may have either value. See the table description for details.

pitcher_type text or null

For pitching slots, 'Starter', 'Reliever', 'Closer', or 'Unknown'. 'Unknown' indicates this is a pitcher, but we don't know which type.

null for non-pitching slots.

slot_number integer or null

The slot number, if any (1 for SP1, 2 for SP2, etc.). You typically won't need to use this column, since it's already baked into the slot's name and abbreviation. It's provided in case it's ever convenient to manipulate a slot number numerically.

null for non-numbered slots (anything besides Starter and Reliever).

location bigint or null

The fielder location where this slot typically plays, if applicable. This is a foreign key into taxa.fielder_location.

WARNING: a player in a batting slot who is currently pitching may be recorded as occupying their batting slot or the Pitcher approximate slot, meaning their location may be recorded as Pitcher or as their typical fielder location. See the table description for details.

null for the Designated Hitter.

taxa.base

A base. Like from baseball.

Name Type Description
id bigint

The base number. Note that Home is base number 0. There is (currently) no base number 4.

This numbering was chosen to accommodate potential additional bases.

name text

The code-friendly name for this base. Guaranteed to contain alphanumeric characters only and start with a letter.

This is (currently) also the human-friendly name for this base.

bases_achieved integer

The number of bases the player has to advance to end up on this base. Equal to the base id for every base other than Home.

This column will be deleted if additional bases are ever added to MMOLB. A new column will be added (not necessarily to this schema) to record the total number of bases.

taxa.base_description_format

MMOLB occasionally describes bases in different formats. The same base may be described as "first", "first base", or "1st". This records which format was used for a particular description.

This taxa only exists for the purpose of exactly recreating game event messages from the database (aka "round-tripping"). If you find a genuine use for it in MMOLB analysis please let us know!

Name Type Description
id bigint

An arbitrary ID for a base description format. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using base description format ids in your queries or results.

name text

The code-friendly name for this base description format. Guaranteed to contain alphanumeric characters only and start with a letter.

taxa.fielding_error_type

The type of a fielding error: throwing or catching.

Name Type Description
id bigint

An arbitrary ID for a fielding error type. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using fielding error type ids in your queries or results.

name text

The code-friendly name for this base description format. Guaranteed to contain alphanumeric characters only and start with a letter.

This is (currently) also the human-friendly name.

taxa.pitch_type

Pitch type, as described by the pitch information at the right side of a pitch event.

Name Type Description
id bigint

An arbitrary ID for a pitch type. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using fielding error type ids in your queries or results.

name text

The code-friendly name for this pitch type. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this pitch type.

abbreviation text

The standard baseball abbreviation for this pitch type.

taxa.leagues

The leagues of MMOLB, both greater and lesser.

These leagues are hard-coded. In future this taxa will be replaced with a data.leagues table which is populated dynamically from MMOLB.

This is currently not referenced in any other tables. We know it would be desired, but it will take significant work to get there.

Name Type Description
id bigint

An arbitrary ID number for a league.

name text

The league's name.

color text

The league's background color as shown on the MMOLB lesser league page. Greater league teams also have background colors in the API.

emoji text

The league's emoji.

league_type text

'Greater' or 'Lesser'.

parent_team_id text

The MMOLB team id for the league's parent team. For Lesser League teams, this is the Greater League team they're associated with. For Greater League teams, this is the league's Superstar team.

mmolb_league_id text

The MMOLB id for the league itself.

taxa.day_type

MMOLB has several time periods that are not included in the normal day count. This includes the postseason, election, and several different types of break. This taxa lists the various special "day" types, as well as the standard "Day".

Name Type Description
id bigint

An arbitrary ID for a day type. When this appears as a foreign key you are encouraged to join this taxa and use its name column, rather than directly using day type ids in your queries or results.

name text

The code-friendly name for this day type. Guaranteed to contain alphanumeric characters only and start with a letter.

display_name text

The human-friendly name for this day type.

taxa.attribute_category

The category of an attribute, e.g. "Batting" or "Defense". Luck and Priority are in the "Generic" category.

Name Type Description
id bigint

An arbitrary ID for an attribute category. When this appears as a foreign key you are encouraged to join this taxa and use its namecolumn, rather than directly using attribute category ids in your queries or results.

name text

The code-friendly name for this attribute category. Guaranteed to contain alphanumeric characters only and start with a letter.

This is also the human-friendly name for each attribute category.

taxa.attribute

Identifies a player attribute, e.g. "Intimidation" or "Luck".

Name Type Description
id bigint

An arbitrary ID for an attribute. When this appears as a foreign key you are encouraged to join this taxa and use its namecolumn, rather than directly using attribute ids in your queries or results.

name text

The code-friendly name for this attribute. Guaranteed to contain alphanumeric characters only and start with a letter.

This is also the human-friendly name for each attribute.

category bigint

This attribute's category. References taxa.attribute_category.

taxa.attribute_effect_type

How a particular effect applies to an attribute. Based on [this description][https://discord.com/channels/1136709081319604324/1148829574524850197/1368118313292398673] from the MMOLB discord there are Flat, Additive, and Multiplicative bonuses.

This determines how effects are combined with the attribute's base value to create the final effective attribute value. The formula is:

(Base + sum(Flat)) * sum(Additive) * product(Multiplicative)

Base is the base value of that attribute (visible through the player report, for example), and Flat, Additive, and Multiplicative are the three effect types.

Name Type Description
id bigint

An arbitrary ID for an attribute effect type. When this appears as a foreign key you are encouraged to join this taxa and use its namecolumn, rather than directly using attribute effect type ids in your queries or results.

name text

The code-friendly name for this attribute effect type. Guaranteed to contain alphanumeric characters only and start with a letter.

This is also the human-friendly name for each attribute effect type.

taxa.handedness

Player handedness: Left, Right, or Switch. Players have a separate batting and pitching handedness.

Name Type Description
id bigint

An arbitrary ID for a handedness. When this appears as a foreign key you are encouraged to join this taxa and use its namecolumn, rather than directly using handedness ids in your queries or results.

name text

The code-friendly name for this handedness. Guaranteed to contain alphanumeric characters only and start with a letter.

This is also the human-friendly name for each handedness.

data

This is where you mostly want to look. It contains the actual data you'll be querying.

data.weather

MMOLB weathers.

This table is populated dynamically from API data in a non-determinstic order. This means ids are not stable. You should not store weather ids between queries, nor hard-code weather ids into queries. Always join with the weather table.

Two weathers are considered distinct if their name, emoji, or tooltip are different. There may be many entries in this table with the same name, but their emoji and/or tooltip will be different.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store weather ids between queries, nor hard-code weather ids into queries. Always join with the weather table.

name text

The weather's name, as displayed on mmolb.com.

emoji text

The weather's emoji, as displayed on mmolb.com.

tooltip text

The text that appears when you hover over a weather on mmolb.com.

data.games

All* known MMOLB games. This includes incomplete games, even though we don't process game events until the game is finished.

* Excluding a set of games from Season 0 which never finished and will never finish. Those games are excluded from MMOLDB.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Always use mmolb_game_id for that instead.

ingest bigint

The ID of the ingest that added this game. References info.ingests.

Incomplete games are deleted and re-added, so a given game's ingest may change over time.

mmolb_game_id text

The MMOLB game ID. This is the preferred way to identify a game.

weather bigint

The active weather during this game. This references data.weather. Weather ids are not stable. You should not store these ids between queries, nor hard-code them into queries. Always join to data.weather and use the weather name (and emoji and tooltip if you like) instead.

season integer

The MMOLB season during which this game was played.

day integer or null

The MMOLB regular or postseason day during which this game was played.

null for superstar games --- they have a superstar_day instead.

Exactly one of day and superstar_day is non-null.

superstar_day integer or null

The superstar day during which this game was played.

null for non-superstar games --- they have a day instead.

Exactly one of day and superstar_day is non-null.

away_team_emoji text

The away team's emoji.

away_team_name text

The away team's name.

away_team_mmolb_id text

The away team's MMOLB ID.

away_team_final_score integer or null

The away team's final score.

null for incomplete games.

home_team_emoji text

The home team's emoji.

home_team_name text

The home team's name.

home_team_mmolb_id text

The home team's MMOLB ID.

home_team_final_score integer or null

The home team's final score.

null for incomplete games.

is_ongoing boolean

Whether this game is currently being played. This is slightly different from "not finished" because there are some bugged games from season 0 which will never finish, but they are not "ongoing".

stadium_name text or null

The name of the stadium this game was played in, if any.

null for games from before stadiums were introduced in season 3.

from_version timestamp without time zone

Identifies which version of the game object was used to generate this game and its events in mmoldb. This is primarily used by MMOLDB itself to update games when their data changes. It matches the valid_from field of the entity in chron and in data.entities.

There is almost always a matching game in data.entities, but not quite. When a game is updated, the old raw game is deleted from data.entities before the new game is processed and added to data.games. This mostly happens with ongoing games, but it can also happen with finished games.

away_team_earned_coins integer or null

The number of coins the away team earned. This includes coins earned from Prosperity weather, from Geomagnetic Storms weather, and from any other future coin-earning sources.

null in the following situations:

  1. This is not a coin-awarding weather.
  2. This is a coin-awarding weather, but there was no message about teams earning coins. This can happen in e.g. Geomagnetic Storms weather if no storm events occurred. It is not possible in Prosperity weather, even though it was at once time possible for neither team to earn any coins. In that event there is still a message, but neither team earns any coins (MMOLDB records this as both teams earning 0 coins.)
  3. This is a coin-awarding weather that MMOLDB does not yet support. If this ever occurs, we are presumably working on it.
  4. This is a coin-awarding weather, and we do support it, and there was a message about the teams earning coins, but we don't know which is the home team and which is the away team because the teams have identical names and emoji.
home_team_earned_coins integer or null

The number of coins the home team earned. This includes coins earned from Prosperity weather, from Geomagnetic Storms weather, and from any other future coin-earning sources.

See away_team_earned_coins for an explanation of what null indicates.

away_team_photo_contest_top_scorer text or null

The name of the away team's top scorer in this game's photo contest.

null in the following situations:

  1. This is not Geomagnetic Storms weather.
  2. There were no aurorae during this game, so there was no photo contest.
  3. We can't tell which player belongs to the home team because the home and away teams have the exact same name and emoji.
away_team_photo_contest_score integer or null

The score that the away team's top scoring photo earned in this game's photo contest.

See away_team_photo_contest_top_scorer for an explanation of what null indicates.

home_team_photo_contest_top_scorer text or null

The name of the home team's top scorer in this game's photo contest.

See away_team_photo_contest_top_scorer for an explanation of what null indicates.

home_team_photo_contest_score integer or null

The score that the home team's top scoring photo earned in this game's photo contest.

See away_team_photo_contest_top_scorer for an explanation of what null indicates.

data.events

The big one. Contains a row for each pitch and certain non-pitch events (e.g. balk).

This table does not contain a row for every item in the game event log. Formulaic events, like game start and end messages, batter up messages, etc. are not included. Mound visits are also not included and are currently not available anywhere in the database. Item drops, falling stars, or other weather events are also not included. These items may be added in future.

data.event_baserunners and data.event_fielders are child tables of data.events.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular event use the combination of its mmolb_game_id (which you access by joining data.games on the game_id column) and game_event_index.

game_id bigint

The id of the game this event belongs to. References the data.games table.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use mmolb_game_id, which you access by joining data.games on this column.

game_event_index integer

The index of this game event (starting at 0) in its game's event log. This is used in combination with mmolb_game_id to identify a game event.

fair_ball_event_index integer or null

Fair balls emit two consecutive messages in MMOLB. The first declares that the ball is fair and gives its type and direction, then the second gives its outcome. The second event's id is stored in game_event_index and the first event's id is stored in fair_ball_event_index.

This would be more accurately named fair_ball_game_event_index but the name is already long enough as it is.

null if this event is not a fair ball.

inning integer

The inning number, 1-indexed.

There are event messages that happen during "inning 0" (before the game announces the top of the 1st), but none of them are any of the event types that we store in this table.

top_of_inning boolean

true when it's the top of the inning, false when it's the bottom.

When it's true, the home team is defending and the away team is batting. When it's false, the away team is defending and the home team is batting.

event_type bigint

The event's type. References taxa.event_type.

taxa.event_type ids are stable, but you are encouraged to join taxa.event_type and use its name column rather than directly using event type ids in your queries or results.

hit_base bigint or null

If this event is a hit, the base the batter reached. Home plate (0th base) indicates a home run. References taxa.base.

taxa.base has meaningful id numbers, so it's fine to use them directly in your queries and results (so long as you remember that home plate is 0th base). You can of course still join taxa.base if it's convenient to do so.

null if this event is not a hit.

fair_ball_type bigint or null

If this event is a fair ball, the batted ball type. References taxa.fair_ball_type.

taxa.fair_ball_type ids are stable, but you are encouraged to join taxa.fair_ball_type and use its name column rather than directly using fair ball type ids in your queries or results.

null if this event is not a fair ball.

fair_ball_direction bigint or null

If this event is a fair ball, the fielder position that ball was heading towards. References taxa.fielder_location.

taxa.fielder_location ids are stable, but you are encouraged to join taxa.fielder_location and use its name column rather than directly using fielder location ids in your queries or results.

null if this event is not a fair ball.

fielding_error_type bigint or null

If this event is a fielding error, the type of error (throwing or catching). References taxa.fielding_error_type.

taxa.fielding_error_type ids are stable, but you are encouraged to join taxa.fielding_error_type and use its name column rather than directly using fielding error type ids in your queries or results.

null if this event is not a fielding error.

pitch_type bigint or null

If this event is a pitch, the type of pitch. References taxa.pitch_type.

taxa.pitch_type ids are stable, but you are encouraged to join taxa.pitch_type and use its name column rather than directly using pitch type ids in your queries or results.

null if this event is not a pitch (e.g. a balk).

pitch_speed double precision or null

If this event is a pitch, the speed of the pitch.

This value is parsed from the display text, which is (as of this writing) truncated to one decimal place.

null if this event is not a pitch (e.g. a balk).

pitch_zone integer or null

If this event is a pitch, the region of the strike zone (or near the strike zone) that the pitch went through.

The assignment of numbers to pitch zones comes straight from MMOLB. The zones are arranged like so:

11       12
   1-2-3
   4-5-6
   7-8-9
13       14

null if this event is not a pitch (e.g. a balk).

described_as_sacrifice boolean or null

Records whether MMOLB described this event as a sacrifice.

Ordinarily this would not be stored, and sacrifices would be detected by examining the baserunners. However, early in MMOLB's history, some plays were described as sacrifices despite not scoring nor advancing any runners. See the bug report on the MMOLB official discord. This means the only way to know if an event is a purported sacrifice is to store that information separately.

Although this column exists because of the bug, it is not specific to the bugged events. This will be true for events which are correctly described as sacrifices, in addition to events which are incorrectly described as sacrifices.

This column is mainly intended to ensure game event messages can be perfectly reconstructed from the database (aka "round-tripped"), but it may be useful for analysis as well.

null if events of this type cannot be a sacrifice. As of this writing, the only event types that can be sacrifices are caught outs and grounded double plays.

is_toasty boolean or null

"toasty" is an easter egg that Danny (the creator of MMOLB) has said exists to prevent a situation would otherwise be cause crash in the sim. In season 2 and earlier it manifested as the text "Perfect catch!" at the end of an event. During the s2 postseason Danny shared his intent (in Discord) to change the message to "Amazing throw!" for ground balls. That message is also how know that "toasty" is the generic term for a perfect catch or amazing throw.

This column is mainly intended to ensure game event messages can be perfectly reconstructed from the database (aka "round-tripped"), but it may be useful for analysis as well.

null if events of this type can never be toasty. Note that toasty is a very rare occurrence, so there may be events that can be toasty that we don't yet know can be toasty.

balls_before integer

Number of balls in the count before this event.

The number of balls in the count after this event can be easily computed using the helper columns on taxa.event_type. For example, if you want walks to be represented with a count of 4-X, you can use the provided balls_after function, which takes the event and event type as arguments. In a query that might look like:

select
    data.balls_after(ev, et)
from data.events ev
left join taxa.event_type et on et.id = ev.event_type

If you want the count to be zeroed for a PA-ending event, like it is on mmolb.com, use the slightly longer case when event_type.ends_plate_appearance then 0 else data.balls_after(ev, et) end. In a query that might look like:

select
    (case when
        et.ends_plate_appearance
    then
        0
    else
        data.balls_after(ev, et)
    end)
from data.events ev
left join taxa.event_type et on et.id = ev.event_type
strikes_before integer

Number of strikes in the count before this event.

The number of strikes in the count after this event can be easily computed using the helper columns on taxa.event_type. For example, if you want outs and batted balls to add to the strike total, you can use the provided strikes_after function, which takes the event and event type as arguments. In a query that might look like:

select
    data.strikes_after(ev, et)
from data.events ev
left join taxa.event_type et on et.id = ev.event_type

If you want strikeouts (including foul tip strikeouts) to add to the count, but not batted balls, replace is_strike with is_basic_strike.

If you want the count to be zeroed for a PA-ending event, like it is on mmolb.com, use the slightly longer case when event_type.ends_plate_appearance then 0 else data.strikes_after(event, event_type) end. In a query that might look like:

select
    (case when
        et.ends_plate_appearance
    then
        0
    else
        data.strikes_after(ev, et)
    end)
from data.events ev
left join taxa.event_type et on et.id = ev.event_type
outs_before integer

The number of outs in the half-inning before this event.

outs_after integer

The number of outs in the half-inning after this event.

Computing outs_after within a query, while possible, is much more complicated than balls_after or strikes_after. It's also unambiguous, unlike balls_after or strikes_after.

errors_before integer

The number of errors in the half-inning before this event. This is primarily useful for calculating earned runs.

errors_after integer

The number of errors in the half-inning after this event. This is primarily useful for calculating earned runs.

away_team_score_before integer

The away team's score before this event.

away_team_score_after integer

The away team's score after this event.

home_team_score_before integer

The home team's score before this event.

home_team_score_after integer

The home team's score after this event.

pitcher_name text

The active pitcher's name.

The pitcher's ID is not yet available, but we're working on it.

pitcher_count integer

A number that starts at 0 at the beginning of each game and is incremented each time there is a pitcher change.

For the purposes of this column, a pitcher change is when a different pitching slot is chosen to be the active pitcher for this game. When pitchers are swapped as a result of an augment, or when a player Retires and is replaced in the middle of a game (due to a falling star, for example) this number does not increase. Note that augments have fired during games as late as Season 2 (although after season 0 it's rare).

Each team maintains a separate pitcher_count. The pitcher_count stored in an event is the defending team's pitcher_count. Use top_of_inning to tell which team is defending.

batter_name text

The active batter's name.

The batter's ID is not yet available, but we're working on it.

batter_count integer

A number that starts at 0 at the start of each game and is incremented each time a different batter begins a PA.

Note that this does not increase when the same batter begins a PA multiple times in a row. As of Season 2 this only happens when the previous PA was interrupted by an inning-ending caught stealing. To separate multiple consecutive appearances by the same batter, use batter_subcount.

This number also does not increase when a batter is swapped mid-PA as a result of an augment, or when a player Retires and is replaced in the middle of a PA (due to a falling star, for example). Note that augments have fired during games as late as Season 2 (although after season 0 it's rare).

Each team maintains a separate batter_count. The batter_count stored in an event is the batting team's batter_count. Use top_of_inning to tell which team is batting.

batter_subcount integer

A number that is reset to 0 each time batter_count changes and is incremented each time the previously-active batter begins a new PA.

As of Season 2 this only happens when the previous PA was interrupted by an inning-ending caught stealing. As of Season 2 the only possible values for batter_subcount are 0 and 1, but future game mechanics may make higher numbers possible.

This is used in combination with batter_count to group plate appearances. If you want to group all the pitches, from before and after the caught-stealing, into a single plate appearance use group by batter_count. If you want to count the interrupted plate appearance and the subsequent plate appearances as two separate groups of events, use group by batter_count, batter_subcount. If you want to only include events from the last consecutive plate appearance (discarding the rest), the query is more complicated. Here's an example:

select
    count(1)
from data.events e
where batter_subcount = (
    select max(batter_subcount)
    from data.events e2
    where e2.game_id = e.game_id
      and e2.top_of_inning = e.top_of_inning
      and e2.batter_count = e.batter_count)
group by
    game_id,
    top_of_inning,
    batter_count,
    batter_subcount

Note that if you only want the last event in each group of consecutive plate appearances, it's sufficient to group by batter_count and then select the row with the highest game_event_index, which should be much faster.

Or, if you only want the plate-appearance-ending events, join taxa.event_type and filter on ends_plate_appearance. As of Season 2 there should only be one event with ends_plate_appearance = true for each (game_id, top_of_inning, batter_count) combination.

cheer text or null

The crowd's cheer on this event, if any.

Note: Cheers on fair balls are from the event referenced by fair_ball_event_index. Cheers on other event types are from the event referenced by game_event_index.

WARNING: This column may be turned into a foreign key reference soon, similar to weather. Treat it as unstable.

null for events where the crowd did not cheer.

data.event_baserunners

Baserunner activity for each event in data.events.

This contains a row for every baserunner that existed at any point during each event. That includes runners who stayed on the same base, runners who scored, and batters who become batter-runners but are called out before reaching a base.

The particulars:

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular baserunner on a particular event use the combination of its mmolb_game_id, game_event_index, and base_before. You have to join the data.events table to access game_event_index, and from there join the data.games tables to access mmolb_game_id.

event_id bigint

The id of the event this baserunner belongs to. References the data.events table.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use the combination of mmolb_game_id and game_event_index as described in the documentation for data.events' id column.

baserunner_name text

The baserunner's name.

The baserunner's ID is not yet available, but we're working on it.

base_before bigint or null

The base the runner occupied before this event. References taxa.base.

null indicates this is the batter-runner. The batter is not considered to be at any base while they are batting.

base_after bigint

The base the runner occupied after this event, or the base they were put out at. References taxa.base.

Runners who score always have a row in this table with base_after = 0.

Most queries which reference this column should also reference is_out.

is_out boolean

Whether the runner was put out on this event. If so, base_after is the base they were put out at.

base_description_format bigint or null

The flavor used to describe which base the player reached or was put out at. References taxa.base_description_format.

MMOLB describes the same base in different ways for variety. This column stores which way was used for a particular event, mostly for use in reconstructing event text.

null for event types which never describe a base.

steal boolean

Whether this baserunner attempted to steal a base during this event. Reference is_out to tell whether the attempt was successful.

source_event_index integer or null

The index of the event that put this baserunner on base. Primarily useful for calculating earned runs.

null if this baserunner did not reach base as a result of a pitch. For example, automatic runners in extra innings don't have a source_event_index.

is_earned boolean

True if this runner scoring would count as an earned run for the pitcher referenced by source_event_index. If false, this runner's score will never count as an earned run.

data.event_fielders

Fielder activity for each event in data.events.

This contains a row for every fielder involved in the play.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular baserunner on a particular event use the combination of its mmolb_game_id, game_event_index, and play_order. You have to join the data.events table to access game_event_index, and from there join the data.games tables to access mmolb_game_id.

event_id bigint

The id of the event this fielder belongs to. References the data.events table.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use the combination of mmolb_game_id and game_event_index as described in the documentation for data.events' id column.

fielder_name text

The fielder's name.

The fielder's ID is not yet available, but we're working on it.

fielder_slot bigint

A best-effort attempt at recording the fielder's slot. Due to various inconvenient behaviors of MMOLB this is not always accurate. References taxa.slot.

At various times in MMOLB's short history, and for various event types, the fielders have been identified differently. There are two main issues:

  1. Pitchers are sometimes described as "P" (for "Pitcher"), and at other times with their assigned slot on the team's roster. This means that when a position player is pitching, and fields the ball, they are sometimes (correctly) called "pitcher" and other times (incorrectly) labeled with the position they usually play.
  2. Players on superstar teams play different positions than they do on their usual teams. Superstar games sometimes describe the player with their correct position for the superstar team, and other times (incorrectly) use the position that player has on usual team.

See the documentation of taxa.slot for even more details.

We want to correct these errors within MMOLDB, but it's going to take some time to lay the groundwork that will allow us to do so. In the meantime, take this value with a handful of salt.

play_order integer

The order of this fielder within the play. This is primarily used as a sort key. It can also be used along with mmolb_game_id and game_event_index to uniquely and stably identify a row in this table. See the documentation of id for more.

data.entities

A mirror of Chron's entities endpoint for selected kinds. Currently only holds kind == "game".

NOTE: Unlike Chron, a given entity kind is either in this table or data.versions, not both. kinds for which MMOLDB uses historical data are stored in data.versions, and those for which MMOLDB doesn't use historical data are stored in data.entities. Not all Chron kinds are ingested.

Name Type Description
kind text

The kind of the entity, e.g. "game", "team", "player", etc. Matches Chron's kind.

The type of this field may be changed in the future.

entity_id text

The entity's MMOLB id.

valid_from timestamp without time zone

The earliest date that Chron observed this version of the entity. This may not (and typically will not) line up perfectly with when this version was created because Chron can only poll so often.

Note entities doesn't have a valid_to column because it only stores the latest version of each entity, for which valid_to is always null.

data jsonb

The actual data of the entity. This is the data exactly as returned by the MMOLB API with no modification, so structure is subject to change whenever the API changes.

data.versions

A mirror of Chron's entities endpoint for selected kinds. Currently holds every kind that MMOLDB uses except for "game".

NOTE: Unlike Chron, a given entity kind is either in this table or data.entities, not both. kinds for which MMOLDB uses historical data are stored in data.versions, and those for which MMOLDB doesn't use historical data are stored in data.entities. Not all Chron kinds are ingested.

Name Type Description
kind text

The kind of the entity, e.g. "game", "team", "player", etc. Matches Chron's kind.

The type of this field may be changed in the future.

entity_id text

The entity's MMOLB id.

valid_from timestamp without time zone

The earliest date that Chron observed this version of the entity. This may not (and typically will not) line up perfectly with when this version was created because Chron can only poll so often.

valid_to timestamp without time zone or null

The earliest date after valid_from that Chron observed a different version of this entity -- that is, the first date where we're sure this entity was no longer valid. This may not (and typically will not) line up perfectly with when this version was actually invalidated because Chron can only poll so often.

null if this version is still valid.

data jsonb

The actual data of the entity. This is the data exactly as returned by the MMOLB API with no modification, so structure is subject to change whenever the API changes.

data.aurora_photos

A record of players snapping photos of the aurora during Geomagnetic Storms weather. Every event with aurora photos has two, one from each team, and they are stored as separate rows in this table. is_listed_first identifies which one is listed first.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular aurora photo use the combination of its mmolb_game_id, game_event_index, and is_listed_first. You have to join the data.events table to access game_event_index, and from there join the data.games tables to access mmolb_game_id.

event_id bigint

The id of the event this aurora photo belongs to. References the data.events table.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use the combination of mmolb_game_id and game_event_index as described in the documentation for data.events' id column.

is_listed_first boolean

true if this photo was the first one listed in its event, false if it was the second.

There's always one photo from each team, but there's not enough information in the event itself to know which team, so MMOLDB doesn't attempt to record that.

team_emoji text

The team emoji of the player who took this picture.

player_slot bigint

The roster slot of the player who took this picture. References taxa.slot.

player_name text

The name of the player who took this picture.

data.ejections

Instances of players being ejected by ROBO-UMP.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular ejection use the combination of its mmolb_game_id and game_event_index. You have to join the data.events table to access game_event_index, and from there join the data.games tables to access mmolb_game_id.

event_id bigint

The id of the event this ejection happend on. References the data.events table.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use the combination of mmolb_game_id and game_event_index as described in the documentation for data.events' id column.

team_emoji text

The team emoji of the player who was ejected.

team_name text

The team name of the player who was ejected.

ejected_player_name text

The name of the player who was ejected.

ejected_player_slot bigint

The roster slot of the player who was ejected. References taxa.slot.

violation_type text

The type of violation, e.g. "Communication" or "Sportsmanship".

reason text

The reason for the ejection.

replacement_player_name text

The name of the player is taking the place of the ejected player.

replacement_player_slot bigint or null

The roster slot of the player who is taking the place of the ejected player, if one is given. References taxa.slot.

null if the replacement is a bench player.

data.modifications

The possible player modifications, including Greater and Lesser boons (which are special categories of modifications).

MMOLB doesn't provide a machine-readable description of modifications' effects, and no work has yet been done on parsing them from the description. If you want that, please let us know in the Discord or Github so we know to prioritize it. Or, better yet, contribute it yourself!

This table is populated dynamically from API data in a non-determinstic order. This means ids are not stable. You should not store modification ids between queries, nor hard-code modification ids into queries. Always join with the modifications table.

Two modifications are considered distinct if their name, emoji, or description are different. There may be many entries in this table with the same name, but their emoji and/or description will be different.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store modification ids between queries, nor hard-code modification ids into queries. Always join with the modifications table.

name text

The modification's name. This is not guaranteed to be code-friendly.

emoji text

The modification's emoji.

description text

The modification's description as seen in the tooltip.

data.player_versions

Historical and current player information.

This table contains basic information about a player, like their name and birthday. It has many child tables with more advanced information like their augment and recompose history, their clubhouse reports, and their equipment. Most of these tables use valid_from and valid_until to track which version of the player data was active at any point in time.

For any of these tables, valid_from is the date (always UTC) when this version of the player was first observed, and valid_until is the date when a different version of the player was first observed, or null if this is still the active version. The range of times when the player version was valid is therefore valid_from inclusive to valid_until exclusive. The (non-null) valid_until of one version should exactly match the valid_from of the next version.

Remember that these are the dates of observations of the versions. In reality the data changed sometime between the last observation of the previous version (which is not currently exposed by chron) and the first observation of the new version. In fact, caching and network delay may mean that the change actually occurred on the "wrong" side of the valid_from/valid_until date. This should hopefully be rare.

To select only active versions, add where valid_until is null to your query. To select the version that was active at a given time t, add where valid_from <= t and (valid_until > t or valid_until is null) to your query. Remember to group that in parentheses if you're adding it to other where clauses that are combined using or.

Much of the player information is in child tables:

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a player use their mmolb_player_id.

mmolb_player_id text

The MMOLB id for this player. This is the preferred way to identify a player.

Keep in mind that recomposing players does not change their MMOLB id. If you want to separate recomposed players (and you generally should), there are two ways to do it. One is to group by name as well as id (it must be both, because there are many examples of players with the same name), and assume every name change is a recomposition. That's not strictly true, there have been players whose name was changed without a recomposition, but not many.

The other option is to use data.player_recompositions to track the generation of the player id, i.e. how many times it's been recomposed. Then each (mmolb_player_id, generation) represents a unique player. Unfortunately, there are (as yet unexplained) instances where the straightforward association between recompositions and their player version chooses the wrong player.

If you want to do it that way, you can use a query like this one:

select
	*,
	(select count(1)
	 from data.player_recompositions pr
	 where pr.mmolb_player_id=pv.mmolb_player_id
	 	and pr.time < pv.valid_from
	 ) as generation
from data.player_versions pv
limit 10 -- limit while debugging
valid_from timestamp without time zone

The earliest time this version of the player was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the player was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

duplicates integer

Currently being used to debug MMOLDB itself. This field may be deleted soon.

first_name text

The player's first name.

Players names are usually found in full when they appear in other tables. You can assemble the player's full name with the expression first_name || ' ' || last_name.

last_name text

The player's last name.

Players names are usually found in full when they appear in other tables. You can assemble the player's full name with the expression first_name || ' ' || last_name.

batting_handedness bigint or null

Hand this player uses to bat. References taxa.handedness.

null if this player's batting handedness was not recognized. This represents an ingest error.

pitching_handedness bigint or null

Hand this player uses to pitch. References taxa.handedness.

null if this player's pitching handedness was not recognized. This represents an ingest error.

home text

The player's birthplace. This is the location of the team they were born on at the time they were born.

birthseason integer

The season during which the player was born.

birthday_type bigint or null

The type of the day during which the player was born. References taxa.day_type. See the documentation on taxa.day_type for details.

null if this player's birthday type was not recognized. This represents an ingest error.

birthday_day integer or null

The regular day during which the player was born. This should be set if and only if birthday_type is Day.

null if this player wasn't born on a regular day.

birthday_superstar_day integer or null

The superstar day during which the player was born. This should be set if and only if birthday_type is SuperstarDay.

null if this player wasn't born on a superstar day.

likes text

The thing this player likes.

dislikes text

The thing this player dislikes.

number integer

This player's number (presumably a jersey number). As far as we know this has no mechanical effect.

mmolb_team_id text or null

The MMOLB id for the team this player belongs to.

Teams are planned for addition to MMOLDB, and at that time you'll be able to use this to connect a player to their team.

null if this player is not currently on a team. Players can become teamless for many reasons, including a player Retiring after losing all their durability, being Released (full-team Release, different to Recompose) in the Hall of Unmaking, and Relegation (a season 0 mechanic which is no longer available).

slot bigint or null

The player's roster slot. References taxa.slot.

This can be used along with the player's name to more accurately connect players to game events.

null if this player's slot was not recognized. This represents an ingest error.

durability double precision

The player's durability. Displayed as a green progress bar on the player page.

This is a number that starts at 1 and is reduced by 0.05 at the end of every season, as well as when the player is damaged by a falling star. When it reaches 0, the player Retires.

greater_boon bigint or null

The player's Greater Boon, if they have one. This references data.modifications.

Modification ids (including boons) are not stable. You should not store these ids between queries, nor hard-code them into queries. Always join to data.modifications and use the modification name (and emoji and description if you like) instead.

null if this player does not have a Greater Boon.

lesser_boon bigint or null

The player's Lesser Boon, if they have one. This references data.modifications.

Modification ids (including boons) are not stable. You should not store these ids between queries, nor hard-code them into queries. Always join to data.modifications and use the modification name (and emoji and description if you like) instead.

null if this player does not have a Lesser Boon.

num_modifications integer

The number of modifications the player has.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see player modifications, use the data.player_modification_versions table.

occupied_equipment_slots ARRAY

A list of which equipment slots are occupied on this player.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see player equipment, use the data.player_equipment_versions table.

included_report_categories ARRAY

A list of which clubhouse reports are included on this player.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see player reports, use the data.player_report_versions table.

data.player_modification_versions

Records instances of modifications on players. This connects data.player_versions to data.modifications.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions. There can be a break in versions in this table without a corresponding break in versions in data.player_versions and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular modification on a particular player use the combination of its mmolb_player_id and modification_index.

mmolb_player_id text

The MMOLB id for the player this modification belongs to. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their modifications.

modification_index integer

The index of this modification in the player's modifications list. A (mmolb_player_id, modification_index) pair uniquely and stably identifies a modification on a player.

valid_from timestamp without time zone

The earliest time this version of the player modification was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the player modification was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

duplicates integer

Currently being used to debug MMOLDB itself. This field may be deleted soon.

modification_id bigint

Which modification this is. References the data.modifications table.

Modification ids are not stable. You should not store these ids between queries, nor hard-code them into queries. Always join to data.modifications and use the modification name (and emoji and description if you like) instead.

data.player_equipment_versions

Records player equipment.

Though pieces of equipment are distinct objects that can theoretically be tracked as they're moved around, MMOLB doesn't provide a stable identifer for them. Because of this, we use the player and slot the item is equipped into as its identifier, and an equipment being moved from one player to another is treated as if the old item ceased to exist and the new item came into being. Same for if an existing item is modified using orbs.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions. There can be a break in versions in this table without a corresponding break in versions in data.player_versions and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular piece of equipment on a particular player use the combination of its mmolb_player_id and equipment_slot.

mmolb_player_id text

The MMOLB id for the player this equipment is equipped to. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their equipment.

equipment_slot text

The equipment slot that this piece of equipment is equipped to. Current values are:

  • Accessory
  • Body
  • Feet
  • Hands
  • Head

This may be replaced with a taxa.

A (mmolb_player_id, equipment_slot) pair uniquely and stably identifies a piece of equipment.

valid_from timestamp without time zone

The earliest time this version of the player equipment was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the player equipment was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

duplicates integer

Currently being used to debug MMOLDB itself. This field may be deleted soon.

emoji text

The equipment's emoji.

name text

The equipment's normal name.

If the equipment has a rare_name, this name is not used. Otherwise, this name is combined with the item's prefixes and suffixes to create its displayed name.

As of the end of season 4, there is only one name for each equipment slot:

  • Accessory: Ring
  • Body: T-Shirt
  • Feet: Sneakers
  • Hands: Gloves
  • Head: Cap
special_type text or null

This field exists in the API but we've never seen a value from it.

null for every known piece of equipment.

description text or null

This field exists in the API but we've never seen a value from it.

null for every known piece of equipment.

rare_name text or null

Rare and higher magic items gain a new name that overrides the prefix-and-suffix-based default name. This is that name.

Items keep their rare name even if they are downgraded past rare with a Disintegrating Orb.

null if this item is not, and has never been, Rare or higher.

cost integer or null

Presumably, the amount the equipment cost to buy from Mother Quaelyth.

null if the cost was not an integer. This represents an ingest error.

prefixes ARRAY

A list of up to two prefixes the item has. These are prepended to name to create the item's display name, unless overridden by rare_name.

suffixes ARRAY

A list of up to two suffixes the item has. These are appended to name to create the item's display name, unless overridden by rare_name.

rarity text or null

The item's rarity. "Normal", "Magic", or "Rare".

null if the item's rarity is not a string. This represents a player ingest error.

num_effects integer

The number of effects this piece of equipment has.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see equipment effects, use the data.player_equipment_effect_versions table.

data.player_equipment_effect_versions

Records player equipment effects.

See the documentation of data.player_equipment_versions for a description of how MMOLDB handles equipment.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions, nor versions of data.player_equipment_versions. There can be a break in versions in this table without a corresponding break in versions in either of those tables and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular effect on a particular piece of equipment on a particular player use the combination of its mmolb_player_id, equipment_slot, and effect_index.

mmolb_player_id text

The MMOLB id for the player this equipment is equipped to. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their equipment.

equipment_slot text

The equipment slot that this piece of equipment is equipped to. See equipment_slot on data.player_equipment_versions for more.

effect_index integer

The index of this effect within this piece of equipment's list of effects. A (mmolb_player_id, equipment_slot, effect_index) triple uniquely and stably identifies an equipment effect.

valid_from timestamp without time zone

The earliest time this version of the player equipment effect was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the player equipment effect was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

duplicates integer

Currently being used to debug MMOLDB itself. This field may be deleted soon.

attribute bigint

The attribute this effect modifies. References taxa.attribute.

effect_type bigint

The type of effect. References taxa.attribute_effect_type.

As of the end of season 4, the only effect type that's ever appeared is Flat.

value double precision

The value of the modification. This is stored exactly as displayed in the MMOLB UI. This means that attribute values range from 5 to 20.

data.player_feed_versions

Records when new items appear in the player feed.

This is mainly used by MMOLDB itself. It's not likely to be useful outside of that.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions. There can be a break in versions in this table without a corresponding break in versions in data.player_versions and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular player feed use its mmolb_player_id.

mmolb_player_id text

The MMOLB id for the player this feed belongs to. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their feeds.

valid_from timestamp without time zone

The earliest time this version of the player feed was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the player feed was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

duplicates integer

Currently being used to debug MMOLDB itself. This field may be deleted soon.

num_entries integer

The number of entries in this feed.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see the information from the feed, use the tables:

  • data.player_attribute_augments for the record of all augments to attributes each player has recieved.
  • data.player_paradigm_shifts for the record of all paradigm shifts that have affected this player.
  • data.player_recompositions for the record of all times this player has been recomposed.

data.player_attribute_augments

Records the changes to player attributes as the result of augments.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular augment use the combination of its mmolb_player_id and feed_event_index.

mmolb_player_id text

The MMOLB id for the player this that was augmented. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their augments.

feed_event_index integer

The index of this augment in the player's feed. A (mmolb_player_id, feed_event_index) pair uniquely and stably identifies an applied augment.

time timestamp without time zone

The exact time this augment was applied.

This time is given to us by the MMOLB API, rather than being the time of an API call, and so should be trustworthy.

season integer

The season during which the augment was applied.

day_type bigint or null

The type of the day during which the augment was applied. References taxa.day_type. See the documentation on taxa.day_type for details.

null if this augment's day was not recognized. This represents an ingest error.

day integer or null

The regular day during which the augment was applied. This should be set if and only if day_type is Day.

null if this augment wasn't applied on a regular day.

superstar_day integer or null

The superstar day during which the augment was applied This should be set if and only if day_type is SuperstarDay.

null if this augment wasn't applied on a superstar day.

attribute bigint

The attribute this augment modifies. References taxa.attribute.

value integer

The value by which the attribute was increased. This is stored exactly as displayed in the MMOLB UI. Common values are 5, 6, 15, 30, and 50.

data.player_paradigm_shifts

Records every instance of each player being involved in a paradigm shift.

Each paradigm shift that fires will create 9 entries in this table. MMOLDB doesn't (yet) connect the paradigm shifts to a team.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular paradigm shift use the combination of its mmolb_player_id and feed_event_index.

mmolb_player_id text

The MMOLB id for the player whose priority was changed. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their paradigm shifts.

feed_event_index integer

The index of this paradigm shift in the player's feed. A (mmolb_player_id, feed_event_index) pair uniquely and stably identifies an applied paradigm shift on a particular player.

time timestamp without time zone

The exact time this paradigm shift was applied.

This time is given to us by the MMOLB API, rather than being the time of an API call, and so should be trustworthy.

season integer

The season during which the paradigm shift was applied.

day_type bigint or null

The type of the day during which the paradigm shift was applied. References taxa.day_type. See the documentation on taxa.day_type for details.

null if this paradigm shift's day was not recognized. This represents an ingest error.

day integer or null

The regular day during which the paradigm shift was applied. This should be set if and only if day_type is Day.

null if this paradigm shift wasn't applied on a regular day.

superstar_day integer or null

The superstar day during which the paradigm shift was applied This should be set if and only if day_type is SuperstarDay.

null if this paradigm shift wasn't applied on a superstar day.

attribute bigint

The attribute this paradigm shift sorts the lineup by. References taxa.attribute.

data.player_recompositions

Records every instance of a player being recomposed.

This table is more complicated because there have been 3 distinct recompose bugs that have been handled by deleting and/or inserting inferred recompositions which aren't present in the feed. Inferred recompositions have the feed_event_index of the first feed event to happen after them, but also have an inferred_event_index.

The 3 bugs have been:

  1. Some recompositions were reverted entirely, and their events were removed from the player feed. These have been handled by suppressing the recompositions entirely, so they don't appear in this table. After more investigation the handling may or may not change to insert two inferred recompositions in quick succession, with the latter reverting the former.

  2. Some recompositions occurred without a feed event. These have been handled by adding an inferred recomposition. The time for these inferred recompositions is set to the first time we observed the recomposed player, and the season and day derived from that time.

  3. Some recompositions had their feed event and attributes reverted, but not their name change. These have been handled by suppressing the real recompositions (for the short time they appeared) and inserting an inferred recomposition to replace it (making it permanent), plus an additional recomposition when the overwrite happens. This latter recomposition doesn't change the player name, but does change their attributes. It's also marked as reverting the prior recomposition.

    Because of this bug, it is not entirely true to assume that player attributes after a recomposition are within the newly-generated-player boundaries. You can check for non-null reverts_recomposition to see if the "recomposed" player's attributes weren't freshly generated and may have been subject to augments.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular recomposition use the combination of its mmolb_player_id, inferred_event_index, and feed_event_index.

mmolb_player_id text

The MMOLB id for the player who was recomposed. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their recompositions.

inferred_event_index integer or null

For an inferred recomposition, the index of this recomposition amongst only the other inferred recompositions with the same feed_event_index.

null if this is not an inferred recompsition.

feed_event_index integer

The index of this recomposition in the player's feed, for non-inferred recompositions. For inferred recompsitions this is the index of the next event in the player's feed. See the table description for more details.

A (mmolb_player_id, inferred_event_index, feed_event_index) triple uniquely and stably identifies an applied recomposition on a particular player.

time timestamp without time zone

The exact time this recomposition was applied, for non-inferred recompositions. For inferred recompositions this time may not be exact.

This time is given to us by the MMOLB API, rather than being the time of an API call, and so should be trustworthy (except in the case of inferred recompositions).

season integer

The season during which the recomposition was applied.

day_type bigint or null

The type of the day during which the recomposition was applied. References taxa.day_type. See the documentation on taxa.day_type for details.

null if this recomposition's day was not recognized. This represents an ingest error.

day integer or null

The regular day during which the recomposition was applied. This should be set if and only if day_type is Day.

null if this recomposition wasn't applied on a regular day.

superstar_day integer or null

The superstar day during which the recomposition was applied This should be set if and only if day_type is SuperstarDay.

null if this recomposition wasn't applied on a superstar day.

player_name_before text

The player's full name before the recomposition.

player_name_after text

The player's full name after the recomposition.

reverts_recomposition timestamp without time zone or null

If this recomposition reverts another recomposition, the time of the recomposition it reverts. This means that after this recomposition, the player's attributes are set equal to what they were before the recomposition that this recomposition reverts. See the table description for more details.

null if this recomposition does not revert another recomposition.

data.player_report_versions

The contents of player clubhouse reports, also called "talk"s.

Attributes are recorded in data.player_report_attribute_versions. For many common uses of reports you can skip this table entirely and go straight to that one.

Before the Season 4 superstar break, reports displayed the player's attributes as of the creation of the report. After the Season 4 superstar break, reports display the player's current attributes (for our purposes, this means the attributes as of valid_from and until sometime before valid_until). The season and day of a report does not update live, even after the Season 4 change. For post-s4-ss reports, the season and day is not particularly useful.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions. There can be a break in versions in this table without a corresponding break in versions in data.player_versions and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular report use the combination of its mmolb_player_id and category.

mmolb_player_id text

The MMOLB id for the player who this report is about. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their reports.

category bigint

The category this report is for ("Batting", "Baserunning", etc.). References taxa.attribute_category.

valid_from timestamp without time zone

The earliest time this version of the report was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the report was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

season integer or null

The season during which the report was created.

This value only changes when a new report is generated (including the reports from Recomposing the player). Do not assume that the value of an attribute corresponds with the season that its report was generated.

null for reports that were generated before their season and day were recorded.

day_type bigint or null

The type of the day during which the report was created. References taxa.day_type. See the documentation on taxa.day_type for details.

This value only changes when a new report is generated (including the reports from Recomposing the player). Do not assume that the value of an attribute corresponds with the day_type that its report was generated.

null for reports that were generated before their season and day were recorded.

day integer or null

The regular day during which the report was created. This should be set if and only if day_type is Day.

This value only changes when a new report is generated (including the reports from Recomposing the player). Do not assume that the value of an attribute corresponds with the day that its report was generated.

null if this report wasn't created on a regular day, or if this report was generated before reports' season and day were recorded.

superstar_day integer or null

The superstar day during which the report was created. This should be set if and only if day_type is SuperstarDay.

This value only changes when a new report is generated (including the reports from Recomposing the player). Do not assume that the value of an attribute corresponds with the superstar day that its report was generated.

null if this report wasn't created on a superstar day, or if this report was generated before reports' season and day were recorded.

quote text

The quote from this player report.

included_attributes ARRAY

A list of which attributes are included in this report.

This is used internally by MMOLDB itself, and is not likely to be useful to users. To see attribute values, use the data.player_report_attribute_versions table.

data.player_report_attribute_versions

The values of attributes in player clubhouse reports.

This is a child table of data.player_report_versions, but for many common uses of attributes you don't need to reference the intermediate table.

Before the Season 4 superstar break, reports displayed the player's attributes as of the creation of the report. After the Season 4 superstar break, reports display the player's current attributes (for our purposes, this means the attributes as of valid_from and until sometime before valid_until). The season and day of a report does not update live, even after the Season 4 change. For post-s4-ss reports, the season and day is not particularly useful.

See the documentation of data.player_versions for a description of how to use valid_from and valid_until.

Note that versions of this table do not need to coincide with versions of data.player_versions, nor versions of data.player_report_versions. There can be a break in versions in this table without a corresponding break in versions in either of those tables and vice versa.

Name Type Description
id bigint

An arbitrary numeric ID. These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. To identify a particular report attribute use the combination of its mmolb_player_id and attribute (optionally also category).

mmolb_player_id text

The MMOLB id for the player who this report attribute belongs to. Use this to join on data.player_versions (using valid_from and valid_until accordingly) to associate players with their attributes.

category bigint

The category this report is for ("Batting", "Baserunning", etc.). References taxa.attribute_category.

attribute bigint

Which attribute this is. References taxa.attribute.

valid_from timestamp without time zone

The earliest time this version of the attribute was observed.

See the data.player_versions table description for how to use valid_from and valid_until.

valid_until timestamp without time zone or null

The earliest time a different version of the report was observed, i.e. the earliest time we know this version is no longer valid. The latest time we know this version is valid is not exposed because nobody's requested it yet. If you have a use for it, contact us in the MMOLB discord or on Github.

See the data.player_versions table description for how to use valid_from and valid_until.

null if this version is still valid.

stars integer

The number of stars the player has in this attribute.

info

Holds information that powers the MMOLB front-end (the website you are probably reading this text on). You likely won't need to use this schema, aside from the info.raw_events table, which lets you see the game log message that corresponds to each row in data.events. The info.raw_events table would be in the data schema if not for a technological limitation.

info.ingests

Record of each time ingest ran.

Name Type Description
id bigint

An arbitrary numeric ID. This table's primary key.

started_at timestamp without time zone

When this ingest was started, in UTC.

finished_at timestamp without time zone or null

When this ingest finished, in UTC.

null if the ingest is ongoing or was aborted.

aborted_at timestamp without time zone or null

When this ingest was aborted, in UTC.

null if the ingest is ongoing, finished successfully, or was aborted in a way that prevented us from updating the database.

start_next_ingest_at_page text or null

The next_page token that came with the latest Chronicler page that we've fully ingested. This is internal bookkeeping for the ingest task and is unlikely to be useful to anyone but MMOLDB itself.

null if the first page is not yet fully ingested.

info.raw_events

Game event messages directly from the API, without MMOLDB processing. Useful for understanding confusing game events and diagnosing issues with your query, MMOLDB, or MMOLB.

Every event in data.events correlates with at least one of these by game_id and game_event_index. Some events correlate with a second by game_id and fair_ball_event_index.

A common way to use this table is to add the following to an existing query that uses data.events:

left join info.raw_events on raw_events.game_id=events.game_id
    and raw_events.game_event_index=events.game_event_index

Or, if you're looking at fair_ball_type and/or fair_ball_direction:

left join info.raw_events on raw_events.game_id=events.game_id
    and raw_events.game_event_index=events.fair_ball_event_index
Name Type Description
id bigint

An arbitrary numeric ID. This table's primary key.

game_id bigint

The game this raw event belongs to. References data.games.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use mmolb_game_id, which you access by joining data.games on this column.

game_event_index integer

The index of this game event (starting at 0) in its game's event log. This is used in combination with mmolb_game_id to identify a game event.

event_text text

The full text of this event, as it appeared on mmolb.com and in the API. Almost all the information in data.events was parsed from this message.

info.event_ingest_log

Informational messages MMOLDB generated while it was processing game events. This table powers the game view on the MMOLDB website. It's unlikely to be useful in queries.

Name Type Description
id bigint

An arbitrary numeric ID. This table's primary key.

game_id bigint

The game this event log belongs to. References data.games.

These IDs are not stable. You should not store these ids between queries, nor hard-code them into queries. Instead use mmolb_game_id, which you access by joining data.games on this column.

game_event_index integer or null

The index of the game event this log belogngs to (starting at 0) in its game's event log. This is used in combination with mmolb_game_id to identify a game event.

null indicates that this log item belongs to its game as a whole, and not to a specific event.

log_index integer

The index of this log item within its game event. This is used as a sort key.

log_level integer

The severity level of this log entry.

0 = Critical error 1 = Error 2 = Warning 3 = Info 4 = Debug 5 = Trace

Logs of level 2 and below are considered "issues", and they cause their game to be displayed on the "games with issues" page.

log_text text

The actual text of the log item.

info.ingest_timings

Exists entirely for debugging performance issues with ingest.

The columns are not documented, but if you choose to use them anyway be warned that some of durations overlap. db_insert_duration overlaps all the other db_insert_*_durations, db_fetch_for_check_duration overlaps all the other db_fetch_for_check_*_durations, and save_duration overlaps everything but fetch_duration.