tags:

views:

59

answers:

2

I have a table that holds nested categories. I want to avoid duplicate names on same-level items (i.e., categories with same parent). I've come with this:

CREATE TABLE `category` (
  `category_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `category_name` varchar(100) NOT NULL,
  `parent_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`category_id`),
  UNIQUE KEY `category_name_UNIQUE` (`category_name`,`parent_id`),
  KEY `fk_category_category1` (`parent_id`,`category_id`),
  CONSTRAINT `fk_category_category1` FOREIGN KEY (`parent_id`) REFERENCES `category` (`category_id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci

Unluckily, category_name_UNIQUE does not enforce my rule for root level categories (those where parent_id is NULL). Is there a reasonable workaround?

+3  A: 

Reasonable workaround might include

  • checking constraints with triggers if the update/insert operations are not critical for speed
  • using some sort of special value to denote null; this could be modelled relatively properly - have a root node with id 0 that will never be deleted, have parent_id DEFAULT 0 and ON DELETE SET DEFAULT
Unreason
No `ON DELETE SET DEFAULT` in MySQL, I'm afraid :(
Álvaro G. Vicario
Ah, indeed... recognized by the parser, but rejected: "SET DEFAULT: This action is recognized by the parser, but InnoDB rejects table definitions containing ON DELETE SET DEFAULT or ON UPDATE SET DEFAULT clauses." Well, you could implement the cascade in the triggers.
Unreason
Triggers in MySQL are not allowed to alter other rows from the affected table.
Álvaro G. Vicario
I see, according to http://dev.mysql.com/doc/refman/5.1/en/faqs-triggers.html#qandaitem-24-5-1-2, however there are two things to try (didn't try any) - 1) using 0 for root: you don't need to update other rows, only the current row, with ON DELETE SET NULL make a trigger that will check for NULL and turn it to 0 2) using NULL for root: make a trigger that will check uniqueness for (NULL, category_id), run it only when parent_id is NULL (to avoid killing performance for all other rows), and you don't need to update anything - simply cause trigger to fail if (NULL, category_id) already exists.
Unreason
+2  A: 

As far as I can see, to enforce is on the database side, possibilities:

  1. Define a 'root' node, only adding to root node is allowed, not a ´new' rootnode, or
  2. Add an before insert of before update trirgger

BTW: on parent delete categories are promoted to root categories, is that what you want?

Wrikken
«on parent delete categories are promoted to root categories, is that what you want?» — Short answer, yes. I'll probably change it as development goes forward but I suppose I need to write a trigger if I want that MySQL handles it for me.
Álvaro G. Vicario
I think I'll just allow dupes at root level. That simplifies deletion code anyway.
Álvaro G. Vicario