Puoi CANCELLARE una categoria solo se non c'è uno scherzo corrispondente:
DELETE c FROM categories AS c
LEFT OUTER JOIN jokes AS j ON c.id=j.category_id
WHERE c.id = $category_id AND j.category_id IS NULL;
Se sono presenti battute per la categoria, il join le troverà e pertanto il join esterno restituirà un risultato non nullo. La condizione nella clausola WHERE elimina i risultati non null, quindi l'eliminazione complessiva corrisponderà a zero righe.
Allo stesso modo, puoi INSERIRE una barzelletta in una categoria solo se la categoria esiste:
INSERT INTO jokes (category_id, joke_text)
SELECT c.id, '$joke_text'
FROM categories AS c WHERE c.id = $category_id;
Se non esiste una tale categoria, SELECT restituisce zero righe e INSERT è un no-op.
Entrambi questi casi creano un blocco condiviso (S-lock) nella tabella delle categorie.
Dimostrazione di un S-lock:
In una sessione eseguo:
mysql> INSERT INTO bar (i) SELECT SLEEP(600) FROM foo;
Nella seconda sessione eseguo:
mysql> SHOW ENGINE INNODB STATUS\G
. . .
---TRANSACTION 3849, ACTIVE 1 sec
mysql tables in use 2, locked 2
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 18, OS thread handle 0x7faefe7d1700, query id 203 192.168.56.1 root User sleep
insert into bar (i) select sleep(600) from foo
TABLE LOCK table `test`.`foo` trx id 3849 lock mode IS
RECORD LOCKS space id 22 page no 3 n bits 72 index `GEN_CLUST_INDEX` of table `test`.`foo` trx id 3849 lock mode S
Puoi vedere che questo crea un blocco IS sul tavolo foo e un blocco S su una riga di foo, il tavolo da cui sto leggendo.
La stessa cosa accade per qualsiasi operazione di lettura/scrittura ibrida come SELECT...FOR UPDATE
, INSERT...SELECT
, CREATE TABLE...SELECT
, per impedire che le righe in lettura vengano modificate mentre sono necessarie come origine per l'operazione di scrittura.
L'IS-lock è un blocco a livello di tabella che impedisce le operazioni DDL sulla tabella, quindi nessuno emette DROP TABLE
o ALTER TABLE
mentre questa transazione dipende da alcuni contenuti nella tabella.