I have a database of products with 8 digit codes starting at number 10000000. I'd like to create a function that copies any given products and gives them new codes: new code = old code + 1. The problem is that i never know if the code already exists in the database, so i'm working on a function that tells me what is the next free code. I'm really stuck on creating any efficient SQL query. There are many questions / answers on SO on this topic, but i couldn't really find any which fits my needs:
- Is fast (within seconds) as there could be millions of product codes in a row.
- May start at arbitrary code N (in queries below i use 10000000, but that's just for testing).
- Returns first free code, not next existing code + 1 (even if neither N and N + 1 exist in the database).
- Returns only 1 free code.
I tried:
SELECT
code + 1
FROM
db_table DBT1
WHERE
code >= 10000000
AND NOT EXISTS
(
SELECT
NULL
FROM
db_table DBT2
WHERE
DBT2.code = DBT1.code + 1
)
ORDER BY
code
LIMIT
1
Explain returns:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY DBT1 index code code 99 NULL 1 Using where; Using index
2 DEPENDENT SUBQUERY DBT2 range code code 99 NULL 19859 Using where; Using index
Analyze returns:
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY DBT1 index code code 99 NULL 39718 35963.00 100.00 0.00 Using where; Using index
2 DEPENDENT SUBQUERY DBT2 range code code 99 NULL 19859 17988.22 100.00 0.01 Using where; Using index
This query is neither fast as it took 227 s to find a free code (20035953) and did not really return 1st free code because codes between 10000000 and 20000000 were free.
I also tried:
SELECT
DBT1.code + 1
FROM
db_table AS DBT1
LEFT JOIN
db_table AS DBT2 ON DBT2.code = DBT1.code + 1
WHERE
DBT1.code >= 10000000
AND DBT2.code IS NULL
LIMIT
1
Explain returns:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE DBT1 index code code 99 NULL 39718 Using where; Using index
1 SIMPLE DBT2 ALL code NULL NULL NULL 39718 Range checked for each record (index map: 0x2)
I guess this query is even less efficient since for the same request it timeouted.
After more testing i tried both queries on production server (server i tested it on should have even more resources) and they both work really fast (< second). I've no idea what's the problem here. Maybe it has something to do with different version on mysql 5.5.5-10.2.16-MariaDB-10.2.16+maria (test) vs 5.5.5-10.4.15-MariaDB-1:10.4.15+maria (production), maybe some weird configuration i don't really understand - as both tables are exactly the same structure and keys. Anyway i decied to go with the first query as it seems to work slightly faster.
After even more testing i “figured” that the problem has something to do with the fact that column code is VARCHAR(32) (because there is sometimes stuff like 80000001-SALE) which i should have had added in the question in the first place. So i guess the database can't work very well like this. In the end i created and used SQL function i added as an answer which i'm not going to accept because it feels kind of hacky to me.