WooCommerce bulk edit SKU: replace, prefix, variation SKUs, duplicates
SKU changes rarely come one at a time. A supplier renames a code that sits inside hundreds of SKUs, a new sales channel needs a prefix, variations need SKUs built from the parent's SKU and their size, new products need numbers. BEAR does each of them as a WooCommerce bulk edit SKU run in the Bulk Edit tab. Below is what each behaviour does, how variables build variation SKUs, the limits of sequential numbering, and how to keep SKUs unique, because WooCommerce refuses duplicates and a duplicate stops a bulk run.
WooCommerce bulk edit SKU behaviours: append, prepend, new, replace
First choose the products: filter them in the Filters tab or tick their rows. The line Bulk editing will be applied to: above the bulk form tells you which set the run will change. Then tick the SKU row in the Bulk Edit tab, type the value, pick a behaviour and click Do Bulk Edit. For a product with the SKU AB-100:
| Behaviour | What it does | Value | Result |
|---|---|---|---|
| append | adds the text after the current SKU | -EU | AB-100-EU |
| prepend | adds the text before it | SHOP- | SHOP-AB-100 |
| new | replaces the whole SKU | X-{ID} | X-4821 |
| replace | replaces a part of the SKU with the replace to text | 100 to 200 | AB-200 |
replace is a real find and replace inside the SKU. To turn BSO-AC-PP40, BSO-LON-PP40 and the rest into ...-DD40, filter the SKU with LIKE PP40, then replace PP40 with DD40. For variations, switch on variations and click select all variations before the run, so that only the matching variation rows are ticked; the section on duplicates explains why. It changes every occurrence of the text in a SKU, so search for a part that cannot appear twice: -PP40 rather than 40. Leave replace to text empty to delete the text. Choose same case or ignore case next to it. The behaviour has no wildcards and no positions: it can remove or replace a letter, not move it elsewhere in the SKU.
On a product without a SKU, append and prepend simply write your text. A variation without its own SKU is different: the editor reads the SKU WooCommerce displays for it, which is the parent's. Appending -XL to such a variation therefore gives AB-100-XL, a handy start for variation SKUs.
Variables: {SKU}, {ID}, {PARENT_SKU} and more
The variable drop-down next to the value inserts placeholders that are filled in for each product: {TITLE}, {ID}, {SKU}, {MENU_ORDER}, {PARENT_ID}, {PARENT_TITLE}, {PARENT_SKU}, {REGULAR_PRICE} and {SALE_PRICE}. You can also type {attribute:pa_size} or {meta:your_meta_key}. On a product that is not a variation, the parent placeholders give the product's own values.
{SKU}is the current SKU, so new with{SKU}-v2does the same as append with-v2. It is useful in the middle of a value:EU-{SKU}-2026.{ID}is unique by definition. append with-{ID}makes every SKU in the run unique, which is the quickest way out of duplicates.- Placeholders work in the search text of replace too. The SKU inside product titles is removed this way, see bulk edit product titles.
Variation SKUs from the parent SKU and attributes
Filter the variable products you need, switch on variations on the tools panel, and the bulk edit applies to their variations while the parents are ignored. Then, in the SKU row with new:
{PARENT_SKU}-{attribute:pa_size}
A hoodie HOOD gets HOOD-S, HOOD-M, HOOD-XL. Replace pa_size with the slug of your attribute: global attributes start with pa_, a custom attribute of the product is written as its name in lower case, with hyphens for spaces. The value inserted is the term name as the shop shows it, capitals and spaces included: Dark Blue gives HOOD-Dark Blue. Clean it with a second run, replace a space with -, and if you want capitals, a third run with new and the variable {DO_STRING_UP} (STRING UP), which stands for the current value in upper case.
Two attributes need two runs. Only one attribute placeholder is read per value: {PARENT_SKU}-{attribute:pa_size}-{attribute:pa_color} loses both attributes. Run new with {PARENT_SKU}-{attribute:pa_size} first, then append with -{attribute:pa_color}.
If you only need unique variation SKUs, {PARENT_SKU}-{ID} does it in one run; the FAQ on SKUs for variations shows it with binded editing as well. To give new SKUs to only some variations, for example one size, see filter variations by attribute.
Sequential SKU numbers and their limits
A value ending in digits and a plus sign, such as 10300+, 10300+5 or ABC-10300+, is a counter: the syntax is described in how to generate sequential SKUs. In 2.2.4 the counter behaves as follows:
- It is raised before each product. In the Bulk Edit tab,
10300+gives 10301, 10302 and so on; enter10299+to start at 10300. With binded editing the cell you edit keeps the start number and the other ticked rows continue from it. - It restarts with every batch. The editor sends products to the server in batches of Number of posts processed at a time (Settings tab, 30 by default, at most 100), and each batch counts from the start again. The first product of the second batch gets a number that is already taken, and the run stops on that duplicate. Keep each run within one batch: at most that many ticked rows, or that many filtered products (in variations mode each product takes all its variations into its batch). Start the next run where the last one ended.
- It counts across the whole run. It does not restart per product: appending
-0+to the variations ofHOODthat have no SKU of their own givesHOOD-1,HOOD-2,HOOD-3, and the next product continues with-4. Filtered products are numbered in product ID order, ticked rows in the order you ticked them.
If you need a literal SKU that ends with digits and a plus sign, switch the counter off:
// BEAR: treat "10300+" in the SKU row as plain text, not as a counter.
// Child theme functions.php or a code snippets plugin.
add_filter( 'woobe_sku_auto_increment', '__return_false' );
Prevent duplicate SKUs in WooCommerce
WooCommerce requires every SKU to be unique among products and variations that are not in the trash, and refuses a duplicate with Invalid or duplicated SKU. In 2.2.4 BEAR writes the new SKU to the database before WooCommerce checks it. A duplicate therefore stays in the product's SKU field, the rest of the save is skipped, and the request fails: a bulk run stops with an error, and the remaining products of that batch are not changed. Typical causes are one fixed value with new for many products, a counter over more than one batch, and replace over variations without their own SKU, which receive the parent's SKU when the searched text is not in it: the editor starts from the SKU WooCommerce displays for them, and the parent already has that one.
Switch on BEAR's own SKU control to stop duplicates before they are written:
/*
* BEAR: refuse a SKU that another product or variation already has.
* Child theme functions.php or a code snippets plugin.
*/
add_filter( 'woobe_init_sku_control', '__return_true' );
With the control on, BEAR looks the new SKU up among all products and variations, trashed ones included. If another product has it, nothing is written: a cell shows Error! SKU already exists, and a bulk run skips that product and goes on. If a SKU seems free but is refused, look in the trash. In 2.2.4 the message also appears when a cell is saved with the SKU it already has; nothing changes then.
To repair a duplicate left by an earlier run, search the SKU with EXACT: both products are listed. Give the wrong one a new SKU in its cell; once that succeeds, WooCommerce saves the product completely again. Searching variations by SKU explains which rows such a search shows.
SKUs of duplicated products
The duplicate button on the tools panel uses WooCommerce's own duplication, so a copy of AB-100 gets AB-100-1 (or -2 if that is taken), and the variations of a copied variable product that have their own SKU get the same suffix. Change them afterwards with a bulk edit, or let WooCommerce set your own pattern when the copy is made:
/*
* Duplicated products and their variations get "COPY-AB-100" instead of "AB-100-1".
* Child theme functions.php or a code snippets plugin.
*/
add_action( 'woocommerce_product_duplicate_before_save', function ( $duplicate, $original ) {
$sku = $original->get_sku( 'edit' );
if ( '' !== $sku ) {
// WooCommerce adds -1, -2 ... only if COPY-AB-100 is taken as well
$duplicate->set_sku( wc_product_generate_unique_sku( 0, 'COPY-' . $sku ) );
}
}, 10, 2 );
Check the result before the next run
- Look at the SKU column with variations on. A variation that still shows its parent's SKU has none of its own; filter the SKU with Empty to list such variations.
- Search a few new SKUs with EXACT. A missing one points to a run that stopped on a duplicate: switch on the SKU control and run the rest again.
- The History tab can roll back a SKU run. It cannot restore the empty SKU of a variation that had none: it records the parent's SKU the editor showed, so test variation runs on one product first.
- For thousands of products, keep the batch size suitable for your server, see bulk edit thousands of products.
If a SKU run behaves differently from what is described here, post the behaviour, the value and a sample SKU on the BEAR support forum.