A WordPress table that shows MySQL data is a SELECT query whose returned rows get printed as an HTML table. You can write the $wpdb code and shortcode yourself, or hand the query straight to a table plugin such as wpDataTables.
The table stores a query, not the rows. The data stays in MySQL.
As of October 2026, WordPress.org recommends MySQL 8.0 or greater (or MariaDB 10.11 or greater), and a table can query the site’s own database directly. Data on another server needs the paid wpDataTables plans, which add separate connections to MySQL, MS SQL and PostgreSQL.
Table of Contents
Ways to show MySQL data in a WordPress table
Custom $wpdb code and a shortcode, a single-purpose database table plugin, and wpDataTables with an SQL query as the data source all get you there.
Where the data lives and how many rows it holds matter most. So does whoever has to maintain the table afterward.
| Method | Setup | Large tables | Upkeep |
|---|---|---|---|
| Custom $wpdb code | You write query, loop and markup | You build pagination and filtering | Your code, your security fixes |
| Lightweight plugin | Install, add a shortcode | Pagination and a row limit | Check compatibility first |
| wpDataTables | Wizard, paste a SQL query | Server-side processing | Vendor updates and documentation |
Custom code with $wpdb and a shortcode
Smallest footprint. Most effort.
The job is to query with the WPDB class, loop over the rows, print the table markup and register the whole thing as a shortcode. This walkthrough on fetching data from a database on a WordPress page covers the WPDB class, WP_Query and shortcodes in one place.
You get full control over markup and CSS, and there’s no extra plugin to update. It reads any table in the same database too.
The price is that sorting, search and pagination are yours to build. Every query also becomes a security responsibility you carry.
Anything that comes from a visitor or a shortcode attribute should pass through $wpdb->prepare(). It takes %s, %d and %f placeholders, and since WordPress 6.2.0 it also takes %i for table and column names (WordPress developer reference).
A lightweight database table plugin
DbTable to DataTable sits at version 0.1, has 20+ active installations and is tested only up to WordPress 4.4.34, according to its WordPress.org plugin page in October 2026. The directory also warns that it has not been tested with the latest 3 major WordPress releases and may no longer be maintained, and the page shows its last update about 11 years ago.
The feature list is short.
- A [dbtable from=TableName] shortcode
- Parameters including select, except, cssClass, comments, pagination, width, language and limit (25 rows by default)
- A built-in check that blocks WordPress core tables, so it only shows custom tables
That’s enough for a small custom table that nobody edits. I’d skip it for anything customer-facing, because an untested v0.1 plugin is a compatibility risk you will own.
wpDataTables with a SQL query
wpDataTables turns a SQL query into a sortable, filterable front-end table. It can also edit MySQL tables from the front end and build queries with a visual constructor. MySQL query tables are a paid feature, available from the Standard plan up.
The documentation recommends a MySQL-based table once a data set grows past a few hundred rows (wpDataTables documentation).
Searching, sorting and pagination are handled by MySQL for big tables. There’s a query generator for people who avoid SQL, plus front-end editing and separate database connections.
The cost is some SQL work on your side, because the documentation says preparing MySQL queries for you is not included in plugin support. And one more plugin on the site.
For anything beyond a small static table, this is the sensible default.
How to choose between the three
One small table and one developer? Custom code is enough and adds no plugin.
For a single small custom table with no editing and no growth, a lightweight plugin works if you accept the maintenance gap.
Past a few hundred rows, or once you need filters, editing or a second database, use wpDataTables.
Once a custom-code project needs sorting, search, pagination and filters, you are rebuilding a table plugin by hand.
How to prepare the SQL query for the table

