Problem Solution Discussion Using AUTO_INCREMENT Values to Relate Tables

11.16 Using AUTO_INCREMENT Values to Relate Tables

11.16.1 Problem

Youre using sequence values from one t able as keys in second t able so t hat you can relat e records in t he t wo t ables properly. But t he associat ions arent being set up properly.

11.16.2 Solution

Youre probably not insert ing records in t he proper order, or youre losing t rack of t he sequence values. Change t he insert ion order, or save t he sequence values so t hat you can refer t o t hem when you need t hem .

11.16.3 Discussion

Be careful wit h AUTO_INCREMENT values t hat are used t o generat e I D values in a m ast er t able if you also st ore t hose values in det ail t able records t o link t he det ail records t o t he proper m ast er t able record. This kind of sit uat ion is quit e com m on. Suppose you have an invoice t able list ing invoice inform at ion for cust om er orders, and an inv_item t able list ing t he individual it em s associat ed wit h each invoice. Here, invoice is t he m ast er t able and inv_item is t he det ail t able. To uniquely ident ify each order, t he invoice t able could cont ain an AUTO_INCREMENT colum n inv_id . Youd also st ore t he appropriat e invoice num ber in each inv_item t able record so you can t ell which invoice it goes wit h. The t ables m ight look som et hing like t his: CREATE TABLE invoice inv_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY inv_id, date DATE NOT NULL ... other columns could go here ... customer ID, shipping address, etc. ; CREATE TABLE inv_item inv_id INT UNSIGNED NOT NULL, invoice ID from invoice table INDEX inv_id, qty INT, quantity description VARCHAR40 description ; For t hese kinds of t able relat ionships, it s t ypical t o insert a record int o t he m ast er t able first t o generat e t he AUTO_INCREMENT value t hat ident ifies t he record , t hen insert t he det ail records and refer t o LAST_INSERT_ID t o obt ain t he m ast er record I D. For exam ple, if a cust om er buys a ham m er, t hree boxes of nails, and in ant icipat ion of finger- bashing wit h t he ham m er a dozen bandages, t he records pert aining t o t he order can be inser t ed int o t he t wo t ables like so: INSERT INTO invoice inv_id,date VALUESNULL,CURDATE ; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,1,hammer; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,3,nails, box; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,12,bandage; The first INSERT adds a record t o t he invoice m ast er t able and generat es a new AUTO_INCREMENT value for it s inv_id colum n. The following INSERT st at em ent s each add a record t o t he inv_item det ail t able, using LAST_INSERT_ID t o get t he invoice num ber. This associat es t he det ail records wit h t he proper m ast er record. What if you need t o process m ult iple invoices? Theres a right way and a wrong way t o ent er t he inform at ion. The right way is t o insert all t he inform at ion for t he first invoice, t hen proceed t o t he next . The wrong way is t o add all t he m ast er records int o t he invoice t able, t hen add all t he det ail records t o t he inv_item t able. I f you do t hat , all t he det ail records in t he inv_item t able w ill cont ain t he AUTO_INCREMENT value from t he m ost recent ly ent ered invoice record. Thus, all will appear t o be part of t he sam e invoice, and records in t he t wo t ables wont have t he proper associat ions. I f t he det ail t able cont ains it s own AUTO_INCREMENT colum n, you m ust be even m ore careful about how you add records t o t he t ables. Suppose you want t o num ber t he rows in t he inv_item t able sequent ially for each order. The way t o do t hat is t o creat e a m ult iple- colum n AUTO_INCREMENT index t hat generat es a separat e sequence for t he it em s in each invoice. Recipe 11.14 discusses t his t ype of index. Creat e t he inv_item t able as follows, using a PRIMARY KEY t hat com bines t he inv_id colum n w it h an AUTO_INCREMENT colum n, seq : CREATE TABLE inv_item inv_id INT UNSIGNED NOT NULL, invoice ID from invoice table seq INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY inv_id, seq, qty INT, quantity description VARCHAR40 description ; The inv_id colum n allows each inv_item row t o be associat ed w it h t he proper invoice t able record, j ust as wit h t he original t able st ruct ure. I n addit ion, t he index causes t he seq values for t he it em s in each invoice t o be num bered sequent ially st art ing at 1. However, now t hat bot h t ables cont ain an AUTO_INCREMENT colum n, you cannot ent er inform at ion for an invoice t he sam e way as before. To see why it doesnt work, t ry it : INSERT INTO invoice inv_id,date VALUESNULL,CURDATE ; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,1,hammer; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,3,nails, box; INSERT INTO inv_item inv_id,qty,description VALUESLAST_INSERT_ID ,12,bandage; These queries are t he sam e as before, but now behave som ewhat different ly due t o t he change in t he inv_item t able st ruct ure. The INSERT int o t he invoice t able w orks properly. So does t he first INSERT int o t he inv_item t able; LAST_INSERT_ID ret urns t he inv_id value from t he m ast er record in t he invoice t able. How ever, t his INSERT also generat es it s ow n AUTO_INCREMENT value for t he seq colum n , which changes t he value of LAST_INSERT_ID and causes t he m ast er record inv_id value t o be lost . The result is t hat subsequent insert s int o t he inv_item st or e t he preceding records seq value int o t he inv_id colum n. This causes t he second and following records t o have incorrect inv_id values. These are several ways t o avoid t his difficult y. One involves using a different INSERT synt ax t o add t he det ail records; ot hers save t he m ast er record AUTO_INCREMENT value in a variable for lat er use: • I n se r t m u lt iple de t a il r e cor ds a t a t im e . One solut ion t o t he problem is t o add det ail records using MySQLs INSERT synt ax t hat allows m ult iple rows t o be insert ed wit h a single st at em ent . That way you can apply t he LAST_INSERT_ID value from t he m ast er record t o all t he det ail records: INSERT INTO invoice inv_id,date VALUESNULL,CURDATE ; INSERT INTO inv_item inv_id,qty,description VALUES LAST_INSERT_ID ,1,hammer, LAST_INSERT_ID ,3,nails, box, LAST_INSERT_ID ,12,bandage; • Use a SQL va r ia ble . Anot her m et hod is t o save t he m ast er record AUTO_INCREMENT value in a SQL variable for use when insert ing t he det ail records: INSERT INTO invoice inv_id,date VALUESNULL,CURDATE ; SET inv_id = LAST_INSERT_ID ; INSERT INTO inv_item inv_id,qty,description VALUESinv_id,1,hammer; INSERT INTO inv_item inv_id,qty,description VALUESinv_id,3,nails, box; INSERT INTO inv_item inv_id,qty,description VALUESinv_id,12,bandage; • Use a n API va r ia ble . A t hird m et hod is sim ilar t o t he second, but applies only from wit hin an API . I nsert t he m ast er record, t hen save t he AUTO_INCREMENT value int o an API variable for use when insert ing det ail records. For exam ple, in Perl, you can access t he AUTO_INCREMENT using t he mysql_insertid at t ribut e, so t he invoice- ent ry procedure looks som et hing like t his: dbh-do INSERT INTO invoice inv_id,date VALUESNULL,CURDATE ; inv_id = dbh-{mysql_insertid}; sth = dbh-prepare INSERT INTO inv_item inv_id,qty,description VALUES?,?,?; sth-execute inv_id, 1, hammer; sth-execute inv_id, 3, nails, box; sth-execute inv_id, 12, bandage;

11.17 Using Single-Row Sequence Generators