DaisyVotes — Database Access
Building a vote page for your website, or a Discord bot that shows top voters? DaisyVotes’ tables are plain SQL and you are welcome to read them. This page is the contract: what the columns mean, and the queries that give the right answer.
Connect
Section titled “Connect”Point your site at the same database the plugin uses. For a website integration you want MySQL or MariaDB, not the default SQLite — a SQLite file on the game server is not reachable from your web host. See Storage & Data for switching.
Create a separate, read-only database user for the website. It never needs to write, and a credential that lives in a web root should not be able to.
CREATE USER 'daisyvotes_web'@'%' IDENTIFIED BY 'a-long-random-password';GRANT SELECT ON your_database.vote_players TO 'daisyvotes_web'@'%';GRANT SELECT ON your_database.vote_events TO 'daisyvotes_web'@'%';Column names are verbatim camelCase
Section titled “Column names are verbatim camelCase”This catches everyone once. The columns are named exactly as the plugin’s fields are — totalVotes, not total_votes.
vote_players — one row per player
Section titled “vote_players — one row per player”| Column | Meaning |
|---|---|
uuid | Minecraft UUID, with dashes. Primary key. |
name | Last known username. Changes when the player renames. |
totalVotes | Votes of all time. Always safe to read. |
dailyVotes weeklyVotes monthlyVotes | Votes in that period — only valid alongside the matching key column below. |
dailyKey weeklyKey monthlyKey | Which period the counter beside it counted: 2026-08-10, 2026-W33, 2026-08. |
currentStreak bestStreak | Consecutive days voted. |
votePoints lifetimeVotePoints | Shop currency: current balance, and total ever earned. |
lastVoteAt firstVoteAt | Epoch milliseconds, not seconds. |
vote_events — one row per vote
Section titled “vote_events — one row per vote”| Column | Meaning |
|---|---|
id | Random UUID. |
playerUuid playerName | Who voted. |
siteId serviceName | Which site, as configured and as the site announced itself. |
receivedAt | Epoch milliseconds. Indexed. |
Why you must filter on the period key
Section titled “Why you must filter on the period key”A player’s dailyVotes is only rolled over to zero when that player next votes. Somebody who last voted on Tuesday still carries Tuesday’s daily total on Friday. The column looks authoritative and is not.
The key column says which period the number belongs to. Match it against the current period and stale rows simply do not appear.
Leaderboard queries
Section titled “Leaderboard queries”SELECT name, dailyVotes AS votesFROM vote_playersWHERE dailyKey = ? AND dailyVotes > 0ORDER BY dailyVotes DESCLIMIT 10;Bind the key as Y-m-d, e.g. 2026-08-10.
SELECT name, weeklyVotes AS votesFROM vote_playersWHERE weeklyKey = ? AND weeklyVotes > 0ORDER BY weeklyVotes DESCLIMIT 10;Bind the ISO week as o-\WW, e.g. 2026-W33. ISO weeks start on Monday, and the ISO week-numbering year is not always the calendar year — around New Year they differ.
SELECT name, monthlyVotes AS votesFROM vote_playersWHERE monthlyKey = ? AND monthlyVotes > 0ORDER BY monthlyVotes DESCLIMIT 10;Bind the key as Y-m, e.g. 2026-08.
SELECT name, totalVotes AS votesFROM vote_playersWHERE totalVotes > 0ORDER BY totalVotes DESCLIMIT 10;No key — all-time never rolls over.
Building the keys, in the right timezone
Section titled “Building the keys, in the right timezone”The keys are written by the Minecraft server, in whatever wheel.reset-zone is set to in config.yml (server means the game host’s own zone). If your web host is in a different zone, computing “today” locally puts the site out of step with the game for part of every day.
// PHP — note 'o' (ISO week-numbering year) and 'W' (ISO week), not 'Y' and 'W'.$tz = new DateTimeZone('Europe/London'); // must match wheel.reset-zone$now = new DateTime('now', $tz);
$dailyKey = $now->format('Y-m-d'); // 2026-08-10$weeklyKey = $now->format('o') . '-W' . $now->format('W'); // 2026-W33$monthlyKey = $now->format('Y-m'); // 2026-08The alternative: count the event log
Section titled “The alternative: count the event log”If you would rather not depend on the counters at all, aggregate vote_events. It is the ground truth, the window is explicit, and it cannot be stale — at the cost of a GROUP BY instead of an indexed sort.
SELECT playerName, COUNT(*) AS votesFROM vote_eventsWHERE receivedAt >= ? -- window start, epoch MILLIsecondsGROUP BY playerUuid, playerNameORDER BY votes DESCLIMIT 10;Use this when you need a window the plugin does not track — “last 7 days”, “this season”, per-site totals. Use the counters for the standard daily/weekly/monthly boards; that is what they are indexed for.
Notes that save a support ticket
Section titled “Notes that save a support ticket”- Cache the result. A leaderboard does not need to be live to the second. A minute or two of caching removes essentially all load from the game server’s database.
- UUIDs have dashes. Some avatar and profile APIs want them stripped.
- A UUID that resolves to no Minecraft account is probably from before 2.6.0: a vote arriving while the player was offline used to get a UUID derived from their name. Those rows are real votes under the wrong identity. See Migrating.
- Never write to these tables while the server is running. The plugin caches player rows in memory and will overwrite you.
vote_eventsgrows forever. Nothing prunes it. It is indexed onreceivedAt, so windowed queries stay fast, but do notSELECT *from it without aWHERE.totalVotesandCOUNT(vote_events)can disagree on a server that ran a version before 2.6.0, where two simultaneous votes could be credited once while both events were still recorded. From 2.6.0 they track each other.