WooCommerce SQL Audit Queries for Variable Products, Variations, Stock, and SKUs
WooCommerce variable products can become difficult to manage when a store has hundreds or thousands of products.
A variable product is a parent product that contains one or more variations. For example, a T-shirt may be sold in different sizes and colors. The T-shirt is the parent product, and each size or color option is a variation.
When variations are missing, SKUs are empty, or stock settings are incorrect, customers may see products as out of stock or inventory systems may fail to sync correctly.
This tutorial shows several useful SQL queries you can run against a WordPress/WooCommerce database to find common variable product issues.
Important Safety Notes Before Running SQL
Before running any SQL query on a live WooCommerce store, follow these steps:
- Take a full database backup.
- Run the query on a staging site first.
- Use read-only queries when auditing data.
- Do not run
UPDATE,DELETE, orINSERTqueries unless you fully understand the result. - Replace
wp_with your actual WordPress database table prefix if your site uses a custom prefix.
These queries are designed for auditing. They only read data and return results.
What These Queries Help You Find
The SQL queries in this guide help identify:
- Variable products with zero or one variation
- Variable parent products where parent-level stock management is enabled
- Variable products where at least one variation has no SKU
- Variable products where at least one variation does not have stock management enabled
These checks are useful for store owners, developers, and support teams who need to debug product availability, stock sync issues, or marketplace integration problems.
Understanding the Main WooCommerce Tables
WooCommerce stores product data using standard WordPress tables.
wp_posts
The wp_posts table stores products and variations.
In WooCommerce:
- Parent products usually have
post_type = 'product' - Variations usually have
post_type = 'product_variation' - A variation is connected to its parent using
post_parent
Example:
SELECT ID, post_title, post_type, post_parent
FROM wp_posts
WHERE post_type IN ('product', 'product_variation');
wp_postmeta
The wp_postmeta table stores extra product information.
Important WooCommerce meta keys include:
_sku_manage_stock_stock_stock_status_price_regular_price_sale_price
wp_terms, wp_term_taxonomy, and wp_term_relationships
WooCommerce product types are stored using WordPress taxonomy tables.
A variable product is connected to the product_type taxonomy with the term slug variable.
That is why the queries join these tables:
wp_term_relationships
wp_term_taxonomy
wp_terms
wp_options
The wp_options table stores site-level settings.
The queries use this table to get the site URL and build a clickable admin edit link.
Example admin edit link format:
https://example.com/wp-admin/post.php?post=123&action=edit
Query 1: Find Variable Products With Zero or One Variation
A variable product usually needs more than one variation.
If a variable product has zero variations, it may be broken or incomplete.
If it has only one variation, it may not need to be a variable product at all, or it may be missing variations.
This query returns variable parent products that have zero or one active variation.
SELECT
p.ID AS product_id,
p.post_title AS product_name,
COUNT(v.ID) AS variation_count,
CONCAT(
(SELECT option_value FROM wp_options WHERE option_name = 'siteurl'),
'/wp-admin/post.php?post=',
p.ID,
'&action=edit'
) AS edit_link
FROM wp_posts p
LEFT JOIN wp_posts v
ON v.post_parent = p.ID
AND v.post_type = 'product_variation'
AND v.post_status NOT IN ('trash', 'auto-draft')
WHERE p.post_type = 'product'
AND p.post_status NOT IN ('trash', 'auto-draft')
AND EXISTS (
SELECT 1
FROM wp_term_relationships tr
INNER JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id
INNER JOIN wp_terms t
ON t.term_id = tt.term_id
WHERE tr.object_id = p.ID
AND tt.taxonomy = 'product_type'
AND t.slug = 'variable'
)
GROUP BY
p.ID,
p.post_title
HAVING COUNT(v.ID) <= 1
ORDER BY variation_count ASC, p.ID DESC;
How This Query Works
The query starts from parent products in wp_posts.
FROM wp_posts p
It then looks for variations connected to each parent product:
LEFT JOIN wp_posts v
ON v.post_parent = p.ID
AND v.post_type = 'product_variation'
A LEFT JOIN is used because we still want to find parent products that have zero variations.
The query excludes trashed and auto-draft variations:
AND v.post_status NOT IN ('trash', 'auto-draft')
The EXISTS section confirms that the product is a variable product:
AND t.slug = 'variable'
Finally, this line returns only products with zero or one variation:
HAVING COUNT(v.ID) <= 1
Why This Matters
Variable products with too few variations may cause:
- Confusing product pages
- Broken product selection options
- Incomplete catalog data
- Sync issues with marketplaces or inventory tools
Query 2: Find Variable Parent Products With Parent-Level Stock Management Enabled
In many WooCommerce setups, stock should be managed at the variation level.
For example, a T-shirt may have separate stock for:
- Small / Red
- Medium / Red
- Large / Blue
If stock is managed at the parent product level, it may override or interfere with variation-level stock behavior.
This can cause products to appear out of stock on the frontend even when variations have stock available.
This query returns variable parent products where _manage_stock = 'yes' is set at the parent level.
SELECT
p.ID AS product_id,
p.post_title AS product_name,
ms.meta_value AS manage_stock,
CONCAT(
siteurl.option_value,
'/wp-admin/post.php?post=',
p.ID,
'&action=edit'
) AS edit_link
FROM wp_posts p
INNER JOIN wp_postmeta ms
ON ms.post_id = p.ID
AND ms.meta_key = '_manage_stock'
AND ms.meta_value = 'yes'
INNER JOIN wp_term_relationships tr
ON tr.object_id = p.ID
INNER JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'product_type'
INNER JOIN wp_terms t
ON t.term_id = tt.term_id
AND t.slug = 'variable'
CROSS JOIN wp_options siteurl
WHERE p.post_type = 'product'
AND p.post_status NOT IN ('trash', 'auto-draft')
AND siteurl.option_name = 'siteurl'
ORDER BY p.ID DESC;
How This Query Works
The query checks the parent product metadata:
INNER JOIN wp_postmeta ms
ON ms.post_id = p.ID
AND ms.meta_key = '_manage_stock'
AND ms.meta_value = 'yes'
This means it only returns products where stock management is enabled on the parent product.
The query then confirms that the product is a variable product:
AND t.slug = 'variable'
The CROSS JOIN is used to get the site URL from wp_options:
CROSS JOIN wp_options siteurl
This allows the query to build an admin edit link.
Why This Matters
Parent-level stock management on variable products can be risky because each variation may have its own stock quantity.
If the parent product has stock management enabled, WooCommerce may treat the parent stock status as more important than expected.
This can cause problems such as:
- Product showing as out of stock
- Variation stock being ignored
- Incorrect availability on the frontend
- Marketplace sync mismatches
- Confusing inventory reports
Query 3: Find Variable Products Where at Least One Variation Has No SKU
SKUs are important when syncing WooCommerce with inventory systems, ERPs, POS systems, or marketplaces.
If a variation has no SKU, external systems may not know which item to update.
This query returns variable parent products where at least one variation has a missing or empty SKU.
SELECT DISTINCT
p.ID AS product_id,
p.post_title AS product_name,
CONCAT(
siteurl.option_value,
'/wp-admin/post.php?post=',
p.ID,
'&action=edit'
) AS edit_link
FROM wp_posts p
INNER JOIN wp_posts v
ON v.post_parent = p.ID
AND v.post_type = 'product_variation'
AND v.post_status NOT IN ('trash', 'auto-draft')
LEFT JOIN wp_postmeta sku
ON sku.post_id = v.ID
AND sku.meta_key = '_sku'
INNER JOIN wp_term_relationships tr
ON tr.object_id = p.ID
INNER JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'product_type'
INNER JOIN wp_terms t
ON t.term_id = tt.term_id
AND t.slug = 'variable'
CROSS JOIN wp_options siteurl
WHERE p.post_type = 'product'
AND p.post_status NOT IN ('trash', 'auto-draft')
AND siteurl.option_name = 'siteurl'
AND (
sku.meta_id IS NULL
OR TRIM(sku.meta_value) = ''
)
ORDER BY p.ID DESC;
How This Query Works
The query joins parent products to their variations:
INNER JOIN wp_posts v
ON v.post_parent = p.ID
AND v.post_type = 'product_variation'
Then it checks for the variation SKU:
LEFT JOIN wp_postmeta sku
ON sku.post_id = v.ID
AND sku.meta_key = '_sku'
A LEFT JOIN is used because some variations may not have a _sku row at all.
This condition finds missing or empty SKUs:
AND (
sku.meta_id IS NULL
OR TRIM(sku.meta_value) = ''
)
The DISTINCT keyword is used because one parent product may have multiple variations with missing SKUs.
Without DISTINCT, the same parent product could appear more than once.
Why This Matters
Missing variation SKUs can cause serious sync problems.
For example, your inventory tool may try to update stock for a variation but cannot match it correctly.
This may lead to:
- Stock not updating
- Price not syncing
- Marketplace listings going stale
- Duplicate products in external systems
- Incorrect fulfillment data
Query 4: Find Variable Products Where at Least One Variation Does Not Manage Stock
WooCommerce can manage stock at the product level or variation level.
For variable products, many stores prefer managing stock at the variation level.
For example:
- Size Small has 5 units
- Size Medium has 3 units
- Size Large has 0 units
If a variation does not have stock management enabled, WooCommerce may not use its stock quantity as expected.
This query returns variable parent products where at least one variation does not have _manage_stock = 'yes'.
SELECT DISTINCT
p.ID AS product_id,
p.post_title AS product_name,
CONCAT(
siteurl.option_value,
'/wp-admin/post.php?post=',
p.ID,
'&action=edit'
) AS edit_link
FROM wp_posts p
INNER JOIN wp_posts v
ON v.post_parent = p.ID
AND v.post_type = 'product_variation'
AND v.post_status NOT IN ('trash', 'auto-draft')
LEFT JOIN wp_postmeta vms
ON vms.post_id = v.ID
AND vms.meta_key = '_manage_stock'
INNER JOIN wp_term_relationships tr
ON tr.object_id = p.ID
INNER JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id
AND tt.taxonomy = 'product_type'
INNER JOIN wp_terms t
ON t.term_id = tt.term_id
AND t.slug = 'variable'
CROSS JOIN wp_options siteurl
WHERE p.post_type = 'product'
AND p.post_status NOT IN ('trash', 'auto-draft')
AND siteurl.option_name = 'siteurl'
AND (
vms.meta_id IS NULL
OR vms.meta_value <> 'yes'
)
ORDER BY p.ID DESC;
How This Query Works
The query starts with variable parent products and joins their variations.
It then checks the _manage_stock setting for each variation:
LEFT JOIN wp_postmeta vms
ON vms.post_id = v.ID
AND vms.meta_key = '_manage_stock'
This condition finds variations where stock management is missing or not enabled:
AND (
vms.meta_id IS NULL
OR vms.meta_value <> 'yes'
)
This means the query catches both cases:
- The variation has no
_manage_stockmeta row. - The variation has
_manage_stock, but it is not set toyes.
Why This Matters
If variation-level stock management is not enabled, WooCommerce may ignore the variation stock quantity.
This can cause problems such as:
- Incorrect stock display
- Products appearing available when they are not
- Products appearing unavailable when they should be available
- Stock sync tools not updating correctly
- Marketplace overselling
Making the Queries Work With a Custom WordPress Table Prefix
Many WordPress sites do not use the default wp_ table prefix.
For example, your tables may be named:
abc_posts
abc_postmeta
abc_options
If your site uses a custom prefix, update every table name in the query.
For example, change this:
FROM wp_posts p
To this:
FROM abc_posts p
You must update all table names consistently.
How to Run These Queries
You can run these queries using tools such as:
- phpMyAdmin
- Adminer
- MySQL Workbench
- TablePlus
- Sequel Ace
- A hosting control panel database tool
- WP-CLI with database access
Example Using WP-CLI
If you have SSH access and WP-CLI installed, you can run a query like this:
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_type = 'product' LIMIT 10;"
For longer queries, it is usually better to save the SQL in a file:
wp db query < audit-variable-products.sql
Understanding the Admin Edit Link
Each query builds an edit link like this:
CONCAT(
siteurl.option_value,
'/wp-admin/post.php?post=',
p.ID,
'&action=edit'
) AS edit_link
This creates a direct link to edit the product in the WordPress admin area.
Example result:
https://example.com/wp-admin/post.php?post=123&action=edit
This is useful because you can click the link and fix the product directly.
Example Result
A query result may look like this:
product_id | product_name | variation_count | edit_link
-----------|-----------------------|-----------------|-----------------------------------------------
1542 | Blue Hoodie | 0 | https://example.com/wp-admin/post.php?post=1542&action=edit
1391 | Cotton T-Shirt | 1 | https://example.com/wp-admin/post.php?post=1391&action=edit
1178 | Leather Wallet | 1 | https://example.com/wp-admin/post.php?post=1178&action=edit
From here, you can open each edit link and review the product setup.
Suggested Audit Workflow
A practical workflow is to run the checks in this order:
- Find variable products with zero or one variation.
- Check parent-level stock management.
- Find variations with missing SKUs.
- Find variations where stock management is not enabled.
- Fix the products in WooCommerce admin.
- Clear product caches if your site uses caching.
- Re-run the queries to confirm the issues are fixed.
- Test the product on the frontend.
This helps you clean up product data in a safe and organized way.
Common Problems These Queries Can Help Diagnose
Product Shows Out of Stock Even Though Variations Have Stock
This may happen when parent-level stock management is enabled on a variable product.
Run this query:
Query 2: Find Variable Parent Products With Parent-Level Stock Management Enabled
Then review the parent product stock settings in WooCommerce.
Product Has Missing Options on the Frontend
This may happen when a variable product has no variations or only one variation.
Run this query:
Query 1: Find Variable Products With Zero or One Variation
Then check whether the product should really be variable.
Inventory Sync Is Not Updating Some Variations
This may happen when variations do not have SKUs.
Run this query:
Query 3: Find Variable Products Where at Least One Variation Has No SKU
Then add the missing variation SKUs.
Stock Quantity Is Ignored
This may happen when stock management is not enabled at the variation level.
Run this query:
Query 4: Find Variable Products Where at Least One Variation Does Not Manage Stock
Then check the variation stock settings.
Key Considerations
When using these SQL queries, keep these points in mind:
- Always back up the database before working with SQL.
- These queries assume the default table prefix is
wp_. - Some stores intentionally manage stock at the parent level.
- Some stores intentionally leave SKUs empty.
- Some plugins may customize WooCommerce stock behavior.
- Marketplace or inventory sync tools may require unique SKUs for every variation.
- Product lookup tables and caches may need to be refreshed after changes.
- SQL results should be reviewed carefully before making bulk changes.
Not every result is automatically an error. These queries identify products that need review.
Recommendations
For most WooCommerce stores with variable products, these are good general practices:
- Give every variation a unique SKU.
- Manage stock at the variation level when each variation has separate inventory.
- Avoid enabling parent-level stock management unless there is a clear reason.
- Review variable products that have zero or one variation.
- Use admin edit links to fix products manually first.
- Test product pages after making stock or variation changes.
- Document your inventory sync rules before making bulk edits.
- Re-run these audit queries regularly.
If you use an external inventory system, make sure WooCommerce variation SKUs match the identifiers used by that system.
Final Thoughts
WooCommerce variable products are powerful, but they depend on clean product data.
Missing variations, empty SKUs, parent-level stock settings, and disabled variation stock management can all cause frontend and sync issues.
These SQL queries give you a practical way to audit your store and quickly find products that need attention.
Start by running the queries in read-only mode, review the results carefully, and fix products from the WooCommerce admin area whenever possible.