Start with a SELECT statement that returns exactly the rows and columns the table should show. Run it in phpMyAdmin or MySQL Workbench first, before it goes anywhere near WordPress.
The wpDataTables documentation uses SELECT * FROM dummy_employees against a 100-row sample table. To repeat it, download the SQL dump, open phpMyAdmin, pick the database wpDataTables is configured to use, open the Import tab, choose the file and click Go.
Then paste the query into the SQL tab of phpMyAdmin. If it returns the right data there, the plugin reads the same columns.
Writing the query by hand
The plugin reads queries with a PHP SQL parser, and a few rules decide what works.
| Rule | What happens |
|---|---|
| Statement types | The documentation lists 13, from SELECT to DESCRIBE |
| DROP and DELETE | Disabled for security |
| Variables (@), stored procedures, nested queries | Not supported, use a MySQL view |
| Table names | Backticks added automatically |
The view workaround is simple. Build a MySQL view that returns what you need, call it view1, and use SELECT * FROM view1 as the table query.
Joins and conditions work in the query as well. When it touches WordPress core tables such as posts or users, check the column names against the WordPress database schema first.
Building the query with the generator
No SQL? The Create a Table wizard can generate the query for you if you pick the “Generate a Query to MySQL Database” option, or the sibling option for the WordPress database.
- Pick one or several tables from the list of all MySQL tables
- Move the columns you want into the selected columns list
- In the last step, review and edit the query, which comes with a preview of the resulting rows
- Click Create the table
Treat the output as a draft. The documentation itself says the generator is not an ultimate generator and only tries to guess what you want.
How to create a MySQL table in WordPress with wpDataTables
In short, you choose Create a Table, link it to an existing data source, paste the SQL query and save.
- Go to wpDataTables, Create a Table and choose “Create a table linked to an existing data source”.
- Enter a Table Title so you can find the table later.
- Select SQL query in the Input data source type box.
- Paste the tested query into the SQL editor.
- Click Save Changes. The plugin reads the table structure and sets up the column metadata.
- Insert the table into a post or page.
Optional column and display settings
On the Display tab, tick Limit table width and Word wrap, then save.
To hide a column, open Column settings, or the Column List dialog above the table, and untick “Visible on front-end” for the ID column.
The Column List dialog also renames and reorders columns, and it has the visibility toggles. Click Apply to store the changes and check them in the live preview.
Placing the table on a page
In the classic editor, put the cursor where the table belongs, click the “Insert a wpDataTable” button and pick the MySQL-based table.
Other routes work too.
- Copy the shortcode from the table edit page and paste it manually
- Gutenberg, Elementor or WPBakery, each with its own shortcode section in the documentation
When does a large MySQL table need server-side processing?
Turn it on once a table passes roughly 2000 to 3000 rows. A separate server-side processing page says it switches on automatically for SQL query tables above 2,000 rows and then cannot be disabled, so expect it from about that size.
Without it, the plugin reads the whole data set and prints it onto the page. Page generation on the server and initialization in the browser both slow down as rows grow, and a PHP memory or timeout limit can break the page outright.
With it on, MySQL runs all filtering, sorting and pagination, and a background AJAX request fetches only a small number of rows at a time.
| Row count | Setup | What decides it |
|---|---|---|
| Under a few hundred | Other sources are fine | Convenience |
| A few hundred to about 2000 | MySQL-based table, standard loading | Documentation recommendation |
| 2000 to 3000 and up | Server-side processing on | Page load, PHP limits, automatic switch-on above 2,000 rows |
| Hundreds of thousands | Server-side plus indexes | Query and table design |
The documentation’s test set has 2,560,000 rows. Without server-side processing it would typically need 5 to 15 minutes to load, if it loaded at all (wpDataTables documentation).
To enable it, choose the SQL query data source, paste the query, tick the Server-side processing checkbox and click Save Changes.
What still slows a server-side table
Server-side processing moves the work to MySQL. It does not remove it.
If a query returns hundreds of thousands or millions of rows from tables without proper indexes and data types, the table stays slow. The documentation puts that delay on the MySQL engine, not the plugin.
- Index the columns you filter and sort on
- Use proper data types
- Remember it works only for tables saved in the database
How to connect a separate or remote MySQL database
Switch on the separate database connection setting in wpDataTables, fill in the connection details, and click Test DB settings before you build any table on it. External connections to MySQL, MS SQL and PostgreSQL are part of the paid plans, from Standard up.
Each table can then use its own connection, so one site can show data from several databases and servers.
What each connection field does
- Connection Name is the label you will see in the table wizard
- The Vendor is MySQL, MSSQL or PostgreSQL
- Name holds the database you want to query
- For Host, use a domain or an IP address
- Port only matters when yours is non-standard
- User and password are the login for that database
Tick Default Connection on one of them and the wizard preselects it for new tables.
Key figures for separate connections
- The default ports are 3306 for MySQL, 1433 for MSSQL and 5432 for PostgreSQL
- MS SQL needs a minimum of SQL Server 2012
- More than one connection arrived with plugin version 2.3
- Version 2.7 added the SQL driver select-box, with dblib preselected for MSSQL
All four values come from the wpDataTables documentation on multiple database connections (page last modified December 2024).
Testing the connection
The current documentation calls the check Test DB settings. A 2018 support article on remote MySQL failures calls it Test MySQL settings, so expect either label.
Once the test passes, the table wizard lets you pick which database a new table is created on.
How to make a MySQL table dynamic and relational
Placeholders change the query result per user, page or shortcode. Foreign key relations swap stored IDs for readable values from another wpDataTable.
Placeholders in the SQL query
The documentation lists 19 placeholder names, from %CURRENT_USER_ID% and %CURRENT_DATE% to the nine variables %VAR1% to %VAR9%, although its intro text still says 13 (wpDataTables documentation).
The documented pattern is a query like this one.
SELECT * FROM my_table WHERE my_field > %VAR1% AND my_field < %VAR2% AND user_id = %CURRENT_USER_ID%
- Variables default to empty strings, so a query that filters on them can return no rows until you set values (0 and 100, for example)
- You can override them per page in the shortcode:
wdt_ID Company name Sector Yearly revenue Client since 1 Black Turtle Architecture 10,000,000.00 29/05/2011 3 Theodore Consulting Technology 11,000,000.00 17/05/2004 4 White Wing Industries Business Consulting 1,250,000.00 18/05/2006 5 Twinkles Inc. IT 8,000,000.00 01/05/2006 6 The Large Company Construction 7,000,000.00 01/06/2015 8 Jailbreak Machinery IT 27,500,000.00 01/06/2001 9 Infinite Scroll Maniacs IT 20,000,000.00 28/05/1996 10 Thebest Construction Agency Construction 90,000,000.00 01/05/1996 11 Allen Planning IT 0.88 15/12/2017 12 Hamburg Calling Technology 2,000,000.00 26/08/2004 wdt_ID Company name Sector Yearly revenue Client since ∑ = 176,750,000.88 Avg = 17,675,000.09 Min = 0.88 Max = 90,000,000.00 - Placeholders are case-sensitive, so write %VAR1% in the query and var1= in the shortcode
- String values need single quotes around %VAR1% in the query
- %CURRENT_USER_ID% takes its value only while the table is generated, then the actual visitor’s ID is fetched
One table, many outputs.
Foreign key relations
Foreign keys make an orders table readable. The documentation’s Northwind sample has an orders table that stores employee_id, customer_id and shipper_id, and displayed raw those columns are bare numbers.
Build the source tables first, each as an SQL query table (SELECT * FROM employees, and so on). Then configure the relation on the orders column.
In the column settings, open the Data tab and change the column type from Integer to String. For possible values, choose “Use values from another wpDataTable (foreign key)” and open Configure relation.
The relation modal asks for the source wpDataTable, the Display value (last_name, say) and the Store value, usually id.
Sorting then runs on the displayed remote values, not the stored IDs. Filters take their options from the remote table too, and Exact filtering is always on for that column.
Joining tables across separate databases
Databases on the same server are the easy case.
- Create a MySQL view from a stored query that uses database.table.field syntax
- Build the wpDataTable on that view
Physically separate servers need a workaround.
- Schedule a PHP script (once an hour, for example) that copies the remote data into one database
- Build the wpDataTable on the combined table
The wpDataTables support FAQ says direct JOINs across separate connections are not possible. Foreign key relations are the third route, because they can show data from one table in another even when the tables use different databases.
How to control columns, sorting, filtering and editing on the front end
The Display tab handles responsiveness, column settings handle visibility and filters, and the Editing tab decides who can change data. Each column’s editor input type matters as well.
Responsive columns
Turn on the Responsiveness switch in the Display tab first. Then use Hide on tablets and Hide on mobiles in each column’s settings.
Hidden cells collapse into an expandable block under the first visible column, so no data is lost on small screens. Leave at least one column visible on both device types.
Since version 4.4 the expand action can be the +/- button, a row click or a cell click. A longer guide on making a table responsive helps when the defaults are not enough.
Sorting and filtering per column
- Select box and Checkbox filters work for short lists of values
- Date Range suits date columns
- Number Range fits amounts and totals
- Foreign key columns pull their filter options from the remote table
Pick the filter from the data, not from habit. A select box suits a short list of companies, and a text filter suits thousands of customers.
Front-end editing
- Open the Editing tab in the table settings and click Allow editing.
- Check the “MySQL table name for editing” and “ID column for editing” fields. The plugin tries to detect them from the query, and you fill them in when it cannot.
- Choose an editor input type for every column users may change.
- Set Editor roles and save the changes.
Set the ID column up as an auto-increment integer on the MySQL side. The documentation warns that a non-unique ID column makes one save edit multiple entries, so keep the ID column hidden from visitors too.
One security note. Leaving Editor roles unchecked allows editing for everyone, according to the documentation, and that includes visitors who are not logged in. Restrict the roles.
To show each user only their own rows, the documentation has a separate guide on letting users see and edit only their own data.
Why is the MySQL table not displaying, and where does this approach stop working?
Usually it’s a blocked remote connection, a JavaScript conflict, unsupported SQL or a placeholder default that returns no rows.
| Symptom | Likely cause | Fix |
|---|---|---|
| Separate connection fails | Remote MySQL server refuses the site host | Check credentials, allow the host IP, rerun the test |
| Table never renders, or loses sorting and pagination | Another plugin or theme breaks the JavaScript | Deactivate other plugins one by one |
| No data at creation | Placeholders default to empty strings | Set values for %VAR1% and the rest |
| Query not accepted | Variables, stored procedures or nested queries | Build a MySQL view and query that |
The connection and JavaScript causes come from the wpDataTables support FAQ. The query rules and the empty-string default come from the documentation, and the empty result is the likely effect of that default.
Where editing overrides the row-count rule
Front-end editing needs server-side processing, and the plugin turns it on automatically if you forget (wpDataTables documentation).
So a small editable table still loads through server-side processing, even though the row-count guidance earlier in this article says small tables do not need it. Row count decides whether you need the setting. Editing decides whether you can avoid it.
The editing limits are worth knowing before you plan a table.
- Only one MySQL table can be edited at a time, so queries that join several tables cannot be editable
- Only MySQL tables, or tables made with the manual and import options, can be edited
- A MySQL view edits only one of its underlying tables, and only if you pass the proper ID
The reason is mechanical. The plugin generates UPDATE and INSERT statements automatically, and it has no way to update several tables in one save.
FAQ About MySQL Data in a WordPress Table
Is wpDataTables Lite enough to show MySQL data?
No. The free Lite version does not create MySQL query tables, and wpDataTables lists them as available from the Standard plan up.
Lite covers simple tables and file-linked tables built from CSV and Excel.
Can I show WordPress posts or custom fields in a table?
Yes. The “Generate a Query to WordPress Database” option lets you pick post types, custom (meta) fields and taxonomies, then builds the query and a preview. It is part of the paid query builder, not the free Lite version.
Add conditions such as page.ID > 5, or group by post ID so a post with several tags is not repeated once per tag.
How do I get a CSV or Excel file into a MySQL table first?
Choose “Create a table by importing data from data source” in the wizard and upload a CSV, XLS or XLSX file from the Media Library.
The rows are copied into the database as an editable table. They stop syncing with the file, so later changes to the file never reach the table.
Can visitors export the table to PDF, CSV or Excel?
Yes, through Table Tools, which adds copy, PDF, CSV, Excel and print buttons plus a column visibility toggle.
With server-side processing on, an export holds only the visible rows, so pick “Show All” in the entries box first. Hidden columns and conditional formatting never reach the file.
Does it work with MariaDB instead of MySQL?
Yes. wpDataTables says its MySQL query tables connect to the WordPress database or any reachable MySQL or MariaDB database.
WordPress.org also lists MariaDB as a supported alternative to MySQL for running the site itself.
Keeping MySQL Data Current in a WordPress Table
A WordPress table built on a SQL query reads from MySQL, so it should follow the data as it changes and needs no refresh job. wpDataTables does not state this outright in its caching documentation, so confirm it by editing a test row.
What keeps it that way is a simple order of operations.
- Keep the source of truth in MySQL, not in a file
- Build the table on a query, not an import
- Leave the cache option to file and feed sources
The order pushes staleness to the last step. The cache option is documented for CSV, Excel, XML, JSON, nested JSON, Google Sheets and PHP array sources, and the same page recommends keeping those files near 5,000 rows.
The trade-off is that a live query has no cache to hide behind, so query speed sets page speed. This holds as of October 2026, and it changes only if the caching documentation (last modified August 2026) extends to SQL query tables.



