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