Categorieën
Model
!Structurele problemen!
- De tabel section_category bevat entries met category_id = 0. Deze category bestaat niet in de tabel categories waardoor de integriteit van deze tabel verloren gaat. Meer uitleg over referential integrity: http://en.wikipedia.org/wiki/Referential_integrity
- De huidige key van de tabel is (section_id,category_id,uid), wat betekent dat volgende entries mogelijk zijn:
+------------+-------------+-----+----------+ | section_id | category_id | uid | position | +------------+-------------+-----+----------+ | 1 | 1 | 1 | 0 | | 1 | 2 | 1 | 0 | | 1 | 3 | 1 | 0 | +------------+-------------+-----+----------+
De gebruiker 1 heeft sectie 1 in zowel category 1,2 en 3. Terwijl de relatie van deze entiteiten is dat een gebruiker 1 sectie in 1 categorie kan onderbrengen. De primary key op deze tabel zou moeten zijn (section_id,category_id). De kolom uid is overbodig want de rijen kunnen geidentificeerd worden aan de hand van user_id in categories en section_user.
- De query om alle secties van een gebruiker op te halen per categorie is nu:
( SELECT su.`permission` , s.`xml_uri` , s.`title` AS `section_title` , s.`author_id` , s.`id` AS `section_id` , sc.`category_id` , c.`title` , c.`position` AS `position` , c.`collapsed` , sc.`position` AS `section_position` FROM `section_category` sc LEFT JOIN `categories` c ON c.`id` = `sc`.`category_id` LEFT JOIN `section_user` su ON su.`section_id` = sc.`section_id` AND su.`user_id` = 126 JOIN `section` s ON s.`id` = sc.`section_id` WHERE sc.`uid` = 126 ) UNION ( SELECT su.`permission` , NULL AS `xml_uri` , NULL AS `section_title` , NULL AS `author_id` , NULL AS `section_id` , c.`id` AS `category_id` , c.`title` , c.`position` AS `position` , c.`collapsed` , sc.`position` AS `section_position` FROM `categories` c LEFT JOIN `section_category` sc ON c.`id` = `sc`.`category_id` AND sc.`uid` = 126 LEFT JOIN `section_user` su ON su.`section_id` = sc.`section_id` WHERE c.`uid` = 126 AND sc.section_id is NULL ) ORDER BY `position` ASC , `section_position`;
Dit kan ook op een meer eenvoudig en duidelijkere manier opgehaald worden (reeds rekening houdend met de aanpassing van het weglaten van 0 als categorieloos en uid verwijderd):
(SELECT `su`.`permission` , `s`.`xml_uri` , `s`.`title` AS `section_title` , `s`.`author_id` , `s`.`id` AS `section_id` ,
`sc`.`category_id` , `c`.`title` , `c`.`position` AS `position` , `c`.`collapsed` , `sc`.`position` AS `section_position` FROM `section_user` `su`
LEFT JOIN (`section_category` `sc`, `categories` `c`) ON (`su`.`section_id` = `sc`.`section_id` AND `sc`.`category_id` = `c`.`id` AND `c`.`uid` = 126)
LEFT JOIN `section` `s` ON `su`.`section_id` = `s`.`id`
WHERE `su`.`user_id` = 126 AND `s`.`id` IS NOT NULL)
UNION
(SELECT NULL AS `permission` , NULL AS `xml_uri` , NULL AS `section_title` , NULL AS `author_id` , NULL AS `section_id` ,
c.`id` AS `category_id` , c.`title` , c.`position` AS `position` , c.`collapsed`, NULL AS `section_position` FROM `categories` `c`
LEFT JOIN `section_category` `sc` ON `c`.`id` = `sc`.`category_id`
WHERE `c`.`uid` = 126 AND `sc`.`category_id` IS NULL)
ORDER BY `position` ASC, `section_position`;
Note: 's'.'id' IS NOT NULL is te wijten aan invalid data aanwezig in section_user waar section_id entries aanwezig zijn die niet meer bestaan in section.
Query
Haal alle secties op van een user via de tabel section_user:
SELECT `su`.* FROM `section_user` `sc` WHERE `su`.`user_id` = 126 73 rows in set (0.00 sec)
Breidt de resultaten uit met entries uit section_category, d.w.z. zoek de categorie voor de gevonden secties uit section_user :
SELECT `su`.*,`sc`.* FROM `section_user` `su` LEFT JOIN `section_category` `sc` ON `su`.`section_id` = `sc`.`section_id` WHERE `su`.`user_id` = 126; 116 rows in set (0.00 sec)
Omdat de relatie zonder bovenstaande wijziging toelaat dat secties tot categorieen behoren die over meerdere gebruikers worden gedeeld (de 0 categorie) is de resultset uitgebreid naar 116 rijen.
Dit probleem wordt opgelost door de query uit te breiden naar de categories tabel waar de gegevens voor gevonden category_id worden aangevuld:
SELECT `su`.*,`sc`.*,`c`.* FROM `section_user` `su` LEFT JOIN (`section_category` `sc`, `categories` `c`) ON (`su`.`section_id` = `sc`.`section_id` AND `sc`.`category_id` = `c`.`id` AND `c`.`uid` = 126) WHERE `su`.`user_id` = 126; 73 rows in set (0.00 sec)
Om de detailgegevens van de secties op te vragen dient de tabel section toegevoegd te worden:
SELECT `su`.*,`sc`.*,`c`.*,`s`.* FROM `section_user` `su` LEFT JOIN (`section_category` `sc`, `categories` `c`) ON (`su`.`section_id` = `sc`.`section_id` AND `sc`.`category_id` = `c`.`id` AND `c`.`uid` = 126) LEFT JOIN `section` `s` ON `su`.`section_id` = `s`.`id` WHERE `su`.`user_id` = 126; 73 rows in set (0.00 sec)
Uit de resultaten kunnen NULL gegevens gevonden worden bij de details van de sectie, dit komt door inconsistentie van de data in section_user waar verwijzingen staan naar section_ids die niet meer bestaan in de section tabel. Dit wordt opgelost door te controleren op section_id via section tabel:
SELECT `su`.*,`sc`.*,`c`.*,`s`.* FROM `section_user` `su` LEFT JOIN (`section_category` `sc`, `categories` `c`) ON (`su`.`section_id` = `sc`.`section_id` AND `sc`.`category_id` = `c`.`id` AND `c`.`uid` = 126) LEFT JOIN `section` `s` ON `su`.`section_id` = `s`.`id` WHERE `su`.`user_id` = 126 AND `s`.`id` IS NOT NULL; 48 rows in set (0.00 sec)
Om de resultaten uit te breiden met categorieen die geen secties bevatten dient een union uitgevoerd te worden op de categories tabel waar de category_id niet voorkomt in section_category:
SELECT `c`.* FROM `categories` `c` LEFT JOIN `section_category` `sc` ON `c`.`id` = `sc`.`category_id` WHERE `c`.`uid` = 126 AND `sc`.`category_id` IS NULL
Performance query
Oude query:
+----+--------------+------------+--------+--------------------+-----------------+---------+----------------------------+------+-------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+--------+--------------------+-----------------+---------+----------------------------+------+-------------------------+ | 1 | PRIMARY | s | ALL | PRIMARY | NULL | NULL | NULL | 5459 | | | 1 | PRIMARY | sc | ref | PRIMARY | PRIMARY | 4 | curios.s.id | 1 | Using where | | 1 | PRIMARY | c | eq_ref | PRIMARY | PRIMARY | 4 | curios.sc.category_id | 1 | | | 1 | PRIMARY | su | eq_ref | PRIMARY,section_id | PRIMARY | 8 | const,curios.sc.section_id | 1 | | | 2 | UNION | c | ref | uid | uid | 4 | const | 2 | | | 2 | UNION | sc | ref | idx_category_id | idx_category_id | 4 | curios.c.id | 1326 | Using where; Not exists | | 2 | UNION | su | ref | section_id | section_id | 4 | curios.sc.section_id | 1 | | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using filesort | +----+--------------+------------+--------+--------------------+-----------------+---------+----------------------------+------+-------------------------+
Nieuwe query:
+----+--------------+------------+--------+-------------------------+-----------------+---------+----------------------------------+------+--------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+--------+-------------------------+-----------------+---------+----------------------------------+------+--------------------------------------+ | 1 | PRIMARY | su | ref | PRIMARY,section_id | PRIMARY | 4 | const | 73 | | | 1 | PRIMARY | c | ref | PRIMARY,uid | uid | 4 | const | 0 | | | 1 | PRIMARY | sc | ref | PRIMARY,idx_category_id | PRIMARY | 8 | curios.su.section_id,curios.c.id | 1 | | | 1 | PRIMARY | s | eq_ref | PRIMARY | PRIMARY | 8 | curios.su.section_id | 1 | Using where | | 2 | UNION | c | ref | uid | uid | 4 | const | 2 | | | 2 | UNION | sc | ref | idx_category_id | idx_category_id | 4 | curios.c.id | 1326 | Using where; Using index; Not exists | | NULL | UNION RESULT | <union1,2> | ALL | NULL | NULL | NULL | NULL | NULL | Using filesort | +----+--------------+------------+--------+-------------------------+-----------------+---------+----------------------------------+------+--------------------------------------+
Integriteit
Controleer de integriteit van de bovenstaande tabellen m.b.v. de volgende queries:
- uid in de tabel categories moet bestaan in de user tabel:
SELECT COUNT( `c`.`uid` ) FROM `categories` `c` LEFT JOIN `user` `u` ON `c`.`uid` = `u`.`id` WHERE `u`.`id` IS NULL
- category_id in de tabel section_category moet bestaan in de categories tabel, tenzij category_id == 0 (categorieloze sectie):
SELECT COUNT( `sc`.`category_id` ) FROM `section_category` `sc` LEFT JOIN `categories` `c` ON `sc`.`category_id` = `c`.`id` WHERE `sc`.`category_id` != 0 AND `c`.`id` IS NULL
- section_id in de tabel section_category moet bestaan in de section tabel:
SELECT COUNT( `sc`.`section_id` ) FROM `section_category` `sc` LEFT JOIN `section` `s` ON `sc`.`section_id` = `s`.`id` WHERE `s`.`id` IS NULL
- uid in de tabel section_category moet bestaan in de user tabel:
SELECT COUNT( `sc`.`uid` ) FROM `section_category` `sc` LEFT JOIN `user` `u` ON `sc`.`uid` = `u`.`id` WHERE `u`.`id` IS NULL
- author_id in de tabel section moet bestaan in de user tabel:
SELECT COUNT( `s`.`author_id` ) FROM `section` `s` LEFT JOIN `user` `u` ON `s`.`author_id` = `u`.`id` WHERE `u`.`id` IS NULL
- section_id in de tabel section_user moet bestaan in de section tabel:
SELECT COUNT( `su`.`section_id` ) FROM `section_user` `su` LEFT JOIN `section` `s` ON `su`.`section_id` = `s`.`id` WHERE `s`.`id` IS NULL
- user_id in de tabel section_user moet bestaan in de user tabel:
SELECT COUNT( `su`.`user_id` ) FROM `section_user` `su` LEFT JOIN `user` `u` ON `su`.`user_id` = `u`.`id` WHERE `u`.`id` IS NULL

