Why we built NetterTech Events on custom database tables
This post is for developers, agency engineers, and technically curious venue operators who want to understand why NetterTech Events uses custom database tables instead of the standard WordPress post_meta approach. If you don’t care how the engine works, only that it’s fast, the short version is: custom tables are faster, safer, and more maintainable for structured event data.
The Post Meta Problem
WordPress stores custom data in the wp_postmeta table, a key-value store with four columns: meta_id, post_id, meta_key, and meta_value. This pattern works for simple metadata (a subtitle, a color setting, a toggle). It breaks down for structured, relational data like events.
An event has a start date, end date, venue, capacity, recurrence pattern, ticket types, attendee records, and check-in states. Storing all of this in post_meta means:
Query performance degrades. Fetching “all events in March sorted by date” requires JOINing wp_postmeta on wp_posts, filtering on meta_key = ‘_event_start_date’, casting meta_value to a date, and sorting. With thousands of events, this query slows measurably.
No data typing. meta_value is a LONGTEXT column. Dates, integers, booleans, and JSON all live in the same text field. MySQL can’t use native date comparison or integer indexing on text values.
No referential integrity. There’s no foreign key between an event and its ticket types or attendees. Orphaned meta rows accumulate. Cascade deletes require application-level logic.
Indexing limitations. wp_postmeta has indexes on post_id and meta_key, but not on meta_value. You can’t efficiently query “events where capacity > 100” or “events at venue X” without full table scans on large installations.
What Custom Tables Look Like
NetterTech Events uses 20 dedicated tables (prefixed nte_). Here are the core five:
| Table | Purpose | Key Columns |
|---|---|---|
wp_nte_events | Event definitions | id, title, start_date, end_date, venue_id, status, recurrence_rule |
wp_nte_occurrences | Pre-computed occurrences | id, event_id, start_date, end_date |
wp_nte_ticket_types | Ticket definitions | id, event_id, name, price, capacity |
wp_nte_attendees | Registration records | id, event_id, occurrence_id, name, email |
wp_nte_tickets | Individual tickets | id, attendee_id, ticket_type_id, qr_code, checked_in |
Additional tables handle series, spaces, organizers, categories, tags, attendee custom fields, activity logs, reservations, waitlists, reminders, and event revisions. Each table has typed columns (DATETIME, INT, DECIMAL), proper indexes, and foreign key relationships.
The Performance Difference
Calendar Query: “Show all events in March 2026”
Post meta approach:
SELECT p.* FROM wp_posts p
INNER JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = '_event_start_date'
AND CAST(pm.meta_value AS DATETIME) BETWEEN '2026-03-01' AND '2026-03-31'
AND p.post_status = 'publish'
ORDER BY CAST(pm.meta_value AS DATETIME);
This requires a JOIN, a CAST on every row, and can’t use an index on the date value. On a site with 10,000 events and 500,000 meta rows, this query takes measurable time.
Custom table approach:
SELECT * FROM wp_nte_occurrences
WHERE start_date BETWEEN '2026-03-01' AND '2026-03-31'
ORDER BY start_date;
Native DATETIME column, indexed, no JOIN. O(log n) lookup.
Capacity Check: “How many tickets remain for occurrence X?”
Post meta approach: Multiple JOINs across posts, postmeta (for capacity), and WooCommerce order items (for sold count). Application-level aggregation.
Custom table approach:
SELECT tt.capacity - COUNT(t.id) AS remaining
FROM wp_nte_ticket_types tt
LEFT JOIN wp_nte_tickets t ON t.ticket_type_id = tt.id
WHERE tt.event_id = ?
GROUP BY tt.id;
Single query, typed columns, indexed joins.
Why Most Plugins Don’t Do This
Custom tables require more engineering work:
- Schema management. You need activation hooks to create tables, upgrade routines to migrate schemas, and uninstall hooks to clean up. WordPress doesn’t provide scaffolding for this.
- No admin UI for free. Post types get list tables, editors, and meta boxes from WordPress core. Custom tables need custom admin pages built from scratch.
- Search integration. WordPress search queries
wp_posts. Custom table data is invisible to the default search unless you build a bridge. NetterTech Events uses a shadow post type to index event data into WordPress search. - Plugin review. WordPress.org reviewers sometimes scrutinize custom table usage. The tables need to follow WordPress conventions (prefix with
$wpdb->prefix, usedbDelta()for creation).
These are real costs. For simple plugins, post_meta is the pragmatic choice. For event management, with its relational data, date queries, and capacity math, custom tables are the architecturally sound choice.
What This Means for You
For venue operators:
- Faster calendar pages, especially as your event count grows
- Reliable capacity tracking (no race conditions from meta-based counting)
- Clean data exports (structured tables, not key-value soup)
For developers:
- Direct SQL access to typed, indexed columns
- Foreign key relationships between events, occurrences, tickets, and attendees
- A schema you can inspect with
DESCRIBE wp_nte_eventsinstead of guessing meta_key names - Over 5,500 unit tests validating the data layer
For agencies:
- Predictable performance across client sites with varying event volumes
- Database-level integrity instead of application-level validation
- Easier debugging (query the table directly, not meta archaeology)
Trade-Offs
Custom tables aren’t free of downsides:
- WP-CLI integration. Standard
wp post listdoesn’t know about custom tables. We provide our own WP-CLI commands for event management. - Backup tools. Some WordPress backup plugins only back up core tables by default. Make sure your backup solution includes custom tables (most do, but verify).
- Migration complexity. Moving between plugins means mapping custom schemas. The NetterTech Events Migrator handles this for common sources (TEC, BPT, etc.).
Further Reading
- Hooks reference - actions and filters for extending NetterTech Events
- Getting started - installation and setup
- REST API reference - endpoint documentation for integrations
Install free or view plans to get started.